2. GTK+ y su funcionamiento
Como vimos en el primer capitulo, GTK+ es un conjunto de widgets o controles, diseñado con la apariencia de Motif, pero que se ve bastante mejor que Motif
Para hacer una analogia del funcionamiento, voy a usar el peor ejemplo de todos : Visual Basic...
Cuando se programa graficamente en VB, se crea un formulario. Este contendra dentro de el objetos. Estos objetos estan ordenados de forma jerarquica. Por ejemplo
- Form [ objeto padre ] - Frame - Label - Button - Combo - etc...
Un formulario contiene al resto de los objetos [ contenedores ] y al mismo tiempo, un control como el boton posee algunas propiedades, como un Label (excepto el control Label)
GTK+ funciona de una forma similar. Es decir, un control puede contener a otro. Una ventana es un widget (GTK_WINDOW), que contiene al resto de los controles, como un boton (GTK_BUTTON). Un boton puede contener otro control, especificamente un label (GTK_LABEL).
Bueno, sacando el ejemplo de VB, manos a la obra
Ahora, volviendo a lo que es GTK+, existe una jerarquia de objetos. El objeto padre de todos los objetos GTK+ se llama GObject.
Uno de los objetos hijos de GObject es GtkWidget. Y uno de los hijos de GtkWidget es GtkLabel. Es decir, es totalmente permitido el siguiente ejemplo:
GtkLabel * mylabel1; GtkObject * mywidget1; ... mywidget1 = GTK_OBJECT(mylabel1);
Y que hace esta funcion GTK_OBJECT()?
GTK_OBJECT es una macro que retorna un objeto GtkObject * de cualquier objeto de la jerarquia de GTK+. Hay muchas mas funciones, como GTK_WIDGET(), que retorna un GtkWidget *.
Para ver las macros, pueden browsear el codigo fuente o los headers de GTK+.
2.1 Haciendo un simple ejemplo
Hagamos el ejemplo mas simple de todos : una ventana sin nada dentro (el “Hola Mundo” clasico ya se viene)
#include <gtk/gtk.h>int main (int argc, char * argv[]) { GtkWidget * ventana; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_show(ventana); gtk_main(); return 0; }
Para compilar este programa
$ gcc `gtk-config --cflags --libs` ejemplo1.c -o ejemplo1
Ejecutar
$ ./ejemplo1
El resultado seria
[[http://www.tux.cl/lib/exe/fetch.php?cache=cache&media=http%3A%2F%2Fwww.tux.cl%2Farticulos%2Fimagenes%2Ffig1.jpg ]]
Para finalizar el programa, se debe eliminar el proceso (no cerrar la ventana). La razon la explico en la siguiente seccion
Revisemos el codigo fuente
[ gracias por la recomendacion que me dieron ]
GtkWidget * ventana;Creamos un objeto GtkWidget *, que es la ventana principal.
gtk_init(&argc, argv);Para iniciar las aplicaciones en GTK+, es necesario procesar las opciones con las que fue llamado desde la linea de comando y que vuelvan necesariamente al programa.
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);La funcion gtk_window_new() retorna un GtkWidget * que es un objeto ventana. De acuerdo al parametro que se pasa a la funcion (que es una constante), se crea una ventana normal (GTK_WINDOW_TOPLEVEL) o una alerta o popup (GTK_WINDOW_POPUP).
gtk_widget_show(ventana);Esta funcion, gtk_widget_show(), permite desplegar el control, en este caso, el control ventana.
gtk_main();Esta funcion inicializa la rutina principal de GTK+, es decir, comienza la interaccion entre la aplicacion y el manejador de ventanas.
2.2 Señales
Para la programacion grafica, es necesario que los controles se comuniquen con la misma capa. Esto es debido a que el programa debe ser capaz de responder ante la interaccion del usuario. Cuando esto ocurre, un control emite una señal, y el programa debe ser capaz de gestionar una respuesta ante la accion del usuario.
Por ejemplo, un control boton, posee el evento “click” (hacer clic en el boton). Al ocurrir esto, se genera una señal interna. Lo que se debe hacer es “enlazar” la señal generada con una funcion que puede estar programada por el usuario o alguna funcion interna de GTK+.
La funcion para realizar esto es gtk_signal_connect(). Por ejemplo
gtk_signal_connect(GTK_OBJECT(ventana), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
Esta funcion posee cuatro parametros:
- el control que va a emitir la señal : ventana
- la señal a emitir : “delete_event” [ cerrar la ventana ]
- la funcion a ejecutar : gtk_main_quit() [ terminar el loop de gtk_main() ]
- los parametros de la funcion : NULL
(recuerden que el tercer parametro es una macro que retorna un objeto GtkSignalFunc, que es un puntero a una funcion).
gtk_main_quit() hace que el loop gtk_main() concluya.
Agreguemos entonces esta linea al programa de arriba. Debemos agregar esta linea antes de mostrar el control.
#include <gtk/gtk.h> int main (int argc, char * argv[]) { GtkWidget * ventana; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(ventana), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_widget_show(ventana); gtk_main(); return 0; }
Lo que hemos hecho es que al cerrar la ventana, ahora efectivamente se cierra la aplicacion.
2.3 Contenedores
Como explique recien, un control es capaz de contener a otro. Por ejemplo, un boton puede contener un label.
La razon de ser de los contenedores es permitirle a los widgets adaptarse a diferentes tamaños de ventana.
Para contener un widget dentro de otro, se debe usar la funcion gtk_container_add(). Esta funcion acepta dos parametros, el widget contenedor y el widget a contener.
Nota: no todos los widgets soportan contener a otros widgets.
Hagamos un ejemplo simple para mostrar como se contiene un objeto dentro de otro.
GtkButton * miboton; GtkLabel * milabel;... /* creamos el label y blabla */ ... /* creamos el boton y blabla */ ... gtk_container_add(GTK_CONTAINER(miboton), milabel);...
Hay hartos pasos saltados, pero se veran en el ejemplo siguiente:
#include <gtk/gtk.h>int main (int argc, char * argv[]) { GtkWidget * ventana, *miboton; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); miboton = gtk_button_new_with_label("Etiqueta_del_boton"); gtk_container_add(GTK_CONTAINER(ventana), miboton); gtk_signal_connect(GTK_OBJECT(ventana),"delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_widget_show(miboton); gtk_widget_show(ventana); gtk_main(); return 0; }
El resultado de este ejemplo seria el siguiente:
[[http://www.tux.cl/lib/exe/fetch.php?cache=cache&media=http%3A%2F%2Fwww.tux.cl%2Farticulos%2Fimagenes%2Ffig2.jpg ]]
El ejemplo es bastante simple.
Ahora, volviendo a las señales, agreguemos una funcioncita que haga algo mientras se haga clic en el boton.
Lo que se necesita para programar una funcion que interactue con GTK+ es agregar los argumentos requeridos por el widget. Para mas referencia, leer los fuentes o la documentacion de GTK+. [ queda a ejercicio del lector ]
void boton_apretado(GtkWidget * widget, gpointer data) { g_print("Se apreto el boton\n"); }
Agreguemos este trozo de codigo en el ejemplo y agreguemos el manejo de la señal “clicked” al boton.
gtk_signal_connect(GTK_OBJECT(boton), "clicked", GTK_SIGNAL_FUNC(boton_apretado), NULL);
Entonces, quedaria como sigue:
#include <gtk/gtk.h> void boton_apretado(GtkWidget * widget, gpointer data) { g_print("Se apreto el boton\n"); } int main (int argc, char * argv[]) { GtkWidget * ventana, * miboton; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); miboton = gtk_button_new_with_label("Etiqueta_del_boton"); gtk_signal_connect(GTK_OBJECT(miboton), "clicked", GTK_SIGNAL_FUNC(boton_apretado), NULL); gtk_container_add(GTK_CONTAINER(ventana), miboton); gtk_signal_connect(GTK_OBJECT(ventana), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_widget_show(miboton); gtk_widget_show(ventana); gtk_main(); return 0; }
En en ejemplo anterior use la funcion gtk_button_new_with_label(), que permite crear un boton automaticamente con un GtkLabel inserto.
Ahora, como ejemplo adicional, contengamos un Label dentro de un boton
#include <gtk/gtk.h>int main (int argc, char * argv[]) { GtkWidget * ventana, * miboton, * etiqueta; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(ventana), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); miboton = gtk_button_new(); etiqueta = gtk_label_new("Mi etiqueta"); gtk_container_add(GTK_CONTAINER(miboton), etiqueta); gtk_widget_show(etiqueta); gtk_container_add(GTK_CONTAINER(ventana), miboton); gtk_widget_show(miboton); gtk_widget_show(ventana); gtk_main(); return 0; }
Ahora, vamos a trabajar con la ventana, agregando algunas propiedades adicionales.
#include <gtk/gtk.h>int main (int argc, char * argv[]) { GtkWidget * ventana, * miboton; gtk_init (&argc, &argv); ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(ventana), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); miboton = gtk_button_new_with_label("Otro boton mas"); gtk_container_add(GTK_CONTAINER(ventana), miboton); gtk_widget_show(miboton); gtk_window_set_title(GTK_WINDOW(ventana), "Titulo de la Ventana"); gtk_window_set_default_size(GTK_WINDOW(ventana), 400,400); gtk_widget_show(ventana); gtk_main(); return 0; }