COMENTARIOS INICIALES
Este tutorial comprende una introducción a la programación de interfaces gráficas con Perl utilizando sus bindings de Gtk2.
Es recomendado que el lector tenga nociones de Perl sobre todo de su enfoque oriendado a objetos,
referencias, subrutinas y manejo de módulos.
El desarrollo del tutorial va de la siguiente manera: La primera parte consta de un paseo introductorio sobre los conceptos básicos de Gtk y sus componentes, una breve descripción del modelo de eventos y mostrando así nuestro primer programa.
La segunda parte comprende el uso de Gtk2 en Perl, una reseña del uso de la API de C sobre Perl, conceptos de widget y contenedor, señales y eventos, y agrupamiento y posicionamiento de widgets, terminando esta parte con un pequeño visualizador de imagenes.
La tercera y última parte constituye la unión de las primeras 2, anexándole el uso de una cantidad considerable de widgets, y reafirmando los conceptos ya vistos, para así elaborar un pequelo cliente de SQL.
1. INTRODUCCIÓN
1.1 ¿Qué es Gtk?
El Gimp Toolkit es un conjunto de bibliotecas multiplataforma, que crea una poderosa herramienta útil para modelar y crear interfaces gráficas con grandes capacidades, principalmente utilizadas por la suite Gnome.
1.2 Sus componentes principales
GDK ( Gimp Drawing Kit )
Todo lo que tenga que ver con dibujar ( polígonos, líneas ), carga y manipulación de imágenes, y en general cualquier tipo de elemento gráfico, corre a cargo de Gdk.
Glib
Conjunto de bibliotecas que conforman todas el corazón de Gtk y Gnome. Se encarga principalmente de todo el manejo de estructuras de datos, portabilidad, el manejo del sistema de objetos, hilos( threads ) y en general todo lo que corresponde al sistema base de Gtk.
ATK ( Accesibility Toolkit )
Este conjunto de bibliotecas permiten que Gtk mantenga todas esas opciones de accesibilidad desde teclas especiales y dispositivos especiales.
Pango
Se encarga de todo lo relacionado al manejo de fuentes, internacionalización y afines.
1.3 El modelo de eventos ( Event Driven Model )
El funcionamiento de Gtk probablemente (y muy posiblemente) no fuera satisfactorio sin un modelo gobernado por eventos.
Es decir, Gtk no funciona de manera lineal, no se puede anticipar cual será la acción que el usuario elegirá para interactuar con la aplicación y es por tanto que el modelo de eventos-señales está allí para dar control a todos los sucesos ocurridos con dichas interacciones.
En pocas palabras lo que constituye a éste modelo es: un despachador principal, el cual casi todo el tiempo está en espera de eventos( <Gtk2-main> ).
Cuando ocurre un evento es insertado en una cola en la cual cada uno de acuerdo a su turno emite una señal de suceso y el usuario pueda cacharlo y realizar alguna fución específica.
Mas delante en el tutorial veremos un capítulo dedicado a eventos y señales.
1.4 Hola Mundo
No podía hacernos falta como ejemplo introductorio el ya muy conocido y poco temido "Hola Mundo", el cual nos mostrará una breve sinópsis de la sintaxis básica de Gtk2 en Perl:
Código fuente
2. DE C A PERL: UN PASEO POR LA API
Algo peculiar y muy importante que comparten ambos proyectos es su documentación. Y, puesto que la API (Application Programming Interface) de Gtk en C es una referencia completa, el proyecto de documentación de Gtk2-Perl no pretende hacer una copia textual de la de C, por lo tanto se limita a ofrecernos una referencia sobre los métodos de cada clase y algunos cambios que sean de importante mención.
En esta sección se abordarán los puntos básicos e importantes para hacer uso de la API de C en nuestras aplicaciones en Perl y Gtk2. Dichos puntos están completamente documentados en Gtk2::api el cual se encuentra automáticamente en la instalación de Gtk2 y claro, incluyendo la página principal del proyecto y directamente en CPAN.
Espacios de nombre y objetos
Los espacios de nombre en C ( g_,gtk_,etc ) están asociados directamente con sus paquetes correspondientes en Perl. Veamos una tabla con los correspondientes espacios de nombre de C en Perl, los cuales nos ayudarán de mucho al momento de hacer referencia a cualquiera de las clases que vayamos a utilizar en nuestras aplicaciones:
(Glib) g_ => Glib
(Gtk) gtk_ => Gtk2
(Gdk) gdk_ => Gtk2::Gdk
(Pango) pango_ => Gtk2::Pango
Ahora bien, los objetos en Perl toman su espacio de nombre automáticamente de acuerdo al paquete que corresponde siendo propiamente instanciados, y es gracias a esto, que en Perl no se necesitan hacer conversiones de tipo, puesto que los bindings que nos ofrece lo realizan automáticamente en su capa de más bajo nivel (XS). Por ejemplo:
GtkWindow => Gtk2::Window
GdkPixbuf => Gtk2::Gdk::Pixbuf
---------------------------------------------
en C:
GtkWidget *boton;
boton = gtk_check_button_new_with_mnemonic( 'Ejecutar' );
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( boton ), TRUE );
gtk_widget_show( boton );
en Perl:
my $boton = Gtk2::CheckButton->new( 'Cambiar' );
$boton->set_active( TRUE );
$boton->show;
Es sencillo apreciar la facilidad con la que Perl nos permite usar Gtk2 de una manera Orientada a Objetos (a la manera de verlos en Perl) y hacer uso de una manera agradable el llamado de los métodos correspondientes a la clase. Este tipo de libertades son muy propias de Perl.
Constantes ( Flags/Enums )
Las constantes utilizadas como banderas(flags) o valores enumerados son manejadas en Gtk2-Perl como simples cadenas. Mostremos algunas equivalencias:
GTK_WINDOW_TOP_LEVEL => 'toplevel'
GTK_STOCK_OK => 'gtk-ok' # *
GTK_BUTTONS_OK_CANCEL => 'gtk-cancel'
*Cabe señalar que los objetos de "stock" no todos persiguen la misma nomenclatura, la lista completa de los elementos de "stock" puede ser obtenida utilizando la clase Gtk2::Stock y mas específicamente el método list_ids.
Otros puntos importantes
Funciones que retornan 2 o mas valores
Puesto que en C es "imposible" retornar múltiples valores, la manera con la que generalmente se realiza dicha actividad es utilizando el paso de apuntadores a la función que requiera de modificarlas. En Perl, es tan natural que simplemente se regresa una lista de elementos como se muestra a continuación:
en Perl:
( $ancho, $alto ) = $ventana->get_size;
@atributos = $ventana->get_size;
mientras que en C:
gtk_window_get_size( GTK_CONTAINER( ventana ), *ancho, *alto );
Sencillo eh? En realiad no es una comparación sobre funcionalidad, sino sobre la manera en la que sencillamente podemos manipular los datos que deseemos.
Manejadores de señales ( callbacks )
De la misma manera que el ejemplo anterior, Perl permite utilizar de manera nata los manejadores de señales, generando los callbacks ( funciones que permiten manejar específicamente un suceso ) como referencias a código (coderefs) o por medio de subrutinas anónimas como lo mostramos a continuación:
en C:
gtk_signal_connect( GTK_OBJECT( boton ), "clicked" ,
G_CALLBACK( imprime ),
(gpointer) "hola mundo" );
en Perl:
$boton->signal_connect( clicked => \&imprime, "hola mundo" );
o mejor aun:
$boton->signal_connect( clicked => sub { print "hola mundo" } );
Con esto podemos observar la naturaleza funcional (del paradigma funcional) que también nos ofrece Perl.
Otras consideraciones
Lo anterior fué solo un resumen del contenido que podremos consultar de Gtk2::api, aun falta por tocar el tema de cambios en ciertos nombres de funciones, algunas modalidades de métodos, manejo de memoria, entre otros. Aspectos los cuales quedan fuera del alcance de este tutorial pero que se requiere y se recomienda consultarlos.
Si bien la documentación original de la API de Gtk es la principal ayuda, hay aspectos que pueden ser complementados con los manuales y ejemplos del proyecto Gtk2-Perl. Al final del documento se citan algunas URL's sobre la documentación pertinente.
3. WIDGETS Y CONTENEDORES
Sabemos que en la computadora podemos representar información utilizando algunas formas reales y algunas muy abstractas. En el lingo de los GUIs es importante mencionar que, aunque la mayoría son abstracciones de algún objeto real, es necesario dividir dichas abstracciones en 2 bandos, una para almacenar objetos y otra los objetos como tal.
3.1 ¿Qué es un contenedor?
Un contenedor es un "recipiente" de widgets, un almacén donde podemos ponerlos, acomodarlos y ordenarlos, y que como grupo, podemos establecerles algunos atributos "globales" a todos ellos dentro de este contenedor. Así mismo, un contenedor es un también un widget.
Existen varios tipos de contenedores, entre ellos podemos mencionar las ventanas(Gtk2::Window), los frames(Gtk2::Frame), las cajas(Gtk2::Box), menús(Gtk2::Menubar), entre otros, y que todos estos mencionados heredan la mayoría de sus atributos y comportamientos como contenedores de la clase Gtk2::Container.
3.2 ¿Qué es un widget?
Entonces... qué es un widget? Bueno su significado literal en español realmente es "cosa". Así es, widget es una palabra de juego del Inglés que sirve para describir una "cosa" normalmente manufacturada en pequeño. En el mundo de los GUI los widgets son "componentes" que nos ayudan a crear otros componentes, ensamblarse con otros componentes o simplemente hacerlos funcionar.
Botones, ventanas, tablas, listas, menús, selectores, diálogos, espacios de texto, todos ellos son widgets, y algunos necesitan ensamblarse en otros para visualizarse correctamente como lo veremos en ejemplos posteriores.
3.3 ¿Cómo uso un widget?
En general cada widget tiene su comportamiento, sus métodos y atributos. Sin embargo, podemos describir de manera general los pasos a seguir para utilizar cualquier widget:
- 1) Instanciar el widget por medio de su constructor => Gtk2::*->new( ... );
- 2) Establecerle atributos propios o heredados.
- 3) Conectar los manejadores hacia las señales emitidas por los eventos que
nos interesen de dicho widget.
- 4) Agrupar o empacar dicho widget en algun contenedor y darle algún atributo
especial de posicionamiento.
- 5) Mostrar el widget => Gtk2::Widget->show( ... ).
Analicemos cada uno de los pasos creando una ventana:
my $ventana = Gtk2::Window->new( 'toplevel' );
$ventana->set_border_width( 4 );
$ventana->set_default_size( 300, 400 );
$ventana->signal_connect( destroy => sub{ Gtk2->main_quit } );
$ventana->show;
En la primera línea creamos un nuevo objeto de tipo Gtk2::Window permitiéndole estar al frente de todas las ventanas(toplevel). Una vez teniendo nuestro objeto, en las líneas 2 y 3 establecemos algunos atributos, en este caso el ancho del borde de nuestra ventana hacia sus descencientes, y el tamaño inicial que ocupará. En la línea 4 conectamos la señal emitida por el evento "cerrar ventana" a una función que termine nuestra aplicación. Y finalmente mostramos nuestra ventana.
4. EVENTOS Y SEÑALES
4.1 ¿Qué es un evento?
Como ya vimos al inicio del tutorial, Gtk es un conjunto de herramientas las cuales están gobernadas por un modelo de señales y eventos. Cuando damos un click con el mouse sobre un botón, cuando arrastramos un elemento de una lista, cuando seleccionamos algún elemento de un menú, cada una de estas acciones constituyen un evento.
Por lo tanto podemos definir un evento como el resultado de la interacción entre el usuario y la aplicación.
4.2 ¿Qué es una señal?
Una vez ejecutado y conocido el evento, la manera en la se nos permite cacharlo o mas propiamente, la manera en la que nos permite manipularlo es emitiendo un "mensaje" de regreso al programa diciendo que dicho evento sucedió, y así el usuario pueda, mediante un callback, manipularlo. A ese mensaje, se le conoce como señal.
Es importante mencionar que cuando aquí nos referimos a señal, no hablamos propiamente de señales como lo realiza el sistema operativo( llamense POSIX ), si bien puede ser una buena analogía, el manejo y manipulación de ellas se realiza de una manera muy diferente, por lo tanto queda dicho que cuando hablemos de señales en este tutorial nos referimos implicitamente a las de Gtk.
4.3 ¿Cómo conecto una señal?
Bien, ya establecimos que la señal es un mensaje el cual nos indica el evento que sucedió. Para que nuestro programa realice una acción determinada tras la ejecución de dicho evento, es necesario "conectar" esa señal emitida con nuestra función( callback ) de la siguiente manera:
Forma canónica:
Glib::Object->signal_connect( objeto, señal, función, [ parámetros ] )
Aplicado:
$widget->signal_connect( evento => \&funcion );
Recordando que todos los widgets(y contenedores, por ser widgets) heredan de GObject, la forma canónica y la jerarquía de clases nos indican que CUALQUIER widget puede emitir señales si así está indicado en su naturaleza. Veamos un ejemplo utilizando un botón:
...
my $boton = Gtk2::Button->new( '_Salir' );
$boton->signal_connect( clicked => sub { Gtk2::main_quit } );
...
La primera línea nos permite instanciar la clase Gtk2::Button añadiéndole una etiqueta, que sería similar a llamarlo con el constructor new_with_mnemonic, sin embargo estamos en Perl, y podemos hacerlo de esa manera. La siguiente linea nos indica que al recibir una señal de que el evento "clicked" fué realizado, se ejecute el callback alli mencionado ( en este caso una subrutina anónima ) el cual llama a <Gtk2-main_quit> para finalizar la aplicación.
También cabe señalar que no son las únicas funciones para manipular señales, es necesario asomarse a la referencia completa sobre señales en la API.
5. Agrupación y posicionamiento de widgets
Normalmente cuando elaboramos una aplicación GUI tenemos la necesidad de colocar nuestros widgets en lugares clave para su utilización, o simplemente para darle ese look'n feel a nuestro programa. Otro caso podría ser que queremos que al cambiar el tamaño en pantalla de nuestra aplicación, los widgets mantengan su estado original o crezcan proporcionalmente a este.
O bien, queremos agrupar un conjunto de widgets (que pueden o no tener algo en común), y que al modificar algún atributo de posicionamiento a este paquete, todos los widgets respeten dichas reglas.
En Gtk el agrupamiento y posicionamiento relativo de widgets tiene que ver con contenedores tales como Cajas y Tablas. Estos contenedores tienen la habilidad de agrupar widgets y establecerles atributos de posicionamiento. Así pues, dichos contenedores nos dan la oportunidad de ofrecer también un posicionamiento automático si así es requerido. En este tutorial nos centraremos principalmente a los widgets de tipo Box(caja) las cuales son mayormente utilizados.
Para la utilización de tablas se recomienda la documentación contenida en Gtk2::Table
Las cajas nos permitirán un agrupamiento de widgets de manera Horizonal(HBox) o Vertical(VBox) segun sea necesario. Dichos contenedores nos permitirán establecer atributos a nuestro grupo de widgets tales como el acolchonado, expansión y llenado de espacio.
Veamos cada uno de los atributos con el siguiente ejemplo:
$vbox = Gtk2::VBox->new( FALSE, 3 );
$vbox->pack_start( $menu, TRUE, FALSE, 3 );
$vbox->pack_start( Gtk2::HSeparator->new, FALSE, FALSE, 3 );
$vbox->pack_start( $img, TRUE, TRUE, 0 );
Primeramente se creó una caja vertical, la cual contiene los siguientes atributos:
# $widget = Gtk2::VBox->( homogeneous, spacing );
$widget = Gtk2::VBox( tamaño_homogeneo, espaciado ) # Gtk2::HBox aplica igual
El primer parámetro se refiere a un valor verdadero/falso que indique si queremos que los widgets tengan todos el mismo tamaño o no. El siguiente parámetro se refiere a la cantidad de espacio( en pixeles ) que queremos entre cada elemento de la caja, es decir entre cada widget empacado.
Una vez creado nuestra caja, y teniendo listos los widgets a utilizar, llamamos pack_start()/pack_end que son métodos contenidos en la clase Gtk2::Box, según el posicionamiento interno que queramos darle, es decir si quieremos que se posicionen al inicio o al final de la caja respetando su posición con los otros widgets, y sus respectivos parámetros:
# Gtk2::Box->pack_start( widget, expand, fill, padding );
Gtk2::Box->pack_start( widget, expandir, llenar, acolchonado ); #pack_end(...)
El atributo expandir nos permite decirle a nuestra caja que todos los widgets contenidos tendrán el mismo tamaño. llenar únicamente será valido si expandir está encendido, de esta manera le indicamos a nuestra caja que los widgets tomarán espacio extra para quedar del mismo tamaño y ocupar todo el espacio libre. El último atributo se refiere a la cantidad de pixeles que internamente llenará nuestro widget.
Antes de dejar el tema del empacado y posicionamiento de widgets, cabe mencionar que existen cajas específicas para widgets en común, por ejemplo las cajas para botones(Gtk2::HButtonBox) que utilizaremos mas delante en nuestros ejemplos.
El visor de imágenes
Nuestra siguiente aplicación será un sencillo visor de imagenes donde el usuario por medio de un selector de archivos(Gtk2::FileChooser), elegirá un archivo de imagen(incluyendo íconos), y el programa desplegará la imagen a su tamaño real, incluyendo el nombre y el tamaño en KB en una etiqueta.
Código fuente
6. UNIENDO TODO
Bien, una vez dada una amplia reseña introductoria a lo que Gtk2 y Perl nos ofrecen para la creación de nuestras aplicaciónes gráficas, en este parte del tutorial haremos uso de varios( muchos ) widgets creando una pequeña aplicación, la cual consiste en un pequeño pero útil cliente de SQL. El cliente será capaz de leer un archivo de configuración escrito en YAML con la información necesaria sobre la BD a la que nos queremos conectar. Luego la ventana principal contendrá 2 frames, en uno de ellos estará en el area de texto donde colocaremos la sentencia de SQL que deseemos, y en la otra los resultados, los cuales serán visualizados como una lista.
Podemos apreciar la aplicación final a continuación:
Código fuente
7. AHORA.... ¿QUÉ? DOCUMENTACIÓN
Este apartado lo dedicaremos a mostrar algunas URL's sobre información clave para el aprendizaje de Gtk2 en Perl.
Página principal de Gtk
Aquí encontrarás toda la información sobre dicha herramienta, desde tutoriales introductorios, la referencia completa de la API, código fuente, noticias, parches, etc.
http://www.gtk.org
Proyecto Gtk2-Perl
Esta es el sitio principal del proyecto de los bindings de Gtk2 en Perl, al igual que la anterior encontrarás tutoriales, ejemplos, listas de correo, código fuente, etc.
http://gtk2-perl.sourceforge.net
Gtk2-Perl CVS
La parte interesante de este sitio es la cantidad de ejemplos que puedes consultar sobre Gtk2-Perl y Gnome2-Perl inclusive.
http://cvs.sourceforge.net/viewcvs.py/gtk2-perl/
Gtk-Perl Tutorial
La versión inicial del tutorial de Perl, puesto que en Gtk2 varias cosas han cambiado, este tutorial puede ser de una gran ayuda para los widgets que funcionan igual aún en Gtk2, incluyendo ejemplos.
http://personal.riverusers.com/~swilhelm/gtkperl-tutorial/
perldoc Gtk2
Y claro... no podía faltar nuestra página de manual de Gtk2 desde perldoc.
REFERENCIAS
Gtk2, Gtk2::api, Glib, Gtk2::SimpleMenu, Gtk2::SimpleList. Data::Dumper, UNIVERSAL, YAML
CRÉDITOS
Agradecimientos especiales (sin orden alguno) a Sean M. Burke, Simon Cozens, Rocco Caputo, Federico Mena y a la gente del proyecto Gtk2-Perl en el canal de irc #gtk-perl en irc.gnome.org (principalmente a muppet y borup), por su invaluable ayuda y comentarios en la elaboración de este tutorial y los ejemplos.
Cortesía de Marco Antonio Manzo Bañuelos |
Marco A. Manzo es un "Maestro Honorario" de Perl en Español, y gentilmente ha ofrecido algunos de sus tutoriales para su publicación en este sitio.
Puedes visitar la página personal de Marco A. Manzo con el siguiente link: http://www.unixmonkeys.com/amnesiac/blog/
O puedes ver su perfil de "Perl en Español".
|