Hola buenas noches, bienvenidos a #gnome-hispano esta noche tenemos con nosotros a German Poo que nos va a hablar de GTK+ Puedo comenzar? si adelante German :) Bueno, esta charla trata de GTK+ nivel intermedio, aunque se trata de la segunda parte de GTK+ es decir, comenzarmos a avanzar de acuerdo a lo que vimos la semana pasada y como habreis dado cuenta rodrigo y yo, trataremos de introducirlo un poco mas en la programación con GTK+ en base a un ejemplo que involucrará: - El entendimiento de los contenedores - Algunos widgets básicos - Como mexclarlos con señales y nuevos diálogos esto último normalmente es un problema para las personas que comienzan a programar con GTK+ para quienes hayan seguido el ejemplo de la semana pasada (Hola mundo) se habran podido dar cuenta que no es muy dificil esto de añadir widgets y conectar señales con funciones para que realicen determinadas funciones sin embargo, para el novato, los dolores de cabeza comienzan cuando se quiere interactuar con otras ventanas y lograr pasar datos de una a otra para ello usaremos lo aprendido en Glib, algunas semanas atrás. Vamos adelante con un ejemplo. - Los contenedores (primera revisión) Los contenedores, son widgets especiales, que no son visibles (no tienen ninguna función visual) más que alojar dentro de si a otros widgets. Dependiendo del contenedor se pueden alojar de 1 a "n" widgets Así por ejemplo, el contenedor principal es un GtkWindow. --- gnomero fija modos [#gnome-hispano +o setepo] --- gnomero fija modos [#gnome-hispano +o telemaco] --- rpons fija modos [#gnome-hispano -ooo gnomero setepo telemaco] que retomando el ejemplo de la semana pasada, teniamos: int main( int argc, char *argv[] ) { GtkWidget *window; GtkWidget *button; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 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; } donde lo esencial es: gtk_container_add (GTK_CONTAINER (window), button); para nuestros propósitos. Con eso indicamos que se añada el widget button al contenedor ventana. Sin embargo, el boton ocupará todo el espacio de la ventana si ésta se maximiza, el botón tendrá el tamaño de la ventana maximizada. Y una vez que un contenedor está completamente ocupado no podemos colocar más widgets en él. Así que, necesitamos otros contenedores. El ejemplo que veremos, para mezclar contenedores será muy sencillo. Crearemos una ventana en ella tendremos un entrada de texto a la izquierda, a la derecha de la entrada de texto tendremos un boton y más abajo, una etiqueta un diseño en ASCII de la ventana sería el siguiente: +---------------------------+ | entrada_de_texto | boton | +---------------------------+ | etiqueta1 | etiqueta2 | +---------------------------+ Y la aplicación lo que hará será abrir un diálogo de archivo y llenar la entrada de texto con el nombre del archivo lo mismo con el la etiqueta2. Pues bien, si miramos el contorno, eso simboliza nuestra ventana. Vemos que esta dividida en 2 files a si vez, cada fila esta dividida en 2 columnas. Entonces, presentamos a los contenedores que nos ayudarán a diseñar nuestra ventana. GtkVBox y GtkHBox GtkVBox es un contenedor que organiza los widgets hijos en una sola columna. GtkHBox es un contenedor que organiza los widgets hijos en una sola fila. Las funciones de creación de cada uno de ellos es: GtkWidget* gtk_vbox_new (gboolean homogeneous, gint spacing); GtkWidget* gtk_hbox_new (gboolean homogeneous, gint spacing); Donde el primer argumento sera TRUE si queremos que todos la separación entre widgets sea homogenea es decir, que ocupen el mismo tamaño. y el último parámetro corresponde al espacio que debe dejar antes de dibujar los widgets. En GNOME normalmente se emplea espaciado 4. Usar espaciado 0 hace que las ventanas se vean feas, en mi opinion. Nuestro programa quedaría: GtkWidget *window; GtkWidget *vbox_principal; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "window"); vbox_principal = gtk_vbox_new (FALSE, 4); gtk_widget_show (vbox_principal); gtk_container_add (GTK_CONTAINER (window), vbox_principal); gtk_container_set_border_width (GTK_CONTAINER (vbox_principal), 4); bueno, aun no esta completo, pero comenzamos a añadir funciones que las explico a continuación. Hemos declarados 2 widgets: window y vbox_principal. Como toda aplicacion GTK+, la primera función a invocar es gtk_init () que inicializar todo lo necesario para que podamos trabajar en el ambiente procedemos a crear la ventana, tal cual lo vimos la semana pasada con window = gtk_window_new (GTK_WINDOW_TOPLEVEL); y la primera función nueva, pero que se entiende al leer es: gtk_window_set_title (GTK_WINDOW (window), "window"); que nos permite definir el título que tendrá la ventana les recuerdo que en gtk el nombre de la funcióón indica el tipo de dato del primer argumento. así, gtk_window_* espera como primer parametro el objeto GTK_WINDOW para el cual se realizará la operación indicacada en _* donde _* en este caso es _set_title Lo siguiente que queremos es inserar un GtkVBox, porque queremos una columa con 2 filas (ver el dibujo) Entonces, indicamos que crearemos uno nuevo, que no lo distribuya en forma homogénea vbox_principal = gtk_vbox_new (FALSE, 4); Debo hacer una acotación: El valor 4 indica el espacio entre las cada una de las filas, no el borde como lo había dicho anteriormente. Aunque la idea es similar, que los widgets no sean todos apretados. no se vean todos apretados, quise decir. gpoo: un comentario adelante. en libgnomeui, parte de gnome-libs hay un #define: #define GNOME_PAD 8 #define GNOME_PAD_SMALL 4 #define GNOME_PAD_BIG 12 para ese parámetro gpoo: ya, cuando quieras ok gracias por la indicación. Con eso, evitamos colocar números "en duro" en nuestro código y si el estándar llegara a cambiar, nuestra aplicación ya estará actualizada. pues bien, he tomado una captura a la ventana que queremos realizar para puedan darse una idea más clara. la subo de inmediato y les doy la URL http://www.calcifer.org/documentos/irclogs/ejemplo-gtk+/ejemplo1.png Como pueden ver, esa es nuestra ventana objetivo. y de muestra, colocaré otra que no tiene espaciado para que puedan ver la diferencia http://www.calcifer.org/documentos/irclogs/ejemplo-gtk+/ejemplo1-sinespaciado.png como pueden apreciar, la primera ventana luce mucho más clara que la primera. que la segunda, perdon. lo siguiente que tenemos es: gtk_widget_show (vbox_principal); que si recordarán, lo que hace es mostrar el widget recién creado. Recuerden, cada widget siempre que es creado n nunca es mostrado en forma inmediata nosotros debemos especificarlo explícitamente. Luego viene lo que nos importa: gtk_container_add (GTK_CONTAINER (window), vbox_principal); Es decir, hemos añadido un contenedor (vbox_principal) de tamaño variable a un contenedor (window) de tamaño fijo de esta manera ya podemos colocar tantos widgets como queramos, siguiendo la misma filosofía. finalmente: gtk_container_set_border_width (GTK_CONTAINER (vbox_principal), 4); que definir como borde 4 para el contenedor. Es decir, entre el borde de la ventana y el vbox_principal habrá una distancia de 4 unidades. ahora bien, en ningún caso he especificado que serán 2 filas. de hecho, pueden ser 100 si quisiera. Es ampliable en forma dinámica. Entonces seguimos el mismo procedimiento para crear ahora un GtkHBox donde crearemos 2, uno para cada fila. Necesitamos: GtkWidget *hbox_entry; GtkWidget *hbox_label; En el primero ubicaremos la entrada de datos y el boton en el segundo, las 2 etiquetas. Además; GtkWidget *entry_file; GtkWidget *button_file; GtkWidget *label1; GtkWidget *label_archivo; El primero corresponderá al widget GtkEntry (entrada de texto) el segundo a un GtkButton (boton abrir) el tercero a un GtkLabel (el que dirá "Nombre del archivo") y el último a un GtkLabel donde colocaremos el nombre del archivo real (cuando terminemos el ejemplo). Creamos el primer GtkHBox, de una manera similar al GtkVBox: hbox_entry = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox_entry); creo que esto no necesita explicación ahora, necesitamos añadir hbox_entry a vbox_principal: gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_entry, FALSE, TRUE, 0); para ello, *no* le decimos: inserte en la fila enésima sino que le pedimox que añada una Analicemos los parámetros: void gtk_box_pack_start (GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding); El primer argumento es el widget en el cual queremos insertar otros widgets. Esta función, por lo tanto, es válida tanto si queremos insertar widgets a un GtkHBox o a un GtkVBox ambos heredan de GtkBox y comparten esta función. El segundo parámetro, child, es el widget que queremos insertar, en este caso hbox_entry Los 3 últimos corresponden a la forma en que debe comportarse este widget respecto a los otros que puedan existir en el GtkBox expand: si se le debe dar espacio extra al hijo. fill: Si el widget se debe rellenar en altura o en anchura. Sólo es sirve TRUE si expand es TRUE. En nuestro caso, da lo mismo que coloquemos TRUE, porque expand dice FALSE. En qué casos sirve? Supongamos que tenemos un editor de texto (por ejemplo, gedit). Si la ventana se maximiza queremos que el espacio de edición de texto, crezca también así, basta definir en expand y fill como TRUE, para que GTK+ lo haga en forma automática. A diferencia de otros ambientes en donde el programador tendría que programarlo manualmente. padding: son espacios de relleno entre el widget child y los otros widgets dentro del GtkBox es decir, nos sirve para indicar mas espaciado. En este caso, le he puesto 0, porque antes ya le había indicado un espaciado de 4 con una vez es suficiente. Ahora bien, tenemos lo siguiente: 2 widgets que no presentan nada visual entonces, añadimos (por fin!) los widgets que nos interesan Creamos la entrada de texto: entry_file = gtk_entry_new (); gtk_widget_show (entry_file); Creamos el botón: button_file = gtk_button_new_from_stock ("gtk-open"); gtk_widget_show (button_file); Algo nuevo: la semana pasada usamos gtk_button_new_with_label esta vez, usamos gtk_button_new_from_stock, la cual nos añade un botón de los predeterminados que tiene GTK+ esto automáticamente colocará el icono de la carpeta abierta más el texto correspondiente. Hemos creado los widgets, pero ahora tenemos que agregarlos a hbox_entry: gtk_box_pack_start (GTK_BOX (hbox_entry), entry_file, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox_entry), button_file, FALSE, FALSE, 0); Observación: El orden en que es llamada esta función es *importante*. Si lo llamamos al revés, entonces aparecerá el botón a la izquierda y la entrada de texto a la derecha Si son observadores, notarán que el parámetro "expand" de la primera llamada va en TRUE es decir, si maximizamos la ventana, la entrada de texto se extenderá a lo ancho de la ventana es decir, se ajustará automáticamente, mientras el botón conservará su aspecto, porque tiene sus parámetros en FALSE Por último, añadimos las etiquetas lo cual no tiene nada nuevo: hbox_label = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox_label); gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_label, FALSE, TRUE, 0); label1 = gtk_label_new ("Nombre del archivo:"); gtk_widget_show (label1); gtk_box_pack_start (GTK_BOX (hbox_label), label1, FALSE, FALSE, 0); como podemos ver, realizamos el mismo procedimiento: Creamos un nuevo GtkHBox, lo añadimos al GtkVBox creamos una etiqueta y la añadimos al GtkHBox el ejemplo completo quedaría: include int main (int argc, char **argv) { GtkWidget *window; GtkWidget *vbox_principal; GtkWidget *hbox_entry; GtkWidget *entry_file; GtkWidget *button_file; GtkWidget *hbox_label; GtkWidget *label1; GtkWidget *label_archivo; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "window"); vbox_principal = gtk_vbox_new (FALSE, 4); gtk_widget_show (vbox_principal); gtk_container_add (GTK_CONTAINER (window), vbox_principal); gtk_container_set_border_width (GTK_CONTAINER (vbox_principal), 4); hbox_entry = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox_entry); gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_entry, FALSE, TRUE, 0); entry_file = gtk_entry_new (); gtk_widget_show (entry_file); gtk_box_pack_start (GTK_BOX (hbox_entry), entry_file, TRUE, TRUE, 0); button_file = gtk_button_new_from_stock ("gtk-open"); gtk_widget_show (button_file); gtk_box_pack_start (GTK_BOX (hbox_entry), button_file, FALSE, FALSE, 0); hbox_label = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox_label); gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_label, FALSE, TRUE, 0); label1 = gtk_label_new ("Nombre del archivo:"); gtk_widget_show (label1); gtk_box_pack_start (GTK_BOX (hbox_label), label1, FALSE, FALSE, 0); label_archivo = gtk_label_new (""); gtk_widget_show (label_archivo); gtk_box_pack_start (GTK_BOX (hbox_label), label_archivo, FALSE, FALSE, 0); gtk_widget_show (window); gtk_main (); return 0; } para compilarlo: $ gcc -Wall -o ejemplo1 `pkg-config --cflags --libs gtk+-2.0` ejemplo1.c Sin embargo, esto no tiene vida. Queremos que al ejecutarse podamos presionar el botón y llamar un diálogo que nos pregunte por un archivo a abrir Hay preguntas? antes de proseguir algun comentario rodrigo? no parece que haya muchas preguntas --- rpons fija modos [#gnome-hispano +o Drak] se pueden omitir los widget_show y poner un show_all solo al final de todo? Si se puede hacer. gracias de hecho, ahorra líneas de código :-) --- rpons fija modos [#gnome-hispano +o hardy] --- rpons fija modos [#gnome-hispano -o Drak] adelante hardy Hay algún estándar de usar o no show_all ?? hardy: no simplemente, usar gtk_widget_show asegura que solo muestras los widgets que quieres es decir, en estos ejemplos, es mejor quizás usar gtk_widget_show_all (window); ok, etonces es cuestión de estilo y gusto de cada uno. Gracias. pero hay ocasiones en los que tienes widgets ocultos que muestras/ocultas segun la necesidad de tu programa haciendo un show_all ahi significa que todos los widgets se muestran incluidos los que puede que quieras ocultos yo personalmente suelo usar gtk_widget_show por cada widget gracias hardy --- rpons fija modos [#gnome-hispano -o hardy] alguna pregunta mas ? continua German ok pongamosle acción a nuestra ventana lo primero que haremos es que nos abrá el selector de archivos el selector de archivos es una ventana predefinida de GTK+ que permite seleccionar un archivo navegar por los directorios, etc. Simplemente tenemos que usarla. Tal cual como lo hacen todas las aplicaciones en su menu/botón "Abrir". Qué debemos hacer? Asociar una función a la señal "clicked" del botón button_file recordemos que el código dice: button_file = gtk_button_new_from_stock ("gtk-open"); gtk_widget_show (button_file); gtk_box_pack_start (GTK_BOX (hbox_entry), button_file, FALSE, FALSE, 0); añadimos entonces: g_signal_connect (G_OBJECT (button_file), "clicked", G_CALLBACK (on_button_clicked), NULL); como nuestra primera aproximación lo que hemos hecho es: Cuando ocurra la señal "clicked" en el objecto "button_file" llame a la función "on_button_clicked" y no le pase ningún parámetro extra (NULL) entonces ahora debemos implementar la función "on_button_cliked" que será de la siguiente manera: void on_button_clicked (GtkWidget * widget, gpointer user_data) donde en el primer parámetro vendrá el botón que acaba de ser presionado el segundo parámetro es un *cualquier dato* que queramos pasar a esta función y que en nuestro caso, le hemos pasado NULL así que no lo usaremos por el momento. German, tenemos una pregunta adelante --- rpons fija modos [#gnome-hispano +o aki_oskar] es sobre pascal va de eventos gtk_signal_connect_object(gtk_object(b2),'clicked',gtk_signal_func(@boton2),pgtkobject(ventana2)); eso es gtk1.2 en donde llamo a @boton2, se podría llamar a boton y pasar como parametro el 2?? si, es 1.2 aki_oskar: quieres usar una misma función para 2 botones para 10 botones, sí y que distinga si es una u otra dependiendo del valor extra entonces el user_data es con lo que puedes distinguir si, que distinga el número que le paso si necesitas mas de un solo dato puedes crear una estructura, y usarla para pasar +1 dato por cierto, 'user_data' es un gpointer (= void *) pero si quereis pasar enteros no se hace así: &entero si no: GINT_TO_POINTER (entero) y luego para recuperarlo, en 'on_button_clicked': entero = GPOINTER_TO_INT (user_data); lo de estructura he probado, pero no lo controlo totalmente hay que tener en cuenta que 'user_data' debe apuntar a memoria correcta es decir, no vale hacer: function (void) { Estructura s; s.dato1 = loquesea; g_signal_connect (..., &s); no vale, porque lo que se le esta pasando es un puntero a datos locales qye, cuando termine 'function', dejaran de existir por ello, la forma correcta es: Estructura *s; s = g_malloc (sizeof Estructura); g_signal_connect (..., s); en pascal, lo de malloc no existe es decir, asignas memoria para los datos a pasar aki_oskar: bueno, pues s/malloc/funcion_de_pascal_para_malloc sería cuestión de buscarlo si, no lo recuerdo, pero existe gracias aki_oskar :) continuamos con la charla de acuerdo --- rpons fija modos [#gnome-hispano -o aki_oskar] ok quedamos en... ok en que acabmos de asignar la función on_button_clicked a la señal clicked del boton button_file declaramos la función: void on_button_clicked (GtkWidget * widget, gpointer user_data) { GtkWidget *fsel = NULL; ... fsel es el widget del dialogo para elegir un archivo y basta que ejecutemos la siguiente función para crearlo: fsel = gtk_file_selection_new ("Abrir archivo"); NO olvidar, que los widgets no se muestran hasta que nosotros digamos cuando hacerlo asi que esa función simplemente la crea el resultado final (que esperamos) es algo como lo que se muestra en: http://www.calcifer.org/documentos/irclogs/ejemplo-gtk+/fsel.png Asi tenemos una nueva ventana y como nueva ventana, vemos que tiene 2 botones (Cancelar y OK) esta ventana nos ahorra harto trabajo de programación, pero no es mágica si sólo le damos: gtk_widget_show (fsel); mostrará el diálogo, pero no efectuará acción alguna al presionar los botones simplemente porque no sabe que ejecutar. Entonces, debemos asignarle funciones al evento "clicked" de cada botón los requerimientos serán: - Si presiona el botón "Cancelar" queremos que el diálogo desaparezca (se destruya) - Si presiona el botón "OK" queremos obtener el nombre del archivo seleccionado y luego queremos que se destruya la ventana, porque no la queremos eternamente en la pantalla, más aún si ya cumplió su objetivo. para el primer caso, tenemos 2 alternativas la primera es: g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button), "clicked", GTK_SIGNAL_FUNC (cancel_callback), (gpointer) fsel); hmmm... permitanmte reescribirlo: g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button), "clicked", G_CALLBACK (cancel_callback), (gpointer) fsel); lo anterior, fue un GTK+ 1.2-ismo si notan, el primer argumento es GTK_FILE_SELECTION (fsel)->cancel_button resulta que fsel es un widget GtkFileSelection, una ventana a fin de cuentas pero nosotros queremos el boton "cancelar" de esa ventana. Pues bien, ese boton se llama "cancel_button" otra observación: el último argumento es fsel. Ahora veremos porqué. void cancel_callback (GtkWidget * button, gpointer data) { gtk_widget_destroy (GTK_WIDGET (data)); data = NULL; } queremos destruir la ventana. Pero cuando se emite la señal "clicked" lo que recibe la función es un botón y nosotros queremos destruir la ventana a la cual pertenece el botón por eso, le pasamos como argumento "fsel", la ventana. la cual recibimos ahora como "data". tambien se puede obtener con gtk_widget_get_toplevel aunque es mejor, para aprender, como lo ha hecho gpoo :-) es decir gtk_widget_destroy (gtk_widget_get_toplevel (button)); bueno, también nos pudimos haber ahorrado la llamada a esa función utilizando g_object_signal_connect_data, no es así rodrigo? g_signal_connect_data, quise decir usando algo como: hmm, no estoy seguro, en 1.2 era gtk_signal_connect_object si, cierto, g_signal_connect_data g_signal_connect_data (G_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button), "clicked", G_CALLBACK (gtk_widget_destroy), NULL, fsel, G_CONNECT_AFTER) con eso, nos evitamos crear la función cancel_callback, y se obtiene un comportamiento similar. ahora nos falta la funcionalidad para el botón "OK" del diálogo g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button), "clicked", GTK_SIGNAL_FUNC (open_callback), (gpointer) fsel); cuya idea es similar la diferencia es que en open_callback usaremos la funcionalidad que queremos con el nombre del archivo. vemos una implementacióm simple de open_callback void open_callback (GtkWidget * button, gpointer data) { gchar *filename; filename = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (data))); g_print ("%s\n", filename); gtk_widget_destroy (GTK_WIDGET (data)); data = NULL; } donde usamos la función gtk_file_selection_get_filename para obtener el nombre del archivo seleccionado si no hay archivos seleccionados, entregará la ruta en donde se encuentra lo hemos dejado en filename y lo imprimimos en consola. No es lo que queremos, pero es una primera aproximación. Vemos que empleamos el valor "data" que hemos pasado como argumento. De esta forma, obtenemos información que no es propia del botón, sino de otro objeto que lo hemos asociado al llamar al callback finalmente destruimos la ventana (data) y falto liberar el espacio solicitado con g_strdup es decir, falto: g_free (filename); ya estamos en condiciones de ver el ejemplo final. es decir, como quedará todo unido y mostrando el texto en la entrada de texto. mostrando el nombre del archivo. Para poder asignar un texto a una entrada de texto usamos la función: gtk_entry_set_set pero no podemos usarla en "open_callback", porque no tenemos ningun GtkEntry. El GtkEntry existe solo en el programa principal. y open_callback puede ser llamado en cualquier momento. Entonces, volvemos al lugar donde definimos la función a la señal clicked de "button_file" y haremos: g_signal_connect (G_OBJECT (button_file), "clicked", G_CALLBACK (on_button_clicked), entry_file); hemos añadido "entry_file" en vez de NULL así en on_button_clicked recibiremos ese dato y podremos modificarlo. Pero se nos presenta un nuevo problema. la función luce así hasta el momento: void on_button_clicked (GtkWidget * widget, gpointer user_data) { GtkWidget *fsel = NULL; fsel = gtk_file_selection_new ("Abrir archivo"); g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button), "clicked", GTK_SIGNAL_FUNC (open_callback), (gpointer) fsel); g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button), "clicked", G_CALLBACK (cancel_callback), (gpointer) fsel); ... en esta función aún no sabemos el nombre del archivo. Sólo lo sabremos después que se presiones "OK" sin embargo, hemos ocupado el único parámetro que disponemos para entregar más datos, el que ocupa fsel en estos momentos. Tenemos la alternativa de usar una estructura, como nos explicó rodrigo hace un rato. pero también podemos usar: g_object_set_data es decir, "añadirle" nuevos datos a un objecto, en este caso al botón ok. algo así como: g_object_set_data (G_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button), "entry", user_data); es decir, al objeto ok_button de fsel, le asignamos user_data y cuya clave será "entry" en estos momentos user_data contiene "entry_file", que acabamos de añadirlo. Y para recuperar dicho valor en open_callback usaremos: GtkWidget *entry; entry = g_object_get_data (G_OBJECT (button), "entry"); así la función open_callback quedará: void open_callback (GtkWidget * button, gpointer data) { gchar *filename; GtkWidget *entry; entry = g_object_get_data (G_OBJECT (button), "entry"); filename = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (data))); gtk_entry_set_text (GTK_ENTRY (entry), filename); gtk_widget_destroy (GTK_WIDGET (data)); data = NULL; g_free (filename); } button es "ok_button", data es "fsel" entry es el valor recuperado de "entry" como objeto adjunto de button en filename hemos recuperado el nombre del archivo y luego se lo hemos asginado a entry a través de gtk_entry_set_text finalmente nos deshacemos de los datos que no necesitamos y del cual estamos ocupando memoria Después de llamar a la función, la ventana deberá quedar como se ve en: http://www.calcifer.org/documentos/irclogs/ejemplo-gtk+/ejemplo1-final.png Bueno, las etiquetas aún no las hemos ocupado antes de ocuparlo y terminar, alguna pregunta? como se maneja una seleccion multiple? de archivos o file_selection no acepta seleccion multiple? gchar** gtk_file_selection_get_selections (GtkFileSelection *filesel); void gtk_file_selection_set_select_multiple (GtkFileSelection *filesel, gboolean select_multiple); gboolean gtk_file_selection_get_select_multiple (GtkFileSelection *filesel); gtk_file_selection_get_selections devuelve un array con todos los ficheros seleccionados y gtk_file_selection_set_select_multiple/gtk_file_selection_get_select_multiple permiten cambiar el tipo de seleccion --- rodrigo fija modos [#gnome-hispano +v Drak] --- nercrack_out es ahora conocido como NerCrack mas preguntas? --- rpons fija modos [#gnome-hispano +v hardy] --- rodrigo fija modos [#gnome-hispano -v Drak] adelante hardy Podríamos haber hecho, que el diálogo de abrir archivo retorne una cadena conteniendo el nombre y asignarle ese valor a entry ? no al menos no como entiendo tu pregunta podrían explicarme por ? resulta que no saber en que momento se ejecutará el callback luego, no puedes hacer: a = open_callback (...); porque no es secuencial la ejecución hacer eso, significa que el programa debe necesariamente detenerse en ese punto hmm, JavaK tiene una pregunta interesante sobre esto --- rodrigo fija modos [#gnome-hispano +v JavaK] ok. adelante JavaK a ver gtkfileselection es un gtkdialog --- rpons fija modos [#gnome-hispano -v hardy] se le podria asignar valores de respuesta a los botones ok y cancel? y asi poder usarlo con gtk_run_dialog? la verdad, nunca lo había pensado así, probablemente funcione y ademas, probablemente los botones ya tengo ids asociados :/ es q yo se crear botones con accines pero no se asignarles valores de respuesta exacto: en gtkfilesel.c: filesel->cancel_button = gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); /* The OK button */ filesel->ok_button = gtk_dialog_add_button (dialog, GTK_STOCK_OK, GTK_RESPONSE_OK); es decir, que probablemente se pueda hacer algo como: filesel = gtk_file_selection_new (); if (gtk_dialog_run (GTK_DIALOG (fielsel)) == GTK_RESPONSE_OK) { gtk_entry_set_text (entry, gtk_file_selection_get_file_name (fielsel)); } gpoo: lo pruebas, please? es que en gtk1.2 gtk_dialog_run no existía ok gtk_dialog_run es bloqueante si bloque la aplicacion hasta que se pulse uno de los botones bueno, bloque el UI de la aplicación no la app en si es modal te refieres si gracias pues ;) gracias a ti JavaK :) funciona --- rodrigo fija modos [#gnome-hispano -v JavaK] en el ejemplo, podría ser de la siguiente forma: mas preguntas ? if (gtk_dialog_run (GTK_DIALOG (fsel)) == GTK_RESPONSE_OK) { gtk_entry_set_text (GTK_ENTRY (user_data), g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (fsel)))); } luego, faltaría destruir la ventana. alguien tiene algo que añadir ? :) prosigo para terminar en realidad, darle un uso a las etiquetas que puse :-) y es aprovechar otra señal, en este caso de entry_file, de tal forma que, cuando cambie, la etiqueta refleje el contenido modificado. es corto: g_signal_connect (G_OBJECT (entry_file), "changed", G_CALLBACK (on_entry_file_changed), label_archivo); Lo mismo que hemos visto, cuando se emita la señal "changed" en entry_file, se ejecute on_entry_file_changed con el argumento label_archivo. Así, tenemos: void on_entry_file_changed (GtkWidget * entry, gpointer data) { gtk_label_set_text (GTK_LABEL (data), gtk_entry_get_text (GTK_ENTRY (entry))); } esto tiene 2 efectos: 1. Cuando se selecciona el archivo desde el diálogo, aparecerá en entry_file y en la etiqueta a la derecha. 2. Cuando se escribar cualquier texto en entry_file se irá actualizando la etiqueta por cada caracter que se escriba/borre. Para terminar, el ejemplo completo: include void open_callback (GtkWidget * button, gpointer data) { gchar *filename; GtkWidget *entry; entry = g_object_get_data (G_OBJECT (button), "entry"); filename = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (data))); gtk_entry_set_text (GTK_ENTRY (entry), filename); gtk_widget_destroy (GTK_WIDGET (data)); data = NULL; g_free (filename); } void on_entry_file_changed (GtkWidget * entry, gpointer data) { gtk_label_set_text (GTK_LABEL (data), gtk_entry_get_text (GTK_ENTRY (entry))); } void cancel_callback (GtkWidget * button, gpointer data) { gtk_widget_destroy (GTK_WIDGET (data)); data = NULL; } void on_button_clicked (GtkWidget * widget, gpointer user_data) { GtkWidget *fsel = NULL; fsel = gtk_file_selection_new ("Abrir archivo"); g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button), "clicked", GTK_SIGNAL_FUNC (open_callback), (gpointer) fsel); g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button), "clicked", G_CALLBACK (cancel_callback), (gpointer) fsel); g_object_set_data (G_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button), "entry", user_data); /* Alternativa de estudio (falta destruir la ventana): if (gtk_dialog_run (GTK_DIALOG (fsel)) == GTK_RESPONSE_OK) { gtk_entry_set_text (GTK_ENTRY (user_data), g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (fsel)))); }*/ gtk_widget_show (fsel); } int main (int argc, char **argv) { GtkWidget *window; GtkWidget *vbox_principal; GtkWidget *hbox_entry; GtkWidget *entry_file; GtkWidget *button_file; GtkWidget *hbox_label; GtkWidget *label1; GtkWidget *label_archivo; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "window"); vbox_principal = gtk_vbox_new (FALSE, 4); gtk_container_add (GTK_CONTAINER (window), vbox_principal); gtk_container_set_border_width (GTK_CONTAINER (vbox_principal), 4); hbox_entry = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_entry, FALSE, TRUE, 0); entry_file = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (hbox_entry), entry_file, TRUE, TRUE, 0); button_file = gtk_button_new_from_stock ("gtk-open"); gtk_widget_show (button_file); gtk_box_pack_start (GTK_BOX (hbox_entry), button_file, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button_file), "clicked", G_CALLBACK (on_button_clicked), entry_file); hbox_label = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox_principal), hbox_label, FALSE, TRUE, 0); label1 = gtk_label_new ("Nombre del archivo:"); gtk_box_pack_start (GTK_BOX (hbox_label), label1, FALSE, FALSE, 0); label_archivo = gtk_label_new (""); gtk_box_pack_start (GTK_BOX (hbox_label), label_archivo, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (entry_file), "changed", G_CALLBACK (on_entry_file_changed), label_archivo); gtk_widget_show_all (window); gtk_main (); return 0; } una observación antes de cerrar: normalmente, y por orden, uno conecta las señales a la medida que crear los widgets sin embargo, en este caso, enlace la señal "changed" al final y no inmediatamente después de crear el widget "entry_file". La razón: se le quiere pasar como argumento "label_archivo", y éste debe existir. Por lo tanto, no se puede hacer antes. Bueno, ha resultado algo largo, casi 2 horas y media. Espero que haya sido de utilidad y que hayan podido aprender algo mas de GTK+. Las siguientes charlas serán: - Segunda revisión al sistema de objetos. Tocando todos los tópicos que no se vieron en la primera charla del tema - GTK+ intermedio 2, con widgets específicos como GtkTextBuffer y GtkTreeView hmm, habria que poner una sobre mas contenedores si, en realidad, pensé que avanzaríamos más rápido. Pero podemos tocarla cuando se vea libglade. quiero decir, glade. Que allí se ve gráficamente. Pero bueno, esten atentos. Muchas gracias por tu tiempo German Cualquier modificación, la sabrán antes días antes de la charla. alguna pregunta ? --- rpons fija modos [#gnome-hispano +v hardy] adelante hardy Aprovechando su experiencia, ¿ Qué programa nos recomendarían estudiar para comprender mejor lo que vimos hoy ? un buen ejemplo de app es gedit para cosas mas sencillas, mira los ejemplos que vienen con gtk bueno, gedit es la aplicación que siempre se recomienda oh, Gracias. sin embargo, ahora usa varias funciones de apoyo. Pero luego se comienza a entender. está muy bien documentado. Pero para todo, siempre tienes gtk-demo en el cual tienes trozo de código que muestran distintas funcionalidades de Gtk. en forma bien parcelada. esa suena bien :-D ! un detalle: El ejemplo que vimos hoy, no atiende el evento "delete-event", eso queda de tarea (lo vimos la semana pasada) más preguntas? si cierto. Para cerrar la ventana y terminar la app. de mi parte no gracias. en debian el paquete se llama gtk2.0-examples --- rpons fija modos [#gnome-hispano -v hardy] alguna cosa por contar, o sino nos vemos la semana que viene parece que no hay mas preguntas Muchas gracias a Rodrigo y German por su tiempo --- gpoo fija modos [#gnome-hispano -m]