Charlas de GNOME Hispano: GTK+ intermedio (parte 1)

Relatores: Rodrigo Moya (rodrigo) y Germán Poo-Caamaño (gpoo)
Moderador: Ramón Pons Vivanco (rpons)
Domingo 26 de enero de 2003, 21:30 horas UTC
irc.gnome.cl #gnome-hispano

rpons
Hola buenas noches, bienvenidos a #gnome-hispano
esta noche tenemos con nosotros a German Poo
que nos va a hablar de GTK+
gpoo
Puedo comenzar?
rpons
si adelante German :)
--- gnomero fija modos [#gnome-hispano +o setepo]
--- gnomero fija modos [#gnome-hispano +o telemaco]
--- rpons fija modos [#gnome-hispano -ooo gnomero setepo telemaco]
gpoo
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.
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.
rodrigo
gpoo: un comentario
gpoo
adelante.
rodrigo
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
gpoo
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
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
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 <gtk/gtk.h>
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?
--- rpons fija modos [#gnome-hispano +o Drak]
rpons
no parece que haya muchas preguntas
rodrigo
<Drak> se pueden omitir los widget_show
<Drak> y poner un show_all solo al final de todo?
gpoo
Si
se puede hacer.
Drak
gracias
--- rpons fija modos [#gnome-hispano +o hardy]
--- rpons fija modos [#gnome-hispano -o Drak]
gpoo
de hecho, ahorra líneas de código :-)
rpons
adelante hardy
hardy
<gpoo> Hay algún estándar de usar o no show_all ??
rodrigo
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);
hardy
ok, etonces es cuestión de estilo y gusto de cada uno. Gracias.
rodrigo
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
--- rpons fija modos [#gnome-hispano -o hardy]
rpons
gracias hardy
alguna pregunta mas ?
continua German
gpoo
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.
rpons
German, tenemos una pregunta
--- rpons fija modos [#gnome-hispano +o aki_oskar]
gpoo
adelante
aki_oskar
es sobre pascal
va de eventos
gtk_signal_connect_object(gtk_object(b2),'clicked',gtk_signal_func(@boton2),pgtkobject(ventana2));

rodrigo
eso es gtk1.2
aki_oskar
en donde llamo a @boton2, se podría llamar a boton y pasar como parametro
 el 2??
si, es 1.2
gpoo
aki_oskar: quieres usar una misma función para 2 botones
aki_oskar
para 10 botones, sí
gpoo
y que distinga si es una u otra dependiendo del valor extra
rodrigo
entonces el user_data es con lo que puedes distinguir
aki_oskar
si, que distinga el número que le paso
rodrigo
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);
aki_oskar
lo de estructura he probado, pero no lo controlo totalmente
rodrigo
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);
aki_oskar
en pascal, lo de malloc no existe
rodrigo
es decir, asignas memoria para los datos a pasar
aki_oskar: bueno, pues s/malloc/funcion_de_pascal_para_malloc
aki_oskar
sería cuestión de buscarlo
rodrigo
si, no lo recuerdo, pero existe
rpons
gracias aki_oskar :)
continuamos con la charla
--- rpons fija modos [#gnome-hispano -o aki_oskar]
aki_oskar
de acuerdo
gpoo
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:
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".
rodrigo
tambien se puede obtener con gtk_widget_get_toplevel
aunque es mejor, para aprender, como lo ha hecho gpoo
gpoo
:-)
rodrigo
es decir gtk_widget_destroy (gtk_widget_get_toplevel (button));
gpoo
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:
rodrigo
hmm, no estoy seguro, en 1.2 era gtk_signal_connect_object
si, cierto, g_signal_connect_data
gpoo
g_signal_connect_data (G_OBJECT
<gpoo>      (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:

ejemplo-gtk+/ejemplo1-final.png

Bueno, las etiquetas aún no las hemos ocupado
antes de ocuparlo y terminar, alguna pregunta?
--- rodrigo fija modos [#gnome-hispano +v Drak]
--- nercrack_out es ahora conocido como NerCrack
--- rpons fija modos [#gnome-hispano +v hardy]
--- rodrigo fija modos [#gnome-hispano -v Drak]
rodrigo
<Drak> como se maneja una seleccion multiple?
<Drak> de archivos
<Drak> 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
mas preguntas?
rpons
adelante hardy
hardy
Podríamos haber hecho, que el diálogo de abrir archivo retorne una cadena
 conteniendo el nombre y asignarle ese valor a entry ?
gpoo
no
al menos no como entiendo tu pregunta
hardy
podrían explicarme por ?
gpoo
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
--- rodrigo fija modos [#gnome-hispano +v JavaK]
rodrigo
hmm, JavaK tiene una pregunta interesante sobre esto
hardy
ok.
rodrigo
adelante JavaK
--- rpons fija modos [#gnome-hispano -v hardy]
JavaK
a ver
gtkfileselection es un gtkdialog
se le podria asignar valores de respuesta a los botones ok y cancel?
y asi poder usarlo con gtk_run_dialog?
rodrigo
la verdad, nunca lo había pensado así, probablemente funcione
y ademas, probablemente los botones ya tengo ids asociados
JavaK
:/
es q yo se crear botones con accines
pero no se asignarles valores de respuesta
rodrigo
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
gpoo
ok
gtk_dialog_run es bloqueante
rodrigo
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
JavaK
es modal te refieres
rodrigo
si
JavaK
gracias pues ;)
rpons
gracias a ti JavaK :)
--- rodrigo fija modos [#gnome-hispano -v JavaK]
gpoo
funciona
en el ejemplo, podría ser de la siguiente forma:
rpons
mas preguntas ?
gpoo
 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.
rpons
alguien tiene algo que añadir ?
:)
gpoo
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 <gtk/gtk.h>
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

rodrigo
hmm, habria que poner una sobre mas contenedores
gpoo
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.
rpons
Muchas gracias por tu tiempo German
gpoo
Cualquier modificación, la sabrán antes días antes de la charla.
--- rpons fija modos [#gnome-hispano +v hardy]
rpons
alguna pregunta ?
adelante hardy
hardy
Aprovechando su experiencia, ¿ Qué programa nos recomendarían estudiar
 para comprender mejor lo que vimos hoy ?
rodrigo
un buen ejemplo de app es gedit
para cosas mas sencillas, mira los ejemplos que vienen con gtk
gpoo
bueno, gedit es la aplicación que siempre se recomienda
hardy
oh, Gracias.
gpoo
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.
hardy
esa suena bien :-D !
gpoo
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?
hardy
si cierto. Para cerrar la ventana y terminar la app.
de mi parte no gracias.
--- rpons fija modos [#gnome-hispano -v hardy]
gpoo
en debian el paquete se llama gtk2.0-examples
rpons
alguna cosa por contar, o sino nos vemos la semana que viene
rodrigo
parece que no hay mas preguntas
rpons
Muchas gracias a Rodrigo y German por su tiempo
--- gpoo fija modos [#gnome-hispano -m]