Buenas noches a todos. Hoy, tal y como dice el topic de este canal, gpoo nos dará una charla de GTK+ a nivel básico El log de esta charla será colgado en la sección de eventos en la web de gnome-hispano, en http://es.gnome.org/eventos/index.php las preguntas hacedmela a mí y yo os daré voz para que se las paséis a gpoo gpoo, cuando quieras :-) ok gracias setepo la charla de hoy: GTK+ basico donde veremos el concepto de widget, el modo de ejecución, las señales los contenedores y un ejemplo "hola mundo" todo para no asustarlos y puedan madurar de a poco la programación gráfica con GTK+ como siempre, nuestra referencia será el Libro GNOME http://libros.es.gnome.org/librognome/librognome/librognome/book1.html Introducción: un poco de historia Cuando se desea programar en un ambiente gráfico, como es X se debe dibujar absolutamente todo, es decir, si queremos un botón debemos dibujar varios rectángulos, cada uno con distintos colores lo que finalmente se traducirá en un botón tal cual lo vemos. Sería el equivalente de programar aplicaciones de consola usando Assembler. Esa biblioteca es XLib. Sin embargo, es engorroso para construir aplicaciones que tengan una apareciencia coherente y atenta contra la productividad del programador. Es así como nacen los Toolkits, que permiten tomar bloques de pantalla estándar llamados widgets. Existen varios Toolkits, como Xt Intrinsics (el más básico), Motif, Open Look, QT, GTK+. De está forma, cuando se programa con un Toolkit, simplemente se indica: añada un botón, una ventana, etc. y nos olvidamos del resto. Es decir, nos ofrece una capa de abstracción respecto al sistema gráfico. GTK+ significa GIMP Tool Kit, y es una biblioteca que agrupa un conjunto de widgets (botones, barra de herramientas, menú, etc.) y que permite crear nuevos widgets. En su inicio, fue creado para desarrollar GIMP. Es el toolkit que se dicidió adoptar para el desarrollo de GNOME porque es un ToolKit libre. Y eso también explica la relación tan estrecha entre GIMP y GNOME. Los widgets Anteriormente indiqué a un widget como un bloque de pantalla estándar. En realidad es más que eso. Porque un widget, además, posee propiedades y podemos programarlo para que reaccione frente a cierto tipo de señales que ocurran durante la ejecución de nuestro programa. A través de la combinación de widgets, podemos construir las interfaces gráficas de nuestras aplicaciones. En GTK+ es posible derivar de cada widget para crear uno nuevo. La clase básica es GtkObject, la cual deriva de GObject, como lo habrán visto con rodrigo la semana pasada. Sin embargo, en el día a día, veremos a GtkWidget como la clase básica, que es derivada de GtkObject y de la cual derivan todos los widgets de GTK+. Debemos tener muy claro que: La creación de cualquier widget, siempre retornará un widget. Así, si creamos un botón, la función de creación del botón retornará un widget. Control de la ejecución de un programa Nuestros programas son escritos en forma secuencial y ordenada. Sin embargo, en un ambiente gráfico, la interacción no es secuencial. Es así que, podemos tener una ventana abierta, y cuando seleccionemos opciones distinas de un menú queremos que se lleven a cabo distinto tipo de acciones. Para eso, GTK+ nos provee de dos funciones para controlar nuestro programa. gtk_init () la cual debe ser llamada *antes* que cualquier otra función de GTK+, ya que es la encargada de inicializar todo lo necesario para trabajar con el ToolKit. El prototipo de esta función es: void gtk_init (int *argc, char ***argv); Donde espera que le pasemos por referencia los valores que recibimos al momento que se ejecute nuestro programa. Más adelante lo veremos con un ejemplo. La otra función es gtk_main () y es una de la últimas funciones de GTK+ que usaremos en nuestro programa principal. Está función ejecuta un ciclo infinito y es la que se encarga de atender todo lo que ocurra en nuestra aplicación es decir, es la que encargará de de esperar la ocurrencia de eventos para posteriormente procesarlos, para lo cual emitirá una señal. Por ejemplo, cuando se presione un botón. Señales Una señal es una forma de mantener una lista de funciones que serán llamadas cuando se "emita" una señal. Esas funciones son llamadas callbacks. De esta forma, cuando ocurre un evento, gtk_main emite una señal y es señal lo que nos interesa programar para que nuestro programa tome vida. Alguna duda (antes de ver el primer ejemplo)? gpoo, parece que no seguimos entonces Un pequeño ejemplo El siguiente ejemplo lo pueden escribir en cualquier editor aunque les recomiendo usar Anjuta. #include int main (int argc, char **argv) { GtkWidget *window; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_show (window); gtk_main (); return 0; } Este ejemplo, lo único que hace es abrir una ventana. Nuestra primera ventana :-) Guardamos nuestro ejemplo, como ejemplo1.c y lo compilamos en un terminal de la siguiente forma: $ gcc -Wall `pkg-config --cflags --libs gtk+-2.0` -o ejemplo1 ejemplo1.c Es necesario que tengan instalados los paquetes de desarrollo de GTK+-2.0, en debian el paquete es libgtk2.0-dev gpoo, tenemos una pregunta adelante --- setepo fija modos [#gnome-hispano +v pollo] pollo, adelante ok hola gpoo hola pollo, cual es la pregunta? se compila igual en GTK 1 que en GTK2 ? No la compilación es distinta en GTK2 se utiliza pkg-config, mientras que en GTK1 se usa gtk-config ok y supongo que el tema de gtk+-2.0 lo cambiaremos por gtk+-1.4 o la que sea no ? no entendi la pregunta si tienes pkg-config y las definiciones apropiadas, tambien te sirve para compilar GTK1 pollo, para gtk 1.x puedes usar gcc -Wall `gtk-config --cflags --libs` -o ejemplo en vez de indicar gtk+-2.0 se coloca gtk+ ok esta claro solo eso gracias :D ahora entiendo, si, asi es. --- setepo fija modos [#gnome-hispano -v pollo] El primer ejemplo funcionará tanto en GTK 1.4 como en GTK-2.x seguimos :-) Pero cuando comencemos a añadir señales, tendremos las primeras incompatibilidades haré la mención para los interesados. Como decía, el ejemplo lo único que hace es mostrar una ventana. Sobre la línea de compilación: Nos valemos de pkg-config para añada toda la información que el compilador necesita para usar la bilioteca GTK+ 2.x. La opción -Wall no es obligatoria, pero *siempre* es recomendable, ya que le indica al compilador que nos muestre cualquier posible equivocación por más mínimo que sea (variables no inicializadas, mala asignación de punteros, variables sin uso). Analicemos el programa línea a línea. Lo primero que hemos indicado es donde se encuentran las definiciones de las funciones que emplearemos (GTK+). Posteriormente definimos la variable window, que es un puntero a un GtkWidget y que corresponderá a la ventana de nuestra aplicación. Como ya lo había indicado antes, la primera función a llamar será gtk_init () para que inicialice todo lo necesario para ejecutar nuestra aplicación con el Toolkit. Vemos que le pasamos por referencia los argumentos del programa principal: argc y argv. Creamos una ventana, con gtk_window_new, indicándole como parámetro que es la ventana principal. Todas las funciones gtk_*_new retornan un puntero de GtkWidget, que en este caso lo hemos dejado en window. Sin embargo, crear un widget no significa que se mostrará en pantalla simplemente se crea el objeto. Para mostrarlo en pantalla usamos la función gtk_widet_show (window). Finalmente, llamamos gtk_main (). Debemos notar algunos detalles para quien lo haya compilado y ejecutado se dará cuenta de lo siguiente: Al presionar el botón de cerrado de la ventana, ésta desaparecerá pero el programa continúa en ejecución esto ocurre porque el programa aún se encuentra en el ciclo infinito gtk_main () si bien, el ciclo nos sirve de ayuda para que nuestra aplicación siga en ejecución queremos que termine cuando lo deseamos pues bien, para ello debemos emplear la función gtk_main_quit () que le indica al ciclo infinito que debe terminar entonces lo que debemos hacer es que nuestro programa responda a la señal de cierre de la ventana. luego lo que debemos hacer es juntar lo que hemos aprendido hoy Los widgets con las señales por el momento sólo hemos creado un widget, gtk_main está esperando a que ocurran pero no le hemos indicado acción alguna a ejecutar cuando estos ocurran. Es tiempo de nuestro ejemplo2 el cual es una breve extensión del ejemplo1 veamos el código: #include gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) { return FALSE; } void destroy( GtkWidget *widget, gpointer data ) { gtk_main_quit (); } int main (int argc, char **argv) { GtkWidget *window; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); gtk_widget_show (window); gtk_main (); return 0; } por el pegado no quedo con la mejor indentación, pero se entiende. Revisemos... Hemos añadidos 2 funciones para nuestras señales (callbacks) delete_event y destroy las explicaré después del programa principal he añadido g_signal_connect, que es una función de glib (en GTK 1.4 esa función se llama gtk_signal_connect) Lo que estamos haciendo, es asociaciar al objeto «window» la señal sea «delete_event» anteriormente quise decir GTK 1.2, GTK 1.4 no existe :-) la señal delete_event se emite cuando se presiona el botón "X" en el marco de la ventana si vemos, esta función lo único que hace es retorna FALSE nos sirve para indicar si se debe emitir la señal destroy esta separación es útil si queremos añadir un diálogo en nuestra aplicación que pregunta al usuario si está seguro de salir porsteriormente, indicamos que atienda a la señal destroy que es donde finalmente hemos puesto gtk_main_quit, y que hará que la aplicación termine en la eventualidad que quisieramos pasar algún parámetro a alguna de las señales, podemos hacerlo con el último parámetro de g_signal_connect, que en esta ocasión ha sido NULL. vamos bien hasta aquí? alguna consulta? continuamos nos falta algo de emoción en nuestro programa, entonces añadiremos un botón cuyo texto sea "Hola mundo" y que al momento de presionarlo, nos imprima en terminal el mensaje "Hola mundo". Pero antes de poder hacerlo, es necesario hablar de: Los contenedores Quienes hayan diseñado interfaz gráficas en Windows notarán una diferencia a la manera de hacerlo en GTK+ salvo que hayan diseñado en Java, en donde también se emplean los contenedores en un principio resulta un poco más complejo, pero los beneficios son mayores Los contenedores son widgets especiales, que alojan a otros widgets y la idea es que permitar que nuestras interfaces se ajusten a cualquier tamaño o tipografía es así entonces, que un contenedor ocupará todo el espacio disponible que encuentre y si maximizamo la ventana, el conetenedor con su widget adentro también cambiará de tamaño debemos entender que un contendor aloja a 1 widget, si queremos que aloje más, debemos trabajar con contenedores especiales, que se verá en la próxima charla. Un botón, GtkButton, tambien es un contenedor, el cual aloja una etiqueta GtkLabel es útil que sea un contenedor, porque también podría alojar una imagen o un contenedor especial para alojar adentro una imageny una etiqueta, o lo que queramos vamos al ejemplo: int main( int argc, char *argv[] ) { GtkWidget *window; GtkWidget *button; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); button = gtk_button_new_with_label ("Hola mundo"); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (hello), NULL); gtk_container_add (GTK_CONTAINER (window), button); gtk_widget_show (button); gtk_widget_show (window); gtk_main (); return 0; } He añadi la definición: GtkWidget *button; button = gtk_button_new_with_label ("Hola mundo"); como dije antes, todas funciones gtk_*_new retornan un widget, en este el botón y le hemos indicado: que cuando ocurra la señal "clicked" ejecuta la función "hello" la cual aun no hemos definido pero si analizamos fríamente hasta ese punto hemos creado la ventana, hemos creado el botón, pero como le decimos que coloque el botón dentro de la ventana? Pues bien, la ventana también es un contenedor y como tal usamos: gtk_container_add (GTK_CONTAINER (window), button); es decir, al contenedor ventana le añadimos el widget button. Luego mostramos el botón y finalmente mostramos la ventana: gtk_widget_show (button); gtk_widget_show (window); otra opción pudo ser: gtk_widget_show_all (window); que muestra al widget "window" y todos los widgets dentro de window. luego creamos la función "hello" (la colocamos al inicio) void hello( GtkWidget *widget, gpointer data ) { g_print ("Hola mundo\n"); } De más está decir, que las funciones delete_event y destroy que creamos hace un rato, deben permanecer porque las usaremos, no las volví a escribir porque eran las mismas. En general, la tónica de creación de widgets y asociación de señales es siempre la misma y es muy sencilla el problema que ocurre es: Cómo saber que funciones existen? Oskuro: cómo saber que parámetros recibe un señal? opps, xchat me completo o: por Oskuro :-) para eso debemos tener la documentacion del API de programación de GTK+ la cual iremos descubriendo las próximas semanas gracias a la aplicación «devhelp» podemos navegar fácilmente a través de ellas y deben notar que: si la función dice "gtk_widget_algo" el primer parámetro *siempre* será un Widget. Si la función dice: gtk_button_set_label, entonces el primer parámetro será un GtkButton entonces resulta fácil asociar el nombre de la función (su primeras palabras) con el widget que será el primer parámetro. lo mismo sucedió con gtk_container_add gtk_container_add (GTK_CONTAINER (window), button); el primer parámetro es un CONTAINER. como sabemos que window es una venana (lo creamos con gtk_window) y que una ventana deriva de un contenedor, entonces le decimos al compilador: pase este widget ventana como un contenedor, porque sabemos que tambien lo es. preguntas parece que no hay ninguna ¿alguien quiere formular alguna pregunta? ¿hay alguien despierto? :-) gpoo, hay una yo creo que no queda mas que invitar a los pacientes asistentes a asistir a la próxima charla --- setepo fija modos [#gnome-hispano +v hardy] hardy, haz tu pregunta Gracias. el próximo domingo sobre Gtk Intermedio oh... hola hardy adelante. Germán, si una ventana es un contenedor y podemos crear dentro otros contenedores, entonces es posible tener contendores anidados ??? si existen 3 contenedores especiales Un gtk_widget_show_all (window); "llama" a todos los contenedores anidados ??? en efecto así, si una ventana tiene muchos widgets no tenemos que llamarlos uno por uno quiero decir, no tenemos que mostrarlos uno por uno lo cual es un gran ahorro :-) sobre todo si hacemos modificaciones no obstante, y a pesar que hemos creados las ventanas y widgets de esta forma Si cierto y muy bune caracteística. Y respecto a los "eventos", un contenedor deja pasar los eventos transparentemente a otro contenedor ? por supuesto los eventos lo asociamos a los widgets así, si tenemos un GtkLabel dentro de un contenedor, dentro de otro contenedor dentro de una ventana, y asociamos el evento al GtkLabel, entonces la señal la recibe el GtkLabel con la excepción que escogí el peor widget para el ejemplo :-/ reemplaza GtkLabel por GtkButton :-) ah ! ok :-) ahora bien, después que pasemos las 3 charlas de GTK+ gracias. hardy, ¿alguna pregunta más? veremos una de libglade, que nos permitirá crear y mantener nuestras interfaces de una manera más fácil pero eso no será útil, sino sabemos lo más básico que es esto. si una más, pero no se si deba preguntar, llegué un poco tarde a la charla :-/ --- setepo fija modos [#gnome-hispano -v hardy] ups.. --- setepo fija modos [#gnome-hispano +v hardy] cuál es la línea de comando para compilar en programa en GTK ? hardy, no te preocupes, tú pregunta la que he indicado anteriormente y la vuelvo a indicar: gcc -Wall `pkg-config --cflags --libs gtk+-2.0` -o ejemplo ejemplo.c muchas gracias :-D ! si quieres saber mas opciones de pkg-config, simplemente prueba pkg-config --help y/o pkg-config --list-all No mas preguntas. --- setepo fija modos [#gnome-hispano -v hardy] --- setepo fija modos [#gnome-hispano +v PeSa] PeSa, pregunta una pregunta... es posible que desde otro programa se genere una señal y que pueda interpretar un widget... ejemplo (un reconocedor de voz) o un teclado en pantalla? eso lo desconozco si te refieres a que ejeucto programa1 y programa2 y quieres que programa2 emita una señal que interprete un widget de programa1 eso es lo que quieres? si PeSa, no podrás con las señales de GObject. Tendrás que usar algún mecanismo de IPC, como sockets ok gracias bueno, con g_io_channel se pueden conectar programas PeSa, ¿alguna más? eso lo vimos en una charla de glib que di no gracias veamos, rodrigo tiene una solucion para ello --- gpoo fija modos [#gnome-hispano +o rodrigo] PeSa, ferulo comenta que eso que dices también es posible con bonobo puedes comunicar widgets entre dos procesos con GtkPlug/GtkSocket eso es lo que usa bonobo claro que eso será en otra charla :-) :-) ¿alguna otra pregunta? ¿algún otro comentario sobre esto? bueno, de apoco los vamos amarrando a las siguientes charlas. parece que no gpoo, ¿damos por terminada la charla? setepo: si, e invitarlos para la próxima semana pues ala, fin --- setepo fija modos [#gnome-hispano -m]