Diferencias entre las revisiones 1 y 2
Versión 1 con fecha 2008-07-10 13:29:19
Tamaño: 59354
Editor: JuanPizarro
Comentario:
Versión 2 con fecha 2008-12-04 08:49:09
Tamaño: 59385
Editor: anónimo
Comentario: converted to 1.6 markup
Los textos eliminados se marcan así. Los textos añadidos se marcan así.
Línea 3: Línea 3:
[[TableOfContents()]] <<TableOfContents>>
Línea 21: Línea 21:
attachment:packbox1.png {{attachment:packbox1.png}}
Línea 59: Línea 59:
attachment:packbox2.png {{attachment:packbox2.png}}
Línea 394: Línea 394:
attachment:cajadebotones.png {{attachment:cajadebotones.png}}
Línea 648: Línea 648:
attachment:tablepacking.png {{attachment:tablepacking.png}}
Línea 877: Línea 877:
attachment:notebook.png {{attachment:notebook.png}}
Línea 1119: Línea 1119:
attachment:panedwindow.png {{attachment:panedwindow.png}}
Línea 1209: Línea 1209:
attachment:fixed.png {{attachment:fixed.png}}
Línea 1324: Línea 1324:
attachment:aspectframe.png {{attachment:aspectframe.png}}
Línea 1414: Línea 1414:
 . '''''Volver a:''''' [:Documentacion/Desarrollo/Gtk:Gtk+]  . '''''Volver a:''''' [[Documentacion/Desarrollo/Gtk|Gtk+]]

Contenedores

GTK utiliza los contenedores para colocar los widgets de una forma determinada. Cuando se desarrolla una aplicación, normalmente se necesita colocar más de un widget dentro de una ventana. En el ejemplo anterior de helloworld se usa sólo un contenedor (gtk_container_add (GTK_CONTAINER (window), button);), para colocar el botón dentro de la ventana donde será mostrado. Pero, ¿qué pasa si se quiere usar más de un widget dentro de una ventana?, ¿cómo se puede controlar la posición de los widgets?

En otros sistemas gráficos (como por ejemplo MS Windows), la colocación de los widgets dentro de las ventanas se hace por medio de coordenadas relativas. Esto hace necesario un nivel de detalle a la hora de diseñar las ventanas de las aplicaciones que lo hacen indeseable. En GTK+, al igual que en todos los toolkits gráficos provinientes del mundo UNIX (Motif, QT, GTK, AWT de Java), está basado en el modelo de contenedores, donde no es necesario el uso de coordenadas. Simplemente se crean distintos tipos de contenedores (cada uno de los cuales coloca los widgets dentro de si mismo de una forma determinada) para cada caso concreto, y simplemente se colocan widgets dentro de dichos contenedores. La forma en que se metan los widgets dentro del contenedor define cómo se comportarán dichos widgets cuando la ventana contenedora cambie de tamaño.

Cajas

Existen varios tipos de contenedores que se irán explicando a lo largo de este capítulo, pero los widgets GtkHBox y GtkVBox son los más usados. GtkHBox y GtkVBox son cajas invisibles que sirven para empaquetar los widgets que se vayan a colocar dentro de una ventana u otro contenedor. Cuando se empaquetan widgets en una caja horizontal (GtkHBox) se insertan horizontalmente de izquierda a derecha o de derecha a izquierda, dependiendo de la función que se utilice después. En una caja vertical (GtkVBox) se insertan de arriba a abajo o vice versa. También se puede usar una combinación de cajas dentro o al lado de otras cajas para crear el efecto deseado.

Para crear una caja horizontal se hace una llamada a la función gtk_hbox_new(), y para cajas verticales, gtk_vbox_new(). Las funciones gtk_box_pack_start() y gtk_box_pack_end se usan para colocar los widgets dentro de las cajas creadas. La función gtk_box_pack_start coloca los widgets de arriba a abajo en una caja vertical y de izquierda a derecha en una horizontal, mientras que gtk_box_pack_end() hace lo contrario, que es colocar los widgets de abajo a arriba en una caja vertical y de derecha a izquierda en una horizontal. Usando estas funciones será posible alinear los widgets a la derecha o izquierda de la caja según se desee.

Usando estas funciones, GTK sabe en qué posición colocar los widgets y así poder cambiar el tamaño de los mismos automáticamente, cuando se cambia el tamaño del contenedor. Además cuentan con varias opciones para poder cambiar el estilo de colocación de los widgets. En la siguiente figura se muestran los 5 estilos diferentes de colocación.

Figura 1. Cinco estilos diferentes de colocación

packbox1.png

Cada línea contiene una caja horizontal (hbox) con varios botones. La llamada a la función gtk_box_pack es para colocar los botones en la caja horizontal.

Las dos funciones para añadir widgets a las cajas tienen la siguiente forma:

   1 void gtk_box_pack_start (       box,     
   2         child,   
   3         expand,          
   4         fill,    
   5         padding);        
   6 GtkBox *        box;
   7 GtkWidget *     child;
   8 gboolean        expand;
   9 gboolean        fill;
  10 guint   padding;
  11 void gtk_box_pack_end ( box,     
  12         child,   
  13         expand,          
  14         fill,    
  15         padding);        
  16 GtkBox *        box;
  17 GtkWidget *     child;
  18 gboolean        expand;
  19 gboolean        fill;
  20 guint   padding;

El primer argumento se refiere a la caja en la que se va a colocar el objeto, el segundo argumento es el objeto. Los objetos en este ejemplo son los botones, así que se colocarán los botones dentro de las cajas, pero puede ser cualquier otro widget.

El argumento expand controla que los botones se extiendan hasta rellenar todo el espacio dentro de la caja (TRUE), o que la caja se ajuste al tamaño de los botones (FALSE). Con expand con una valor de FALSE se pueden alinear los botones a la izquierda o a la derecha.

El argumento fill de las funciones gtk_box_pack controla si el espacio extra se coloca en los botones (TRUE), o como espacio extra entre cada botón (FALSE). Esto sólo tiene validez si el argumento expand está a TRUE.

El argumento padding controla el espacio añadido a cada lado del botón. En la siguiente figura se puede ver mejor el resultado.

Figura 2. Diferencias entre padding y spacing

packbox2.png

Para crear una caja, las funciones tienen el siguiente formato:

   1 GtkWidget * gtk_hbox_new (      homogenous,      
   2         spacing);        
   3 gboolean        homogenous;
   4 gint    spacing;
   5 GtkWidget * gtk_vbox_new (      homogenous,      
   6         spacing);        
   7 gboolean        homogenous;
   8 gint    spacing;

El argumento homogeneous controla si cada botón dentro de la caja tiene el mismo tamaño (la misma anchura en una hbox y la misma altura en una vbox). Si este argumento está a TRUE, las funciones de gtk_box_pack() funcionan como si el argumento expand estuviera siempre a TRUE.

El argumento spacing controla el espacio añadido entre los botones. La figura anterior muestra el resultado y la diferencia con el argumento padding de las funciones gtk_box_pack().

Ejemplo de uso de cajas

El código que se muestra a continuación es el usado para crear las figuras mostradas anteriormente.

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include "gtk/gtk.h"
   4 
   5 gint delete_event( GtkWidget *widget,
   6                    GdkEvent  *event,
   7                    gpointer   data )
   8 {
   9         gtk_main_quit ();
  10         return FALSE;
  11 }
  12 
  13 /* Crea una hbox con botones con etiquetas.
  14  * Se muestran todos los widgets (gtk_widget_show()) a excepción de la caja. */
  15 GtkWidget *make_box( gboolean homogeneous,
  16                      gint     spacing,
  17                      gboolean expand,
  18                      gboolean fill,
  19                      guint    padding )
  20 {
  21         GtkWidget *box;
  22         GtkWidget *button;
  23         char padstr[80];
  24 
  25         /* Crea una hbox con los parámetros establecidos
  26          * para homogeneous y spacing */
  27         box = gtk_hbox_new (homogeneous, spacing);
  28 
  29         /* Crea una serie de botones con etiqueta */
  30         button = gtk_button_new_with_label ("gtk_box_pack");
  31         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  32         gtk_widget_show (button);
  33 
  34         button = gtk_button_new_with_label ("(box,");
  35         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  36         gtk_widget_show (button);
  37 
  38         button = gtk_button_new_with_label ("button,");
  39         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  40         gtk_widget_show (button);
  41 
  42         /* Crea un botón con etiqueta dependiendo del valor del parámetro
  43          * expand. */
  44         if (expand == TRUE)
  45                 button = gtk_button_new_with_label ("TRUE,");
  46         else
  47                 button = gtk_button_new_with_label ("FALSE,");
  48 
  49         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  50         gtk_widget_show (button);
  51 
  52         /* Lo mismo con el botón de "expand"
  53          * pero esta vez de la forma abreviada. */
  54         button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
  55         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  56         gtk_widget_show (button);
  57 
  58         sprintf (padstr, "%d);", padding);
  59 
  60         button = gtk_button_new_with_label (padstr);
  61         gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  62         gtk_widget_show (button);
  63 
  64         return box;
  65 }
  66 
  67 int main( int   argc,
  68           char *argv[])
  69 {
  70         GtkWidget *window;
  71         GtkWidget *button;
  72         GtkWidget *box1;
  73         GtkWidget *box2;
  74         GtkWidget *separator;
  75         GtkWidget *label;
  76         GtkWidget *quitbox;
  77         int which;
  78 
  79 
  80         gtk_init (&argc, &argv);
  81 
  82         if (argc != 2) {
  83                 fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
  84                 /* Esto finaliza GTK y sale con un estatus de 1. */
  85                 exit (1);
  86         }
  87 
  88         which = atoi (argv[1]);
  89 
  90         /* Crea una ventana */
  91         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  92 
  93         /* Simpre se debe conectar la señal delete_event a la ventana
  94          * principal, para así poder cerrarla.  */
  95         g_signal_connect (G_OBJECT (window), "delete_event",
  96                           G_CALLBACK (delete_event), NULL);
  97         gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  98 
  99         /* Crea una caja vertical (vbox) para colocar las cajas horizontales dentro.
 100          * Esto permite colocar las cajas horizontales llenas de botones una
 101          * encima de la otra dentro de la caja vertical (vbox). */
 102         box1 = gtk_vbox_new (FALSE, 0);
 103 
 104         /* Muestra una de las Figuras de arriba. */
 105         switch (which) {
 106         case 1:
 107                 /* crea una etiqueta. */
 108                 label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
 109 
 110                 /* Alínea la etiqueta a la izquierda.  Se hablará más adelante de esta
 111                  * función. */
 112                 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 113 
 114                 /* Coloca la etiqueta dentro de la caja vertical (vbox box1).  Los
 115                  * widgets que se añaden a una vbox se colocan, por orden, uno
 116                  * encima del otro. */
 117                 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
 118 
 119                 /* Muestra la etiqueta */
 120                 gtk_widget_show (label);
 121 
 122                 /* Llamada a la función make box, para crear un hbox con
 123                  * una serie de botones - homogeneous = FALSE, spacing = 0,
 124                  * expand = FALSE, fill = FALSE, padding = 0 */
 125                 box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
 126                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 127                 gtk_widget_show (box2);
 128 
 129                 /* Llamada a la función make box, para crear otra hbox con
 130                  * una serie de botones - homogeneous = FALSE, spacing = 0,
 131                  * expand = TRUE, fill = FALSE, padding = 0 */
 132                 box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
 133                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 134                 gtk_widget_show (box2);
 135 
 136                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 137                 box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
 138                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 139                 gtk_widget_show (box2);
 140 
 141                 /* Crea un separador, se verán más adelante,
 142                  * aunque son bastante sencillos. */
 143                 separator = gtk_hseparator_new ();
 144 
 145                 /* Coloca el separador en una vbox. Todos estos
 146                  * widgets han sido colocados en una vbox, así que estarán
 147                  * ordenados verticalmente. */
 148                 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
 149                 gtk_widget_show (separator);
 150 
 151                 /* Crea otra etiqueta y la muestra. */
 152                 label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
 153                 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 154                 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
 155                 gtk_widget_show (label);
 156 
 157                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 158                 box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
 159                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 160                 gtk_widget_show (box2);
 161 
 162                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 163                 box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
 164                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 165                 gtk_widget_show (box2);
 166 
 167                 /* Otro separador. */
 168                 separator = gtk_hseparator_new ();
 169                 /* Los tres últimos argumentos de gtk_box_pack_start son:
 170                  * expand, fill, padding. */
 171                 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
 172                 gtk_widget_show (separator);
 173 
 174                 break;
 175 
 176         case 2:
 177 
 178                 /* Crea una etiqueta, box1 es una
 179                  * vbox que ya ha sido creada    */
 180                 label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
 181                 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 182                 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
 183                 gtk_widget_show (label);
 184 
 185                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 186                 box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
 187                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 188                 gtk_widget_show (box2);
 189 
 190                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 191                 box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
 192                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 193                 gtk_widget_show (box2);
 194 
 195                 separator = gtk_hseparator_new ();
 196                 /* Los tres últimos argumentos de gtk_box_pack_start son:
 197                  * expand, fill, padding. */
 198                 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
 199                 gtk_widget_show (separator);
 200 
 201                 label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
 202                 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 203                 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
 204                 gtk_widget_show (label);
 205 
 206                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 207                 box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
 208                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 209                 gtk_widget_show (box2);
 210 
 211                 /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
 212                 box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
 213                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 214                 gtk_widget_show (box2);
 215 
 216                 separator = gtk_hseparator_new ();
 217                 /* Los tres últimos argumentos de gtk_box_pack_start son: expand, fill, padding. */
 218                 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
 219                 gtk_widget_show (separator);
 220                 break;
 221 
 222         case 3:
 223 
 224                 /* Esto demuestra la capacidad de gtk_box_pack_end() para
 225                  * alinear a la derecha los widgets. Primero, se crea una caja. */
 226                 box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
 227 
 228                 /* Crea una etiqueta colocada al final. */
 229                 label = gtk_label_new ("end");
 230                 /* Coloca la etiqueta usando gtk_box_pack_end(), así que se situa a la
 231                  * derecha de la hbox creada con la llamada a make_box(). */
 232                 gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
 233                 /* Show the label. */
 234                 gtk_widget_show (label);
 235 
 236                 /* Coloca box2 dentro de box1 (vbox) */
 237                 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
 238                 gtk_widget_show (box2);
 239 
 240                 /* Crea un separador. */
 241                 separator = gtk_hseparator_new ();
 242                 /* Establece las medidas del separador, una anchura de 400 pixels por 5 pixels
 243                  * de altura. La hbox que se creó anteriormente también tendrá 400 pixels de
 244                  * anchura,y la etiqueta "end" se separa de las otras etiquetas en la
 245                  * hbox. */
 246                 gtk_widget_set_size_request (separator, 400, 5);
 247                 /* coloca el separador en la vbox (box1) */
 248                 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
 249                 gtk_widget_show (separator);
 250         }
 251 
 252         /* Crea otra hbox.. (se pueden crear tantas cajas como se necesiten) */
 253         quitbox = gtk_hbox_new (FALSE, 0);
 254 
 255         /* Crea el botón para salir del programa. */
 256         button = gtk_button_new_with_label ("Quit");
 257 
 258         /* Establece la señal para terminar el programa cundo se pulsa el botón ("quit") */
 259         g_signal_connect_swapped (G_OBJECT (button), "clicked",
 260                                   G_CALLBACK (gtk_main_quit),
 261                                   G_OBJECT (window));
 262         /* Coloca el botón en la hbox (quitbox).
 263          * Los 3 últimos argumentos de gtk_box_pack_start son:
 264          * expand, fill, padding. */
 265         gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
 266         /* Coloca la hbox (quitbox) en la vbox (box1) */
 267         gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
 268 
 269         /* Coloca la vbox (box1), la cual contiene ahora todos los widgets, en la
 270          * ventana principal. */
 271         gtk_container_add (GTK_CONTAINER (window), box1);
 272 
 273         /* Se muestra todo lo restante */
 274         gtk_widget_show (button);
 275         gtk_widget_show (quitbox);
 276 
 277         gtk_widget_show (box1);
 278         /* Se muestra la ventana en último lugar, así todo se muestra a la vez. */
 279         gtk_widget_show (window);
 280 
 281         /* La función principal. */
 282         gtk_main ();
 283 
 284         /* El control vuelve aquí cuando se llama a gtk_main_quit(), pero no cuando
 285          * se usa exit(). */
 286 
 287         return 0;
 288 }

Cajas de botones

Uno de los usos más comunes que se le dan a las cajas citadas en el apartado anterior es para agrupar botones. Al ser esta una tarea muy común, GTK incluye las cajas de botones (Gtk?ButtonBox).

Las cajas de botones son una utilidad que permite crear grupos de botones de una forma rápida y sencilla. Estas cajas de botones pueden ser horizontales o verticales.

Las siguientes funciones son para crear una caja de botones horizontal y vertical.

   1           GtkWidget *gtk_hbutton_box_new( void );
   2 
   3           GtkWidget *gtk_vbutton_box_new( void );

Para añadir los botones a la caja de botones, se usa la siguiente función:

   1           gtk_container_add (GTK_CONTAINER (button_box), child_widget);

El siguiente ejemplo muestra las diferentes layout settings de las cajas de botones.

Figura 3. Diferentes layout settings de las cajas de botones

cajadebotones.png

   1 #include <gtk/gtk.h>
   2 
   3 /* Crea una Caja de Botones con los parámetros específicos */
   4 GtkWidget *create_bbox( gint  horizontal,
   5                         char *title,
   6                         gint  espacio,
   7                         gint  child_w,
   8                         gint  child_h,
   9                         gint  layout )
  10 {
  11   GtkWidget *frame;
  12   GtkWidget *bbox;
  13   GtkWidget *button;
  14 
  15   frame = gtk_frame_new (title);
  16 
  17   if (horizontal)
  18     bbox = gtk_hbutton_box_new ();
  19   else
  20     bbox = gtk_vbutton_box_new ();
  21 
  22   gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
  23   gtk_container_add (GTK_CONTAINER (frame), bbox);
  24 
  25   /* Establece la apariencia de la Caja de Botones */
  26   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
  27   gtk_box_set_spacing (GTK_BOX (bbox), espacio);
  28   /*gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);*/
  29 
  30   button = gtk_button_new_from_stock (GTK_STOCK_OK);
  31   gtk_container_add (GTK_CONTAINER (bbox), button);
  32 
  33   button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
  34   gtk_container_add (GTK_CONTAINER (bbox), button);
  35 
  36   button = gtk_button_new_from_stock (GTK_STOCK_HELP);
  37   gtk_container_add (GTK_CONTAINER (bbox), button);
  38 
  39   return frame;
  40 }
  41 
  42 int main( int   argc,
  43           char *argv[] )
  44 {
  45   static GtkWidget* window = NULL;
  46   GtkWidget *main_vbox;
  47   GtkWidget *vbox;
  48   GtkWidget *hbox;
  49   GtkWidget *frame_horz;
  50   GtkWidget *frame_vert;
  51 
  52   /* Inicializa GTK */
  53   gtk_init (&argc, &argv);
  54 
  55   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  56   gtk_window_set_title (GTK_WINDOW (window), "Cajas de Botones");
  57 
  58   g_signal_connect (G_OBJECT (window), "destroy",
  59                     G_CALLBACK (gtk_main_quit),
  60                     NULL);
  61 
  62   gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  63 
  64   main_vbox = gtk_vbox_new (FALSE, 0);
  65   gtk_container_add (GTK_CONTAINER (window), main_vbox);
  66 
  67   frame_horz = gtk_frame_new ("Cajas de Botones Horizontales");
  68   gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
  69 
  70   vbox = gtk_vbox_new (FALSE, 0);
  71   gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
  72   gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
  73 
  74   gtk_box_pack_start (GTK_BOX (vbox),
  75            create_bbox (TRUE, "Spread (espacio 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
  76                       TRUE, TRUE, 0);
  77 
  78   gtk_box_pack_start (GTK_BOX (vbox),
  79            create_bbox (TRUE, "Edge (espacio 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
  80                       TRUE, TRUE, 5);
  81 
  82   gtk_box_pack_start (GTK_BOX (vbox),
  83            create_bbox (TRUE, "Start (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_START),
  84                       TRUE, TRUE, 5);
  85 
  86   gtk_box_pack_start (GTK_BOX (vbox),
  87            create_bbox (TRUE, "End (espacio 10)", 10, 85, 20, GTK_BUTTONBOX_END),
  88                       TRUE, TRUE, 5);
  89 
  90   frame_vert = gtk_frame_new ("Cajas de Botones Verticales");
  91   gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
  92 
  93   hbox = gtk_hbox_new (FALSE, 0);
  94   gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
  95   gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
  96 
  97   gtk_box_pack_start (GTK_BOX (hbox),
  98            create_bbox (FALSE, "Spread (espacio 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
  99                       TRUE, TRUE, 0);
 100 
 101   gtk_box_pack_start (GTK_BOX (hbox),
 102            create_bbox (FALSE, "Edge (espacio 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
 103                       TRUE, TRUE, 5);
 104 
 105   gtk_box_pack_start (GTK_BOX (hbox),
 106            create_bbox (FALSE, "Start (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_START),
 107                       TRUE, TRUE, 5);
 108 
 109   gtk_box_pack_start (GTK_BOX (hbox),
 110            create_bbox (FALSE, "End (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_END),
 111                       TRUE, TRUE, 5);
 112 
 113   gtk_widget_show_all (window);
 114 
 115 
 116   gtk_main ();
 117 
 118   return 0;
 119 }

Tablas

Otro tipo de contenedor son las tablas, muy útiles en algunas ocasiones.

Usando tablas, se crea una rejilla en la que se pueden colocar widgets. Los widgets pueden ocupar los espacios que se especifiquen (1 o más celdas).

La función para crear tablas es gtk_table_new, y tiene la siguiente forma:

   1 GtkWidget * gtk_table_new (     rows,    
   2         columns,         
   3         homogeneous);    
   4 guint   rows;
   5 guint   columns;
   6 gboolean        homogeneous;

El primer argumento es el número de filas de la tabla, el segundo, obviamente, el número de columnas. El argumento homogeneous especifica las medidas de las cajas de la tabla. Si su valor es TRUE, las cajas de la tabla se ajustan al tamaño del widget más largo que esté en la tabla. Por contra, si vale FALSE, las cajas de la tabla se ajustan al tamaño del widget más alto de la fila y el más ancho de la columna.

Las filas y columnas empiezan de 0 a n, siendo “n” el número especificado en la llamada a gtk_table_new(). Si se especifica por ejemplo, 2 filas (rows = 2) y 2 columnas (columns = 2), la estructura quedaría como se ve en la siguiente imagen:

 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

Hay que tener en cuenta que el sistema de coordenadas comienza en la esquina superior izquierda (0,0).

Para colocar widgets dentro de la tabla, se usa la función gtk_table_attach, que tiene la siguiente forma:

   1 void gtk_table_attach ( table,   
   2         child,   
   3         left_attach,     
   4         right_attach,    
   5         top_attach,      
   6         bottom_attach,   
   7         xoptions,        
   8         yoptions,        
   9         xpadding,        
  10         ypadding);       
  11 GtkTable *      table;
  12 GtkWidget *     child;
  13 guint   left_attach;
  14 guint   right_attach;
  15 guint   top_attach;
  16 guint   bottom_attach;
  17 GtkAttachOptions        xoptions;
  18 GtkAttachOptions        yoptions;
  19 guint   xpadding;
  20 guint   ypadding;

El primer argumento es la tabla que se ha creado y el segundo el widget que se va a colocar en la tabla. Los argumentos left_attach y right_attach especifican dónde colocar el widget, y cuántas celdas usar. Si, por ejemplo, se quiere posicionar un botón en la parte inferior derecha de la tabla anterior de 2x2 y rellenar únicamente esa entrada, los valores de los argumentos serían los siguientes: left_attach = 1, right_attach = 2, top_attach = 1, bottom_attach = 2.

Ahora bien, si se quiere posicionar un widget que ocupe la fila superior entera de la tabla de 2x2, los valores de los argumentos serían los siguientes: left_attach = 0, right_attach = 2, top_attach = 0, bottom_attach = 1.

Los argumentos xoptions y yoptions especifican las opciones de colocación en forma de máscara de bits, donde los valores existentes para el tipo GtkAttachOptions pueden ser agrupados mediante el operador binario OR (|). Estas opciones son:

    • GTK_FILL: Si la caja de la tabla es más larga que el widget, y se ha especificado GTK_FILL, el widget se extenderá hasta ocupar todo el sitio que ocupa la caja.
    • GTK_SHRINK: Cuando se reduce el tamaño de la tabla, los widgets, normalmente, no cambian su tamaño junto con el de la tabla, de forma que las partes inferior y derecha de dichos widgets desaparezca a medida que se reduce el tamaño de la tabla. Usando GTK_SHRINK los widgets reducirán su tamaño junto con el de la tabla.
    • GTK_EXPAND: La tabla se extiende hasta ocupar todo el espacio de la ventana.

El argumento padding funciona de la misma forma que con cajas, es decir, especifica el espacio, en pixels, alrededor del widget.

Al tener tantas opciones la función gtk_table_attach, GTK ofrece una función extra que permite añadir widgets a la tabla usando los valores por defecto de colocación. Dicha función es gtk_table_attach_defaults, cuya sintaxis es la siguiente:

   1 void gtk_table_attach_defaults (        table,   
   2         child,   
   3         left_attach,     
   4         right_attach,    
   5         top_attach,      
   6         bottom_attach);  
   7 GtkTable *      table;
   8 GtkWidget *     child;
   9 guint   left_attach;
  10 guint   right_attach;
  11 guint   top_attach;
  12 guint   bottom_attach;

Como puede apreciarse, los parámetros para especificar las opciones de colocación y de espaciado alrededor del widget han sido omitidos en esta función con respecto a gtk_table_attach. Eso se debe a que gtk_table_attach_defaults toma unos valores por defecto para esos parámetros, que son GTK_FILL | GTK_EXPAND en el caso de xoptions y yoptions, y 0 en el caso de xpadding e ypadding. El resto de argumentos son idénticos a los comentados para la función gtk_table_attach.

Las funciones gtk_table_set_row_spacing() y gtk_table_set_col_spacing(), añaden espacio entre las filas y las columnas.

   1 void gtk_table_set_row_spacing( table,   
   2         row,     
   3         spacing);        
   4 GtkTable *      table;
   5 guint   row;
   6 guint   spacing;
   7 
   8 void gtk_table_set_col_spacing( table,   
   9         column,          
  10         spacing);        
  11 GtkTable *      table;
  12 guint   column;
  13 guint   spacing;

Hay que tener en cuenta que el espacio se sitúa a la derecha de la columna, y en las filas, se sitúa debajo de la fila.

También es posible establecer un espacio para las filas y columnas a la vez, con las siguientes funciones:

   1 void gtk_table_set_row_spacings(        table,   
   2         spacing);        
   3 GtkTable *      table;
   4 guint   spacing;
   5 
   6 void gtk_table_set_col_spacings(        table,   
   7         spacing);        
   8 GtkTable *      table;
   9 guint   spacing;

Con estas funciones, no se deja espacio en la última fila y la última columna.

Ejemplo de uso de tablas

A continuación se muestra el código para crear una ventana con tres botones en una tabla de 2x2. Los primeros dos botones se colocan en la fila superior de la tabla. El tercero, el botón "quit", se coloca en la fila inferior, ocupando las dos columnas.

Figura 4. Tabla con 3 botones

tablepacking.png

   1 #include <gtk/gtk.h>
   2 
   3 /* Los datos pasados a esta función se imprimen en la salida estándar (stdout) */
   4 void callback( GtkWidget *widget,
   5                gpointer   data )
   6 {
   7     g_print ("Hello again - %s was pressed\n", (char *) data);
   8 }
   9 
  10 /* Función para terminar el programa */
  11 gint delete_event( GtkWidget *widget,
  12                    GdkEvent  *event,
  13                    gpointer   data )
  14 {
  15     gtk_main_quit ();
  16     return FALSE;
  17 }
  18 
  19 int main( int   argc,
  20           char *argv[] )
  21 {
  22     GtkWidget *window;
  23     GtkWidget *button;
  24     GtkWidget *table;
  25 
  26     gtk_init (&argc, &argv);
  27 
  28     /* Crea una ventana */
  29     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  30 
  31     /* Establece el título de la ventana */
  32     gtk_window_set_title (GTK_WINDOW (window), "Table");
  33 
  34     /* Establece un manejador para "delete_event" que inmediatamente
  35      * cerrará GTK. */
  36     g_signal_connect (G_OBJECT (window), "delete_event",
  37                       G_CALLBACK (delete_event), NULL);
  38 
  39     /* Establece el tamaño del borde de la ventana. */
  40     gtk_container_set_border_width (GTK_CONTAINER (window), 20);
  41 
  42     /* Crea una tabla de 2x2 */
  43     table = gtk_table_new (2, 2, TRUE);
  44 
  45     /* Coloca la tabla en la ventana principal */
  46     gtk_container_add (GTK_CONTAINER (window), table);
  47 
  48     /* Crea el primer botón */
  49     button = gtk_button_new_with_label ("button 1");
  50 
  51     /* cuando se pulsa el botón, se llama a la función que se ha creado anteriormente
  52      * "callback", con un puntero a "button 1" como argumento */
  53     g_signal_connect (G_OBJECT (button), "clicked",
  54                       G_CALLBACK (callback), (gpointer) "button 1");
  55 
  56 
  57     /* Coloca el primer botón (button 1) en el cuadrante superior izquierdo de la tabla */
  58     gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 0, 1);
  59 
  60     gtk_widget_show (button);
  61 
  62     /* Crea el segundo botón */
  63 
  64     button = gtk_button_new_with_label ("button 2");
  65 
  66     /* cuando se pulsa el botón, se llama a la función que se ha creado anteriormente
  67      * "callback", con un puntero a "button 2" como argumento */
  68     g_signal_connect (G_OBJECT (button), "clicked",
  69                       G_CALLBACK (callback), (gpointer) "button 2");
  70     /* Coloca el segundo botón (button 2) en el cuadrante superior derecho de la tabla. */
  71     gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 0, 1);
  72 
  73     gtk_widget_show (button);
  74 
  75     /* Crea el botón de "Quit" */
  76     button = gtk_button_new_with_label ("Quit");
  77 
  78     /* Cuando se pulsa el botón, se hace una llamada a función "delete_event" y el
  79      * programa termina. */
  80     g_signal_connect (G_OBJECT (button), "clicked",
  81                       G_CALLBACK (delete_event), NULL);
  82 
  83     /* Coloca el botón "quit" ocupando los dos cuadrantes inferiores de la tabla.*/
  84     gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 2, 1, 2);
  85 
  86     gtk_widget_show (button);
  87 
  88     gtk_widget_show (table);
  89     gtk_widget_show (window);
  90 
  91     gtk_main ();
  92 
  93     return 0;
  94 }

GtkNotebook

El widget GtkNotebook es una colección de "páginas" que se solapan entre ellas. Cada página, sólo visible una cada vez, contiene una información (widgets) determinada. Este widget ha sido cada vez más común en la programación de interfaces gráficos, ya que es una buena forma de mostrar bloques de similar información uno a uno.

La función que se necesita para la creación de un widget notebook es la siguiente:

   1 GtkWidget * gtk_notebook_new();
   2 void;

GTK también tiene varias funciones para establecer el formato del widget notebook. La primera de estas funciones sirve para posicionar los indicadores de página o pestañas, los cuales pueden ser colocados arriba, abajo, a derecha o izquierda.

   1 void gtk_notebook_set_tab_pos(  notebook,        
   2         pos);    
   3 GtkNotebook *   notebook;
   4 GtkPositionType         pos;

Los diferentes tipos de la variable de tipo GtkPositionType son los siguientes:

    • GTK_POS_LEFT (posición a la izquierda)
    • GTK_POS_RIGHT (posición a la derecha)
    • GTK_POS_TOP (posición arriba)
    • GTK_POS_BOTTOM (posición abajo)
    • GTK_POS_TOP es la opción por defecto.

El siguiente paso sería añadir páginas al notebook. Existes tres formas de añadir páginas. Las dos primeras que se muestran a continuación, y son muy similares.

   1 void gtk_notebook_append_page(  notebook,        
   2         child,   
   3         tab_label);      
   4 GtkNotebook *   notebook;
   5 GtkWidget *     child;
   6 GtkWidget *     tab_label;
   7 
   8 void gtk_notebook_prepend_page( notebook,        
   9         child,   
  10         tab_label);      
  11 GtkNotebook *   notebook;
  12 GtkWidget *     child;
  13 GtkWidget *     tab_label;

La primera función (gtk_notebook_append_page), añade las páginas al final del notebook y la segunda (gtk_botebook_prepend_page), las añade al principio. El argumento child es el widget que será colocado dentro de la página del notebook, y tab_label es la etiqueta de la página que será añadida. El widget child deberá ser creado por separado y normalmente suele ser un contenedor que contiene otros widgets, aunque, por supuesto, puede ser cualquier widget (cualquier GtkWidget).

La última función para añadir páginas al notebook contiene todas las propiedades de las dos anteriores, pero además permite especificar en qué posición se quiere colocar la pagína dentro del notebook. Es decir, permite insertar una página antes de otra ya existente en el notebook.

   1 void gtk_notebook_insert_page(  notebook,        
   2         child,   
   3         tab_label,       
   4         position);       
   5 GtkNotebook *   notebook;
   6 GtkWidget *     child;
   7 GtkWidget *     tab_label;
   8 gint    position;

El parámetro extra (position) se usa para especificar la posición en la que se desea insertar la nueva página. Hay que tener en cuenta que 0 representa la primera página.

Para borrar una página del notebook, se utiliza la siguiente función:

   1 void gtk_notebook_remove_page(  notebook,        
   2         page_num);       
   3 GtkNotebook *   notebook;
   4 gint    page_num;

Esta función borra del notebook, la página que se haya especificado en el parámetro page_num.

Para saber en que página se encuentra el notebook se utiliza la siguiente función:

   1 gint gtk_notebook_get_current_page(     notebook);       
   2 GtkNotebook *   notebook;

Esta función devuelve la posición de la página actual dentro del notebook, siendo 0 la primera página.

Las suguientes funciones sirven para mover las hojas del notebook hacia adelante o hacia atrás. Si el notebook está en la última página y se llama a la función gtk_notebook_next_page éste volverá a la primera página. Igualmente si se llama a la función gtk_notebook_prev_page y notebook está en la primera página, éste volverá a la última página.

   1 void gtk_notebook_next_page(    notebook);       
   2 GtkNoteBook *   notebook;
   3 
   4 void gtk_notebook_prev_page(    notebook);       
   5 GtkNoteBook *   notebook;

La siguiente función sirve para activar una página determinada del notebook. Si por ejemplo se necesita activar la página 5 del notebook, se usaría esta función. Si no se usa esta función, el notebook se sitúa siempre en la primera página al ser creada ésta y las siguientes que se vayan añadiendo.

   1 void gtk_notebook_set_current_page(     notebook,        
   2         page_num);       
   3 GtkNotebook *   notebook;
   4 gint    page_num;

Las siguientes funciones sirven para mostrar u ocultar los indicadores o pestañas y los bordes del notebook respectivamente.

   1 void gtk_notebook_set_show_tabs(        notebook,        
   2         show_tabs);      
   3 GtkNotebook *   notebook;
   4 gboolean        show_tabs;
   5 
   6 void gtk_notebook_set_show_border(      notebook,        
   7         show_border);    
   8 GtkNotebook *   notebook;
   9 gboolean        show_border;

Ambas funciones reciben un valor de tipo boolean que especifica si se debe (TRUE) o no (FALSE) mostrar los indicadores o los bordes.

La siguiente función es muy útil cuando se tiene un gran número de páginas y las pestañas no caben en la página. La función crea dos botones con flechas, para así permitir al usuario el ir recorriendo las múltiples pestañas.

   1 void gtk_notebook_set_scrollable(       notebook,        
   2         scrollable);     
   3 GtkNotebook *   notebook;
   4 gboolean        scrollable;

El siguiente ejemplo se ha sacado del fichero testgtk.c que viene junto con la distribución de GTK. Este pequeño programa, crea una ventana con un notebook que contiene seis botones. El notebook contiene 11 páginas, que son añadidas de tres maneras diferentes (append, insert y prepend). Los botones permiten cambiar la posición de la pestaña, borrar o añadir los bordes y pestañas del notebook y cambiar a la anterior o siguiente página.

Figura 5. GtkNotebook en acción

notebook.png

   1 #include <stdio.h>
   2 #include <gtk/gtk.h>
   3 
   4 /* Esta función va cambiando la posición de las pestañas
   5 This function rotates the position of the tabs */
   6 void rotate_book( GtkButton   *button,
   7                   GtkNotebook *notebook )
   8 {
   9     gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos + 1) % 4);
  10 }
  11 
  12 /* Añade o borra las pestañas y los bordes del notebook */
  13 void tabsborder_book( GtkButton   *button,
  14                       GtkNotebook *notebook )
  15 {
  16     gint tval = FALSE;
  17     gint bval = FALSE;
  18     if (notebook->show_tabs == 0)
  19             tval = TRUE;
  20     if (notebook->show_border == 0)
  21             bval = TRUE;
  22 
  23     gtk_notebook_set_show_tabs (notebook, tval);
  24     gtk_notebook_set_show_border (notebook, bval);
  25 }
  26 
  27 /* Borra una página del notebook */
  28 void remove_book( GtkButton   *button,
  29                   GtkNotebook *notebook )
  30 {
  31     gint page;
  32 
  33     page = gtk_notebook_get_current_page (notebook);
  34     gtk_notebook_remove_page (notebook, page);
  35     /* Con la siguiente función se vuelve a dibujar el notebook
  36      * para que no se vea la página que ha sido borrada. */
  37     gtk_widget_queue_draw (GTK_WIDGET (notebook));
  38 }
  39 
  40 gint delete( GtkWidget *widget,
  41              GtkWidget *event,
  42              gpointer   data )
  43 {
  44     gtk_main_quit ();
  45     return FALSE;
  46 }
  47 
  48 int main( int argc,
  49           char *argv[] )
  50 {
  51     GtkWidget *window;
  52     GtkWidget *button;
  53     GtkWidget *table;
  54     GtkWidget *notebook;
  55     GtkWidget *frame;
  56     GtkWidget *label;
  57     GtkWidget *checkbutton;
  58     int i;
  59     char bufferf[32];
  60     char bufferl[32];
  61 
  62     gtk_init (&argc, &argv);
  63 
  64     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  65 
  66     g_signal_connect (G_OBJECT (window), "delete_event",
  67                       G_CALLBACK (delete), NULL);
  68 
  69     gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  70 
  71     table = gtk_table_new (3, 6, FALSE);
  72     gtk_container_add (GTK_CONTAINER (window), table);
  73 
  74     /* Crea un notebook, y especifica la posición de las pestañas */
  75     notebook = gtk_notebook_new ();
  76     gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  77     gtk_table_attach_defaults (GTK_TABLE (table), notebook, 0, 6, 0, 1);
  78     gtk_widget_show (notebook);
  79 
  80     /* El siguiente bucle, añade 5 páginas al notebook (append)*/
  81     for (i = 0; i < 5; i++) {
  82         sprintf(bufferf, "Append Frame %d", i + 1);
  83         sprintf(bufferl, "Page %d", i + 1);
  84 
  85         frame = gtk_frame_new (bufferf);
  86         gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
  87         gtk_widget_set_size_request (frame, 100, 75);
  88         gtk_widget_show (frame);
  89 
  90         label = gtk_label_new (bufferf);
  91         gtk_container_add (GTK_CONTAINER (frame), label);
  92         gtk_widget_show (label);
  93 
  94         label = gtk_label_new (bufferl);
  95         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
  96     }
  97 
  98     /* Se añade un página para Now let's add a page to a specific spot */
  99     checkbutton = gtk_check_button_new_with_label ("Check me please!");
 100     gtk_widget_set_size_request (checkbutton, 100, 75);
 101     gtk_widget_show (checkbutton);
 102 
 103     label = gtk_label_new ("Add page");
 104     gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
 105 
 106     /* El siguiente bucle, añade 5 páginas al notebook (prepend)*/
 107     for (i = 0; i < 5; i++) {
 108         sprintf (bufferf, "Prepend Frame %d", i + 1);
 109         sprintf (bufferl, "PPage %d", i + 1);
 110 
 111         frame = gtk_frame_new (bufferf);
 112         gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
 113         gtk_widget_set_size_request (frame, 100, 75);
 114         gtk_widget_show (frame);
 115 
 116         label = gtk_label_new (bufferf);
 117         gtk_container_add (GTK_CONTAINER (frame), label);
 118         gtk_widget_show (label);
 119 
 120         label = gtk_label_new (bufferl);
 121         gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), frame, label);
 122     }
 123 
 124     /* Con esta función, se mostrará una página específica "page 4" */
 125     gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 3);
 126 
 127     /* Crea los botones */
 128     button = gtk_button_new_with_label ("close");
 129     g_signal_connect_swapped (G_OBJECT (button), "clicked",
 130                               G_CALLBACK (delete), NULL);
 131     gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 1, 2);
 132     gtk_widget_show (button);
 133 
 134     button = gtk_button_new_with_label ("next page");
 135     g_signal_connect_swapped (G_OBJECT (button), "clicked",
 136                               G_CALLBACK (gtk_notebook_next_page),
 137                               G_OBJECT (notebook));
 138     gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 1, 2);
 139     gtk_widget_show (button);
 140 
 141     button = gtk_button_new_with_label ("prev page");
 142     g_signal_connect_swapped (G_OBJECT (button), "clicked",
 143                               G_CALLBACK (gtk_notebook_prev_page),
 144                               G_OBJECT (notebook));
 145     gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
 146     gtk_widget_show (button);
 147 
 148     button = gtk_button_new_with_label ("tab position");
 149     g_signal_connect (G_OBJECT (button), "clicked",
 150                       G_CALLBACK (rotate_book),
 151                       (gpointer) notebook);
 152     gtk_table_attach_defaults (GTK_TABLE (table), button, 3, 4, 1, 2);
 153     gtk_widget_show (button);
 154 
 155     button = gtk_button_new_with_label ("tabs/border on/off");
 156     g_signal_connect (G_OBJECT (button), "clicked",
 157                       G_CALLBACK (tabsborder_book),
 158                       (gpointer) notebook);
 159     gtk_table_attach_defaults (GTK_TABLE (table), button, 4, 5, 1, 2);
 160     gtk_widget_show (button);
 161 
 162     button = gtk_button_new_with_label ("remove page");
 163     g_signal_connect (G_OBJECT (button), "clicked",
 164                       G_CALLBACK (remove_book),
 165                       (gpointer) notebook);
 166     gtk_table_attach_defaults (GTK_TABLE (table), button, 5, 6, 1, 2);
 167     gtk_widget_show (button);
 168 
 169     gtk_widget_show (table);
 170     gtk_widget_show (window);
 171 
 172     gtk_main ();
 173 
 174     return 0;
 175 }

GtkAlignment

El widget GtkAlignment permite colocar widgets en una posición y tamaño relativos al tamaño de sí mismo. Esto puede ser muy útil, por ejemplo, a la hora de centrar un widget en una ventana.

Sólo hay dos funciones asociadas a este widget, que son:

   1 GtkWidget * gtk_alignment_new(  xalign,          
   2         yalign,          
   3         xscale,          
   4         yscale);         
   5 gfloat          xalign;
   6 gfloat          yalign;
   7 gfloat          xscale;
   8 gfloat          yscale;
   9 
  10 void gtk_alignment_set( alignment,       
  11         xalign,          
  12         yalign,          
  13         xscale,          
  14         yscale);         
  15 GtkAlignment *          alignment;
  16 gfloat          xalign;
  17 gfloat          yalign;
  18 gfloat          xscale;
  19 gfloat          yscale;

La primera función crea un nuevo widget GtkAlignment con los parámetros especificados. La segunda función permite cambiar los parámetros de un widget GtkAlignment ya existente.

Los cuatro parámetros de estas funciones son de tipo gfloat con un valor que puede ir de 0.0 a 1.0. Los argumentos xalign e yalign determinan la posición del widget dentro del GtkAlignment. Los argumentos xscale e yscale determinan la cantidad de espacio asignado al widget.

También se puede añadir un widget al GtkAlignment usando la siguiente función (perteneciente a la clase GtkContainer, de la que deriva GtkAlignment):

   1           gtk_container_add (GTK_CONTAINER (alignment), child_widget);

Se puede ver un ejemplo del uso del widget GtkAlignment en el programa de ejemplo del uso del widget de barra de progreso (GtkProgressBar).

GtkHPaned/GtkVPaned

Los widgets "paned" se utilizan cuando se quiere dividir un área en dos partes, con el tamaño de las dos partes controlado por el usuario. Un separador, con un manejador que el usuario puede arrastrar y cambiar el ratio, separa las dos partes. La división puede ser horizontal (GtkHPaned) o vertical (GtkVPaned).

Para crear una ventana "paned", horizontal o vertical, se necesita una de las siguientes funciones:

   1           GtkWidget *gtk_hpaned_new (void);
   2 
   3           GtkWidget *gtk_vpaned_new (void);

Después de crear una ventana "paned", se necesita añadir widgets a las dos mitades. Para ello GTK cuenta con las siguientes funciones:

   1           void gtk_paned_add1 (GtkPaned *paned, GtkWidget *child);
   2 
   3           void gtk_paned_add2 (GtkPaned *paned, GtkWidget *child);

La función gtk_paned_add1 añade el widget hijo a la izquierda o arriba de una de las partes de la ventana "paned", mientras que la función gtk_paned_add2 añade el widget hijo a la derecha o abajo de una de las partes de la ventana "paned".

El siguiente ejemplo crea parte de un interfaz de usuario de un programa de correo imaginario. La ventana se divide en dos partes verticales. La parte de arriba muestra una lista de los mensajes recibidos y la parte de abajo el texto de los mensajes. Hay que tener dos cosas en cuenta: no se puede añadir texto a un widget de texto hasta no indicarlo explícitamente a GTK ("realize"). Ésto puede hacerse llamando a la gtk_widget_realize(), pero como demostración de una técnica alternativa, conectaremos un manejador a la señal "realize" para añadir texto al widget. Además, necesitaremos añadir la opción GTK_SHRINK a algunos elementos de la tabla que contiene la ventana de texto y sus barras de scroll, de tal forma que cuando la parte inferior se haga pequeña, se encoja también la porción adecuada en lugar de salir por debajo de la ventana.

Figura 6. Ejemplo de Paned Window

panedwindow.png

GtkLayout

El contenedor GtkLayout es similar al contenedor GtkFixed, salvo que el primero implementa un área de scroll muy amplia (hasta 2^32). El sistema X window tiene una limitación ya que las ventanas sólo pueden ser de 32767 pixels tanto de ancho como de largo. El contenedor GtkLayout salva esta limitación pudiendo tener un amplio área de scroll, incluso cuando se tienen muchos widgets hijos dentro de este área.

Para crear un contenedor Layout se utiliza la siguiente función:

   1           GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
   2                                      GtkAdjustment *vadjustment );

Como puede verse, se puede, al crear un GtkLayout, especificar los GtkAdjustment, tanto horizontal como vertical, que serán usados para controlar el desplazamiento del contenido del contenedor. Dichos parámetros son opcionales, por lo que en la mayor parte de los casos se usará esta función especificando NULL para ambos.

Las siguientes funciones sirven para añadir y mover widgets en el contenedor GtkLayout:

   1           void gtk_layout_put( GtkLayout *layout,
   2                                GtkWidget *widget,
   3                                gint       x,
   4                                gint       y );
   5 
   6           void gtk_layout_move( GtkLayout *layout,
   7                                 GtkWidget *widget,
   8                                 gint       x,
   9                                 gint       y );

Para establecer el tamaño de un GtkLayout se usa la siguiente función:

   1           void gtk_layout_set_size( GtkLayout *layout,
   2                                     guint      width,
   3                                     guint      height );

Y para terminar con el GtkLayout, las cuatro funciones siguientes se usan para manipular el alineamiento horizontal y vertical de los widgets.

   1           GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );
   2 
   3           GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );
   4 
   5           void gtk_layout_set_hadjustment( GtkLayout     *layout,
   6                                            GtkAdjustment *adjustment );
   7 
   8           void gtk_layout_set_vadjustment( GtkLayout     *layout,
   9                                            GtkAdjustment *adjustment);

Colocación por coordenadas

Una de las cosas que hacen especial a GTK+ (al igual que a Java Swing y otros toolkits), es que la colocación de los widgets en las ventanas se hace por medio de contenedores, como se comenta en esta sección, que controlan la forma en que son colocados los distintos widgets controlados por el contenedor.

Sin embargo, hay ocasiones en las que puede ser necesario el usar una colocación de los distintos widgets en coordenadas exactas. Para dicho menester, GTK incluye el widget GtkFixed, que permite colocar los widgets en una posición fija dentro de la ventana, relativa a la esquina superior izquierda de la misma. La posición de los widgets se puede cambiar dinámicamente.

Las funciones asociadas a este widget son las siguientes:

   1           GtkWidget* gtk_fixed_new( void );
   2 
   3           void gtk_fixed_put( GtkFixed  *fixed,
   4                               GtkWidget *widget,
   5                               gint       x,
   6                               gint       y );
   7 
   8           void gtk_fixed_move( GtkFixed  *fixed,
   9                                GtkWidget *widget,
  10                                gint       x,
  11                                gint       y );

La primera función, gtk_fixed_new permite crear un nuevo widget GtkFixed

La función gtk_fixed_put coloca el widget en el contenedor GtkFixed en la posición especificada por los parámetros x e y.

gtk_fixed_move permite mover el widget especificado en el parámetro widget a una nueva posición.

   1         void gtk_fixed_set_has_window( GtkFixed  *fixed,
   2                                        gboolean   has_window );
   3 
   4         gboolean gtk_fixed_get_has_window( GtkFixed *fixed );

En las últimas versiones de GTK, los widgets GtkFixed no tenían su propia ventana X, pero ahora la función gtk_fixed_set_has_window permite crearlos con su propia ventana. No obstante, esta función tendrá que ser llamada antes de que se visualice el widget.

El siguiente ejemplo muestra cómo usar el contenedor GtkFixed.

Figura 7. Ejemplo de GtkFixed

fixed.png

   1 #include <gtk/gtk.h>
   2 
   3 /* Para agilizar el trabajo, se usarán algunas variables globales para
   4  * guardar la posición del widget dentro del contenedor fixed */
   5 gint x = 50;
   6 gint y = 50;
   7 
   8 /* Esta función mueve el botón a una nueva posición
   9  * del contenedor Fixed. */
  10 void move_button( GtkWidget *widget,
  11                   GtkWidget *fixed )
  12 {
  13   x = (x + 30) % 300;
  14   y = (y + 50) % 300;
  15   gtk_fixed_move (GTK_FIXED (fixed), widget, x, y);
  16 }
  17 
  18 int main( int   argc,
  19           char *argv[] )
  20 {
  21   /* GtkWidget es el tipo utilizado para widgets */
  22   GtkWidget *window;
  23   GtkWidget *fixed;
  24   GtkWidget *button;
  25   gint i;
  26 
  27   /* Inicializa GTK */
  28   gtk_init (&argc, &argv);
  29 
  30   /* Crea una ventana */
  31   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  32   gtk_window_set_title (GTK_WINDOW (window), "Fixed Container");
  33 
  34   /* Conecta el evento "destroy" a un manejador de señales */
  35   g_signal_connect (G_OBJECT (window), "destroy",
  36                     G_CALLBACK (gtk_main_quit), NULL);
  37 
  38   /* Establece la anchura del borde de la ventana. */
  39   gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  40 
  41   /* Crea un Contenedor Fixed */
  42   fixed = gtk_fixed_new ();
  43   gtk_container_add (GTK_CONTAINER (window), fixed);
  44   gtk_widget_show (fixed);
  45 
  46   for (i = 1 ; i <= 3 ; i++) {
  47     /* Crea un botón con la etiqueta "Press me" */
  48     button = gtk_button_new_with_label ("Press me");
  49 
  50     /* Cuando el botón recive la señal "clicked", se llama a la función
  51      * move_button() pasando el Contenedor Fixed como argumento */
  52     g_signal_connect (G_OBJECT (button), "clicked",
  53                       G_CALLBACK (move_button), (gpointer) fixed);
  54 
  55     /* Coloca el botón dentro de la ventana de los contenedores fixed. */
  56     gtk_fixed_put (GTK_FIXED (fixed), button, i*50, i*50);
  57 
  58     /* El último paso es mostrar el nuevo widget que se ha creado. */
  59     gtk_widget_show (button);
  60   }
  61 
  62   /* Muestra la ventana */
  63   gtk_widget_show (window);
  64 
  65   /* Entra en el bucle de eventos */
  66   gtk_main ();
  67 
  68   return 0;
  69 }

Marcos

GtkAspectFrame

GtkAspectFrame es igual que el widget GtkFrame, salvo que aquél además especifica el ratio entre la anchura y la altura del widget hijo, para así poder tener un valor determinado y añadir espacio extra si fuera necesario. Esto es muy útil cuando se quiere visualizar una imagen y ampliarla o reducirla segun cambie el tamaño de la ventana contenedora. El tamaño de la imagen debe cambiar cuando el usuario maximice o minimice la ventana, pero el ratio siempre deberá coincidir con la imagen original.

La siguiente función crea un GtkAspectFrame nuevo.

   1 GtkWidget * gtk_aspect_frame_new(       label,   
   2         xalign,          
   3         yalign,          
   4         ratio,   
   5         obey_child);     
   6 const gchar *   label;
   7 gfloat          xalign;
   8 gfloat          yalign;
   9 gfloat          ratio;
  10 gboolean        obey_child;

Los argumentos xalign e yalign establecen la posición al igual que se comentó con el widget definido en “GtkAlignment”. Si el valor del argumento obey_child es TRUE, el ratio del widget hijo coincidirá con el ratio del tamaño necesario. En caso contrario, el valor del ratio vendrá dado por el valor del argumento del mismo nombre.

La siguiente función cambia las opciones de un GtkAspectFrame

   1 void gtk_aspect_frame_set(      aspect_frame,    
   2         xalign,          
   3         yalign,          
   4         ratio,   
   5         obey_child);     
   6 GtkAspectFrame *        aspect_frame;
   7 gfloat          xalign;
   8 gfloat          yalign;
   9 gfloat          ratio;
  10 gboolean        obey_child;

El siguiente programa usa un GtkAspectFrame para presentar un área de dibujo cuyo ratio será siempre 2:1, independientemente que el usuario cambie el tamaño de la ventana principal.

Figura 8. GtkAspectFrame: al redimensionar la ventana, el ratio se mantiene

aspectframe.png

   1 #include <gtk/gtk.h>
   2 
   3 int main( int argc,
   4           char *argv[] )
   5 {
   6     GtkWidget *window;
   7     GtkWidget *aspect_frame;
   8     GtkWidget *drawing_area;
   9     gtk_init (&argc, &argv);
  10 
  11     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  12     gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
  13     g_signal_connect (G_OBJECT (window), "destroy",
  14                       G_CALLBACK (gtk_main_quit), NULL);
  15     gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  16 
  17     /* Crea un widget aspect_frame y lo añade a la ventana principal */
  18 
  19     aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
  20                                          0.5, /* centra x */
  21                                          0.5, /* centra y */
  22                                          2, /* el ratio: xsize/ysize = 2 */
  23                                          FALSE /* este argumento se ignora */);
  24 
  25     gtk_container_add (GTK_CONTAINER (window), aspect_frame);
  26     gtk_widget_show (aspect_frame);
  27 
  28     /* Se añade un widget child al widget aspect frame */
  29 
  30     drawing_area = gtk_drawing_area_new ();
  31 
  32     /* el tamaño de la ventana se establece a 200x200, aunque el widget AspectFrame
  33      * establece el tamaño a 200x100 ya que el ratio es de 2x1 */
  34     gtk_widget_set_size_request (drawing_area, 200, 200);
  35     gtk_container_add (GTK_CONTAINER (aspect_frame), drawing_area);
  36     gtk_widget_show (drawing_area);
  37 
  38     gtk_widget_show (window);
  39     gtk_main ();
  40     return 0;
  41 }

GtkViewport

El widget GtkViewport se suele usar muy poco directamente. Normalmente se usan las ventanas con scroll, las cuales usan el widget Viewport.

GtkViewport permite la colocación de un widget mayor dentro de él, de tal forma que es posible visualizar todo el widget por partes. Se usa la alineación para definir el área que se va a ver.

La siguiente función crea un widget Viewport:

   1           GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
   2                                        GtkAdjustment *vadjustment );

Como se puede ver, se puede especificar la alineación horizontal o vertical que va a usar el widget. Si el valor de los argumentos es NULL, creará la alineación internamente.

Con las siguientes funciones se puede obtener y cambiar la alineación de un widget que ya ha sido creado anteriormente.

   1           GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );
   2 
   3           GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );
   4 
   5           void gtk_viewport_set_hadjustment( GtkViewport   *viewport,
   6                                              GtkAdjustment *adjustment );
   7 
   8           void gtk_viewport_set_vadjustment( GtkViewport   *viewport,
   9                                              GtkAdjustment *adjustment );

Por último, se usará la siguiente función para cambiar la apariencia del widget viewport.

   1           void gtk_viewport_set_shadow_type( GtkViewport   *viewport,
   2                                              GtkShadowType  type );

Los posibles valores para el parámetro type son:

  • GTK_SHADOW_NONE
  • GTK_SHADOW_IN
  • GTK_SHADOW_OUT
  • GTK_SHADOW_ETCHED_IN
  • GTK_SHADOW_ETCHED_OUT


Documentacion/Desarrollo/Gtk/Contenedores (última edición 2008-12-04 08:49:09 efectuada por anónimo)