28 de Junio 2006

IconView

Nuevo widget portado a T-Gtk

Recientemente estoy realizando portando mas widgets en T-Gtk, y esta vez le
a tocado a los iconview.

Desarrollando la aplicacion que anteriormente habiamos comentado, me encontré
con la necesidad de mostrar una imagen.

Pensé un momento, y me dí cuenta, que lo mejor era mostrar todas las imagenes
que quiesiera.

Para ello, la mejor manera que vi, fue el usar el widget llamado GtkIconView.

Dicho widget hace uso de un modelo de datos, para mostrar imagenes más una etiqueta.

Su funcionamiento es muy simple usando POO, podemos realizar lo que estamos buscando, que era mostrar una lista de imagenes.

El codigo es muy simple:


DEFINE WINDOW oWnd TITLE "GtkIconView. T-Gtk for [x]Harbour (c)03-2006 Rafa Carmona" SIZE 500,500
DEFINE SCROLLEDWINDOW oScroll OF oWnd CONTAINER
oModel := Create_Model()
DEFINE ICONVIEW oIconView MODEL oModel OF oScroll CONTAINER
* Indicamos que la primera columna es el que contiene el texto.
oIconView:SetTexColumn( 1 )
* Indicamos que la segunda columna es el que contiene la imagen.
oIconView:SetPixBufColumn( 2 )
* Indicamos que queremos 5 columnas.
oIconView:SetColumns( 5 )
* Cada vez que activemos la imagen, double-click o return, saltaremos
* a la funcion Comprueba, y veremos como mostrar la imagen real.
oIconView:bItem_Activated := {|oSender,pPath| Comprueba( oSender, pPath ) }
ACTIVATE WINDOW oWnd CENTER

Como podeis apreciar, el codigo inicial, son unas pocas lineas, sin ningun
tipo de misterio ni complicacion.

Ahora nos detendremos en la creacion del modelo de datos, que lo realizamos
desde la funcion Create_Model(), el cual podeis observar a continuacion,
y os sorprendereis lo poquita cosa que es:

 FUNCTION Create_Model()
   local aIter := array( 4 )
   local oLbx, x, oImage,pixbuf
   Local aFiles := {}
   local aPngs := Directory( "../../images/*.png" )
   local aJpgs := Directory( "../../images/*.jpg" )
   
 * Obtenemos TODOS los ficheros graficos
   Aeval( aPngs ,{ | a| AADD( aFiles, a[1] ) } )
   Aeval( aJpgs ,{ | a| AADD( aFiles, a[1] ) } )
 
 

* Definimos el model de datos, ListStore, con los tipos adecuados.
DEFINE LIST_STORE oLbx TYPES G_TYPE_STRING, GDK_TYPE_PIXBUF
For x := 1 To Len( aFiles )
APPEND LIST_STORE oLbx ITER aIter
SET LIST_STORE oLbx ITER aIter POS 1 VALUE aFiles[x]
* Cargamos la imagen
DEFINE IMAGE oImage FILE "../../images/"+aFiles[ x ] LOAD
* Conversion de la imagen a 100x100
pixbuf := gdk_pixbuf_scale_simple( oImage:GetPixBuf(), 100,100 )
SET LIST_STORE oLbx ITER aIter POS 2 VALUE pixbuf
gdk_pixbuf_unref( pixbuf )
Next
RETURN oLbx

La única cosa que nos trae la atencion, es la funcion gdk_pixbuf_scale_simple().
Bueno, he de decir, que si cargamos el modelo de datos con la imagen, este lo
cargará con el tamaño REAL, asi, tendremos en la vista de los iconos, diferentes
tamaños, dando la sensacion un poco extraña.

Para ello lo que vamos a realizar, es transformar dicha imagen a una que nos
interese, en este caso a 100x100.

Asi, cuando cargamos la imagen:
DEFINE IMAGE oImage FILE "../../images/"+aFiles[ x ] LOAD

Podemos facilmente transformala al tamaño que nos interese:
pixbuf := gdk_pixbuf_scale_simple( oImage:GetPixBuf(), 100,100 )

Y podeis observar , como el valor de la variable, pixbuf, es la que vamos
a introducir en el modelo de datos:
SET LIST_STORE oLbx ITER aIter POS 2 VALUE pixbuf

Y lo último que nos queda por hacer, es desreferenciar el pixbuf:
gdk_pixbuf_unref( pixbuf )

Ahora lo úncio que nos queda, es coger el valor de donde estamos
y hacer algo interesante , como mostrar la imagen en su tamaño real,
en una ventana.

 Static Function Comprueba( oIconView, pPath  )
     Local oWnd , oImage, width, height, pImage, cText
 
 

* Obtenemos el nombre del fichero
cText := oIconView:GetValue( 1,, pPath )

DEFINE WINDOW oWnd TITLE cText TYPE_HINT GDK_WINDOW_TYPE_HINT_MENU
DEFINE IMAGE oImage FILE "../../images/"+cText OF oWnd CONTAINER

ACTIVATE WINDOW oWnd MODAL CENTER

Return nil

Bueno, espero que con esta explicación podais entender mejor dicho widget.
Como siempre , una imagen vale más que mil palabras ;-)

tgtk_iconview.JPG

Escrito por Rafa Carmona a las 7:11 PM | Comentarios (0)

8 de Junio 2006

Evolucionando T-Gtk.

Estoy desarrollando un sistema básico para llevar los libros , revistas, pdfs,
documentos, etc..., que tenemos todos nosotros en casa.

La mayoria pensará, y con razón, que hay cientos programas similares, hasta libres, gratuitos, etc.

La razón por la cual desarrollé dicha aplicacion, fue por puros motivos técnicos.
Me explico.

Lo primero que tenia claro es que necesitaba desarrollar 'algo' bajo SQL.
En la red , hallé una muy buena documentación en castellano sobre ello,
http://mysql.conclase.net/

Observé como la forma de trabajar con las bases de datos, era el sistema perfecto
para T-GTK, empleando el MVC( Modelo Vista Controlador ).

Siguiendo con el SQL, y decidir a emplear MySql, tuve que encontrar algun sistema
que me permitiera trabajar con Harbour.

Bajo xHarbour, tenemos en las contrib, las clases necesarias para ello, pero , como
ni tan siguiera sabia como tiraban, mi buen amigo Manu Exposito, me brindó la
oportunidad de usar Eagle-1.

Entones Jose A.Suarez, me dejó una clase muy simple, que para mi, era todo lo que yo
necesitaba, hacer una consulta y obtener unos resultados, y pude ver entonces,
la simplicidad y potencia de los sistemas de motores de bases de datos.

Quizas, el tiempo, me diga que deberia de usar las clases que proporciona Eagle-1,
que estoy seguro que sería más productivo, pero, en estos momentos, no estoy hablando
de productividad, si no , de aprender como hacer las operaciones en lenguaje SQL.

Bien, una vez que hemos decidido usar MySql con Eagle 1, el siguiente paso fue
determinar que aplicacion desarrollar, asi, que ese 'algo' fue el ejemplo sobre libros
que trae como ejercicio la documentación al respecto, quedando pendiente temas como
el de socios, etc., que no creo que tenga intencion de terminarlo.

En cualquier caso, tambien me planteaba un reto, y era usar GLADE, y tenerlo todo
en una simple ventana.

GTK+, no posee 'nativamente' hablando, un sistema de ventanas tipo MDI, que si bien hay
de 3 terceros, no esta en el standard, por lo tanto, decidi no usar ni implementarlo.

He observado y hablado con compañeros de Delphi, y veo , que ellos , tienen una pestaña,
una para el browse, y otra que contiene o contendrá el contenido de la ficha en la cual
estamos posicionados.

La verdad, es que me hubiese costado MUCHISIMO menos, mostrar un dialogo con el contenido
de la ficha tecnica, pero el reto valía la pena ;-)

Asi, por ejemplo, podemos 'cargar' la ficha, cuando cambiamos de pestaña:

 DEFINE NOTEBOOK oBook ID "notebook" RESOURCE cGlade  ;
        ON CHANGE ( oTreeView:SetFocus(),;
                     if( oBook:nPageCurrent != 1,;
                         Ficha( oTreeView, aControl, oTreeView_Autor, oTreeView_Materia ),) )
 

En la funcion Ficha, ahora, solamente tenemos que coger los datos sobre el cual estamos,
y lo hacemos como:

 IF oTreeView:IsGetSelected( aIter ) // Si fue posible seleccionarlo desde la vista
    // Podemos obtener el valor asi:
    pPath   := oTreeView:GetPath( aIter )             // Obtenemos el camino hacia el , por medio de aIter
    nCodigo := oTreeview:GetValue( 1, "Int" , pPath ) // Obtenemos el codigo del libro.
 ENDIF
 

Pero recientemente, desarrollé un method llamado GetAutoValue( nColumn ).
Dicho method, es capaz de determinar que tipo de datos hay metido en el modelo de datos,
y extraerlo, ahorrandonos hacerlo nosotros manualmente, asi que, lo mismo que arriba se queda
como:

nCodigo := oTreeview:GetAutoValue( 1 ) // Obtenemos la clave del libro.

Este codigo NO ELIMINA ni INVALIDA al otro, son complementos, y como ya dije alguna vez,
en tema del MVC, hay diferentes formas y caminos para obtener el mismo resultado.

Despues, es trivial , hacer una select sobre el libro que queramos y que campos necesitamos:

 cQuery := "SELECT clavelibro , titulo, titulo_original, idioma, ...etc..."+;
                 "WHERE clavelibro =" + cValtoChar( nCodigo )
 
 

aValues := aQuery( cQuery ) // Funcion generica que nos devuelve resultados.

aValues va a contener el array con el contenido de la select , asi que tan simple como
alimentar a tantos widgets como queramos o necesitemos :


if aValues != NIL // Si la query fue correcta, actualizamos controles.
aControl[1]:SetValue( aValues[1,1] )
...etc...
endif

El jugar con los TreeView, los Listore, las columnas, el acceso a MySql, a sido
realmente agotador, pues muchas noches me he quedado en vela pensando
como resolver cual problema, o lo que es peor, porque dicho codigo ocasionaba
un problema posterior, pero lo que sin duda más me alegra , es poder disfrutar
de desarrollar mi propia herramienta de trabajo.

Anoche , recién le metí una nueva caracteristica a las vistas, y por eso todo
este post , y le añadí la posibilidad de meter widgets en el Headers de la Vista.

El programa en cuestion, en cuanto lo tenga más pulilo, lo liberaré, y seguramente
tambien el codigo fuente asociado, para mostrar como con T-Gtk, no hace falta ser
un genio de la programacion para hacer cosas realmente interesantes y sobretodo
multiplataformas.

El modelo de datos que he escogido a sido un TREE STORE, en vez de un
LIST STORE.

El motivo principal, es que me apetecia ver las observaciones sin tener que ir a
la ficha tecnica, asi, en el mismo browse, puede consultar las observaciones,
por ello podeis observar como hay algunos que tienen un 'expansor' y otros no.

Los que no tienen , es que no tienen observaciones , y los que si, pueden
expandirse para mostrar los comentarios.

Pero, lo mas gratificante, es lo sencillo que resulta su creación:


// Modelo de Datos.
DEFINE TREE_STORE oLbx ;
TYPES G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,;
G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING

nLen := Len( oSql:aRes )

For x := 1 To nLen
APPEND TREE_STORE oLbx ITER aParent
nLen_Row := Len( oSql:aRes[ x ] )
For n := 1 to nLen_Row
if n != 8 // El BLOB a parte
SET TREE_STORE oLbx ITER aParent POS n VALUE oSql:aRes[x,n]
else
if !Empty( oSql:aRes[x,n] ) // Si el BLOB no esta vacio....
INSERT TREE_STORE oLbx ROW x ;
ITER aChild PARENT aParent ;
VALUES oSql:aRes[x,1] ,oSql:aRes[x,n] // Hijo con valores directamente
endif
endif
next

Next

Ya esta! Con esto SE A RELLENADO el modelo de datos, la vista, ya se encargará
de mostrarlo y como mostrarlo, pero los datos en si, no es necesario nada más.

Espero os guste el resultado.

miscosas3.JPG

Escrito por Rafa Carmona a las 2:54 PM | Comentarios (4)