Contenedores

De Wiki GNOME Chile
Saltar a: navegación, buscar

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.


Contenido

GtkHBox y GtkVBox

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:


void gtk_box_pack_start (box, 
 	child, 	 
 	expand, 	 
 	fill, 	 
 	padding);
 
GtkBox    *box;
GtkWidget *child;
gboolean  expand;
gboolean  fill;
guint  	  padding;


void gtk_box_pack_end (box, 
 	child, 	 
 	expand, 	 
 	fill, 	 
 	padding);
 
GtkBox    *box;
GtkWidget *child;
gboolean  expand;
gboolean  fill;
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:


GtkWidget * gtk_hbox_new (homogenous, 	 
 	spacing);

gboolean  homogenous;
gint  	  spacing;


GtkWidget * gtk_vbox_new (homogenous, 	 
 	spacing);

gboolean homogenous;
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.


#include <stdio.h>
#include <stdlib.h>
#include "gtk/gtk.h"

gint delete_event( GtkWidget *widget,
                   GdkEvent  *event,
                   gpointer   data )
{
        gtk_main_quit ();
        return FALSE;
}

/* Crea una hbox con botones con etiquetas.
 * Se muestran todos los widgets (gtk_widget_show()) a excepción de la caja. */
GtkWidget *make_box( gboolean homogeneous,
                     gint     spacing,
                     gboolean expand,
                     gboolean fill,
                     guint    padding )
{
        GtkWidget *box;
        GtkWidget *button;
        char padstr[80];

        /* Crea una hbox con los parámetros establecidos
         * para homogeneous y spacing */
        box = gtk_hbox_new (homogeneous, spacing);

        /* Crea una serie de botones con etiqueta */
        button = gtk_button_new_with_label ("gtk_box_pack");
        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        button = gtk_button_new_with_label ("(box,");
        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        button = gtk_button_new_with_label ("button,");
        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        /* Crea un botón con etiqueta dependiendo del valor del parámetro
         * expand. */
        if (expand == TRUE)
                button = gtk_button_new_with_label ("TRUE,");
        else
                button = gtk_button_new_with_label ("FALSE,");

        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        /* Lo mismo con el botón de "expand"
         * pero esta vez de la forma abreviada. */
        button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        sprintf (padstr, "%d);", padding);

        button = gtk_button_new_with_label (padstr);
        gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
        gtk_widget_show (button);

        return box;
}

int main( int   argc,
          char *argv[])
{
        GtkWidget *window;
        GtkWidget *button;
        GtkWidget *box1;
        GtkWidget *box2;
        GtkWidget *separator;
        GtkWidget *label;
        GtkWidget *quitbox;
        int which;


        gtk_init (&argc, &argv);

        if (argc != 2) {
                fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
                /* Esto finaliza GTK y sale con un estatus de 1. */
                exit (1);
        }

        which = atoi (argv[1]);

        /* Crea una ventana */
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

        /* Simpre se debe conectar la señal delete_event a la ventana
         * principal, para así poder cerrarla.  */
        g_signal_connect (G_OBJECT (window), "delete_event",
                          G_CALLBACK (delete_event), NULL);
        gtk_container_set_border_width (GTK_CONTAINER (window), 10);

        /* Crea una caja vertical (vbox) para colocar las cajas horizontales dentro.
         * Esto permite colocar las cajas horizontales llenas de botones una
         * encima de la otra dentro de la caja vertical (vbox). */
        box1 = gtk_vbox_new (FALSE, 0);

        /* Muestra una de las Figuras de arriba. */
        switch (which) {
        case 1:
                /* crea una etiqueta. */
                label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");

                /* Alínea la etiqueta a la izquierda.  Se hablará más adelante de esta
                 * función. */
                gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

                /* Coloca la etiqueta dentro de la caja vertical (vbox box1).  Los
                 * widgets que se añaden a una vbox se colocan, por orden, uno
                 * encima del otro. */
                gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

                /* Muestra la etiqueta */
                gtk_widget_show (label);

                /* Llamada a la función make box, para crear un hbox con
                 * una serie de botones - homogeneous = FALSE, spacing = 0,
                 * expand = FALSE, fill = FALSE, padding = 0 */
                box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Llamada a la función make box, para crear otra hbox con
                 * una serie de botones - homogeneous = FALSE, spacing = 0,
                 * expand = TRUE, fill = FALSE, padding = 0 */
                box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Crea un separador, se verán más adelante,
                 * aunque son bastante sencillos. */
                separator = gtk_hseparator_new ();

                /* Coloca el separador en una vbox. Todos estos
                 * widgets han sido colocados en una vbox, así que estarán
                 * ordenados verticalmente. */
                gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
                gtk_widget_show (separator);

                /* Crea otra etiqueta y la muestra. */
                label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
                gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
                gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
                gtk_widget_show (label);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Otro separador. */
                separator = gtk_hseparator_new ();
                /* Los tres últimos argumentos de gtk_box_pack_start son:
                 * expand, fill, padding. */
                gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
                gtk_widget_show (separator);

                break;

        case 2:

                /* Crea una etiqueta, box1 es una
                 * vbox que ya ha sido creada    */
                label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
                gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
                gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
                gtk_widget_show (label);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                separator = gtk_hseparator_new ();
                /* Los tres últimos argumentos de gtk_box_pack_start son:
                 * expand, fill, padding. */
                gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
                gtk_widget_show (separator);

                label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
                gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
                gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
                gtk_widget_show (label);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Los argumentos son: homogeneous, spacing, expand, fill, padding */
                box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                separator = gtk_hseparator_new ();
                /* Los tres últimos argumentos de gtk_box_pack_start son: expand, fill, padding. */
                gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
                gtk_widget_show (separator);
                break;

        case 3:

                /* Esto demuestra la capacidad de gtk_box_pack_end() para
                 * alinear a la derecha los widgets. Primero, se crea una caja. */
                box2 = make_box (FALSE, 0, FALSE, FALSE, 0);

                /* Crea una etiqueta colocada al final. */
                label = gtk_label_new ("end");
                /* Coloca la etiqueta usando gtk_box_pack_end(), así que se situa a la
                 * derecha de la hbox creada con la llamada a make_box(). */
                gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
                /* Show the label. */
                gtk_widget_show (label);

                /* Coloca box2 dentro de box1 (vbox) */
                gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
                gtk_widget_show (box2);

                /* Crea un separador. */
                separator = gtk_hseparator_new ();
                /* Establece las medidas del separador, una anchura de 400 pixels por 5 pixels
                 * de altura. La hbox que se creó anteriormente también tendrá 400 pixels de
                 * anchura,y la etiqueta "end" se separa de las otras etiquetas en la
                 * hbox. */
                gtk_widget_set_size_request (separator, 400, 5);
                /* coloca el separador en la vbox (box1) */
                gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
                gtk_widget_show (separator);
        }

        /* Crea otra hbox.. (se pueden crear tantas cajas como se necesiten) */
        quitbox = gtk_hbox_new (FALSE, 0);

        /* Crea el botón para salir del programa. */
        button = gtk_button_new_with_label ("Quit");

        /* Establece la señal para terminar el programa cundo se pulsa el botón ("quit") */
        g_signal_connect_swapped (G_OBJECT (button), "clicked",
                                  G_CALLBACK (gtk_main_quit),
                                  G_OBJECT (window));
        /* Coloca el botón en la hbox (quitbox).
         * Los 3 últimos argumentos de gtk_box_pack_start son:
         * expand, fill, padding. */
        gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
        /* Coloca la hbox (quitbox) en la vbox (box1) */
        gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

        /* Coloca la vbox (box1), la cual contiene ahora todos los widgets, en la
         * ventana principal. */
        gtk_container_add (GTK_CONTAINER (window), box1);

        /* Se muestra todo lo restante */
        gtk_widget_show (button);
        gtk_widget_show (quitbox);

        gtk_widget_show (box1);
        /* Se muestra la ventana en último lugar, así todo se muestra a la vez. */
        gtk_widget_show (window);

        /* La función principal. */
        gtk_main ();

        /* El control vuelve aquí cuando se llama a gtk_main_quit(), pero no cuando
         * se usa exit(). */

        return 0;
}


GtkHButtonBox y GtkVButtonBox

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.


	  GtkWidget *gtk_hbutton_box_new( void );

	  GtkWidget *gtk_vbutton_box_new( void );


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


	  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


Ejemplo de uso de cajas de botones


#include <gtk/gtk.h>

/* Crea una Caja de Botones con los parámetros específicos */
GtkWidget *create_bbox( gint  horizontal,
                        char *title,
                        gint  espacio,
                        gint  child_w,
                        gint  child_h,
                        gint  layout )
{
  GtkWidget *frame;
  GtkWidget *bbox;
  GtkWidget *button;

  frame = gtk_frame_new (title);

  if (horizontal)
    bbox = gtk_hbutton_box_new ();
  else
    bbox = gtk_vbutton_box_new ();

  gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), bbox);

  /* Establece la apariencia de la Caja de Botones */
  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
  gtk_box_set_spacing (GTK_BOX (bbox), espacio);
  /*gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);*/

  button = gtk_button_new_from_stock (GTK_STOCK_OK);
  gtk_container_add (GTK_CONTAINER (bbox), button);

  button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
  gtk_container_add (GTK_CONTAINER (bbox), button);

  button = gtk_button_new_from_stock (GTK_STOCK_HELP);
  gtk_container_add (GTK_CONTAINER (bbox), button);

  return frame;
}

int main( int   argc,
          char *argv[] )
{
  static GtkWidget* window = NULL;
  GtkWidget *main_vbox;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *frame_horz;
  GtkWidget *frame_vert;

  /* Inicializa GTK */
  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Cajas de Botones");

  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);

  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  main_vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);

  frame_horz = gtk_frame_new ("Cajas de Botones Horizontales");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);

  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
  gtk_container_add (GTK_CONTAINER (frame_horz), vbox);

  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Spread (espacio 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
                      TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Edge (espacio 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
                      TRUE, TRUE, 5);

  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Start (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_START),
                      TRUE, TRUE, 5);

  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "End (espacio 10)", 10, 85, 20, GTK_BUTTONBOX_END),
                      TRUE, TRUE, 5);

  frame_vert = gtk_frame_new ("Cajas de Botones Verticales");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);

  hbox = gtk_hbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
  gtk_container_add (GTK_CONTAINER (frame_vert), hbox);

  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Spread (espacio 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
                      TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Edge (espacio 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
                      TRUE, TRUE, 5);

  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Start (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_START),
                      TRUE, TRUE, 5);

  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "End (espacio 20)", 20, 85, 20, GTK_BUTTONBOX_END),
                      TRUE, TRUE, 5);

  gtk_widget_show_all (window);


  gtk_main ();

  return 0;
}


GtkTable

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:


GtkWidget * gtk_table_new (rows, 
 	columns, 	 
 	homogeneous);	 

guint  	  rows;
guint  	  columns;
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:


void gtk_table_attach (table, 
 	child, 	 
 	left_attach, 	 
 	right_attach, 	 
 	top_attach, 	 
 	bottom_attach, 	 
 	xoptions, 	 
 	yoptions, 	 
 	xpadding, 	 
 	ypadding);

GtkTable          *table;
GtkWidget         *child;
guint   	  left_attach;
guint   	  right_attach;
guint   	  top_attach;
guint  	          bottom_attach;
GtkAttachOptions  xoptions;
GtkAttachOptions  yoptions;
guint  	          xpadding;
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:


void gtk_table_attach_defaults (table, 
 	child, 	 
 	left_attach, 	 
 	right_attach, 	 
 	top_attach, 	 
 	bottom_attach);	 

GtkTable  *table;
GtkWidget *child;
guint  	  left_attach;
guint  	  right_attach;
guint  	  top_attach;
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.


void gtk_table_set_row_spacing(table,
 	row, 	 
 	spacing);

GtkTable *table;
guint  	 row;
guint  	 spacing;


void gtk_table_set_col_spacing(table,
 	column, 	 
 	spacing);

GtkTable *table;
guint  	 column;
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:


void gtk_table_set_row_spacings(table,
 	spacing);

GtkTable *table;
guint  	spacing;


void gtk_table_set_col_spacings(table, 
 	spacing);

GtkTable *table;
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



#include <gtk/gtk.h>

/* Los datos pasados a esta función se imprimen en la salida estándar (stdout) */
void callback( GtkWidget *widget,
               gpointer   data )
{
    g_print ("Hello again - %s was pressed\n", (char *) data);
}

/* Función para terminar el programa */
gint delete_event( GtkWidget *widget,
                   GdkEvent  *event,
                   gpointer   data )
{
    gtk_main_quit ();
    return FALSE;
}

int main( int   argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;

    gtk_init (&argc, &argv);

    /* Crea una ventana */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* Establece el título de la ventana */
    gtk_window_set_title (GTK_WINDOW (window), "Table");

    /* Establece un manejador para "delete_event" que inmediatamente
     * cerrará GTK. */
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (delete_event), NULL);

    /* Establece el tamaño del borde de la ventana. */
    gtk_container_set_border_width (GTK_CONTAINER (window), 20);

    /* Crea una tabla de 2x2 */
    table = gtk_table_new (2, 2, TRUE);

    /* Coloca la tabla en la ventana principal */
    gtk_container_add (GTK_CONTAINER (window), table);

    /* Crea el primer botón */
    button = gtk_button_new_with_label ("button 1");

    /* cuando se pulsa el botón, se llama a la función que se ha creado anteriormente
     * "callback", con un puntero a "button 1" como argumento */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (callback), (gpointer) "button 1");


    /* Coloca el primer botón (button 1) en el cuadrante superior izquierdo de la tabla */
    gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 0, 1);

    gtk_widget_show (button);

    /* Crea el segundo botón */

    button = gtk_button_new_with_label ("button 2");

    /* cuando se pulsa el botón, se llama a la función que se ha creado anteriormente
     * "callback", con un puntero a "button 2" como argumento */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (callback), (gpointer) "button 2");
    /* Coloca el segundo botón (button 2) en el cuadrante superior derecho de la tabla. */
    gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 0, 1);

    gtk_widget_show (button);

    /* Crea el botón de "Quit" */
    button = gtk_button_new_with_label ("Quit");

    /* Cuando se pulsa el botón, se hace una llamada a función "delete_event" y el
     * programa termina. */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (delete_event), NULL);

    /* Coloca el botón "quit" ocupando los dos cuadrantes inferiores de la tabla.*/
    gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 2, 1, 2);

    gtk_widget_show (button);

    gtk_widget_show (table);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}


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:


GtkWidget * gtk_notebook_new();
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.


void gtk_notebook_set_tab_pos(notebook, 
 	pos);

GtkNotebook      *notebook;
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.


void gtk_notebook_append_page(notebook, 
 	child, 	 
 	tab_label);

GtkNotebook *notebook;
GtkWidget   *child;
GtkWidget   *tab_label;


void gtk_notebook_prepend_page(notebook,
 	child, 	 
 	tab_label);

GtkNotebook *notebook;
GtkWidget   *child;
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.


void gtk_notebook_insert_page(notebook, 
 	child, 	 
 	tab_label, 	 
 	position);

GtkNotebook *notebook;
GtkWidget   *child;
GtkWidget   *tab_label;
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:


void gtk_notebook_remove_page(notebook,
 	page_num);	 

GtkNotebook *notebook;
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:


gint gtk_notebook_get_current_page(notebook);	 

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.


void gtk_notebook_next_page(notebook);	 

GtkNoteBook *notebook;


void gtk_notebook_prev_page(notebook);	 

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.


void gtk_notebook_set_current_page(notebook, 
 	page_num);

GtkNotebook *notebook;
gint        page_num;


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


void gtk_notebook_set_show_tabs(notebook, 
 	show_tabs);

GtkNotebook *notebook;
gboolean    show_tabs;


void gtk_notebook_set_show_border(notebook, 
 	show_border);

GtkNotebook *notebook;
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.


void gtk_notebook_set_scrollable(notebook, 
 	scrollable);

GtkNotebook *notebook;
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


Ejemplo de uso de GtkNotebook


#include <stdio.h>
#include <gtk/gtk.h>

/* Esta función va cambiando la posición de las pestañas
This function rotates the position of the tabs */
void rotate_book( GtkButton   *button,
                  GtkNotebook *notebook )
{
    gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos + 1) % 4);
}

/* Añade o borra las pestañas y los bordes del notebook */
void tabsborder_book( GtkButton   *button,
                      GtkNotebook *notebook )
{
    gint tval = FALSE;
    gint bval = FALSE;
    if (notebook->show_tabs == 0)
            tval = TRUE;
    if (notebook->show_border == 0)
            bval = TRUE;

    gtk_notebook_set_show_tabs (notebook, tval);
    gtk_notebook_set_show_border (notebook, bval);
}

/* Borra una página del notebook */
void remove_book( GtkButton   *button,
                  GtkNotebook *notebook )
{
    gint page;

    page = gtk_notebook_get_current_page (notebook);
    gtk_notebook_remove_page (notebook, page);
    /* Con la siguiente función se vuelve a dibujar el notebook
     * para que no se vea la página que ha sido borrada. */
    gtk_widget_queue_draw (GTK_WIDGET (notebook));
}

gint delete( GtkWidget *widget,
             GtkWidget *event,
             gpointer   data )
{
    gtk_main_quit ();
    return FALSE;
}

int main( int argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;
    GtkWidget *notebook;
    GtkWidget *frame;
    GtkWidget *label;
    GtkWidget *checkbutton;
    int i;
    char bufferf[32];
    char bufferl[32];

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (delete), NULL);

    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    table = gtk_table_new (3, 6, FALSE);
    gtk_container_add (GTK_CONTAINER (window), table);

    /* Crea un notebook, y especifica la posición de las pestañas */
    notebook = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
    gtk_table_attach_defaults (GTK_TABLE (table), notebook, 0, 6, 0, 1);
    gtk_widget_show (notebook);

    /* El siguiente bucle, añade 5 páginas al notebook (append)*/
    for (i = 0; i < 5; i++) {
        sprintf(bufferf, "Append Frame %d", i + 1);
        sprintf(bufferl, "Page %d", i + 1);

        frame = gtk_frame_new (bufferf);
        gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);

        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);

        label = gtk_label_new (bufferl);
        gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
    }

    /* Se añade un página para Now let's add a page to a specific spot */
    checkbutton = gtk_check_button_new_with_label ("Check me please!");
    gtk_widget_set_size_request (checkbutton, 100, 75);
    gtk_widget_show (checkbutton);

    label = gtk_label_new ("Add page");
    gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);

    /* El siguiente bucle, añade 5 páginas al notebook (prepend)*/
    for (i = 0; i < 5; i++) {
        sprintf (bufferf, "Prepend Frame %d", i + 1);
        sprintf (bufferl, "PPage %d", i + 1);

        frame = gtk_frame_new (bufferf);
        gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);

        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);

        label = gtk_label_new (bufferl);
        gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), frame, label);
    }

    /* Con esta función, se mostrará una página específica "page 4" */
    gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 3);

    /* Crea los botones */
    button = gtk_button_new_with_label ("close");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (delete), NULL);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 1, 2);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("next page");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_notebook_next_page),
                              G_OBJECT (notebook));
    gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 1, 2);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("prev page");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_notebook_prev_page),
                              G_OBJECT (notebook));
    gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("tab position");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (rotate_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 3, 4, 1, 2);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("tabs/border on/off");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (tabsborder_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 4, 5, 1, 2);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("remove page");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (remove_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 5, 6, 1, 2);
    gtk_widget_show (button);

    gtk_widget_show (table);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}

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:


GtkWidget * gtk_alignment_new(xalign, 
 	yalign, 	 
 	xscale, 	 
 	yscale);

gfloat  	xalign;
gfloat  	yalign;
gfloat  	xscale;
gfloat  	yscale;


void gtk_alignment_set(alignment, 
 	xalign, 	 
 	yalign, 	 
 	xscale, 	 
 	yscale);
 
GtkAlignment    *alignment;
gfloat  	xalign;
gfloat  	yalign;
gfloat  	xscale;
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):


  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:


  GtkWidget *gtk_hpaned_new (void)''';

  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:


  void gtk_paned_add1 (GtkPaned *paned, GtkWidget *child);

  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:


  GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
                             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:


  void gtk_layout_put( GtkLayout *layout,
                       GtkWidget *widget,
                       gint       x,
                       gint       y );

  void gtk_layout_move( GtkLayout *layout,
                        GtkWidget *widget,
                        gint       x,
                        gint       y );


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


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


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


  GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );

  GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );

  void gtk_layout_set_hadjustment( GtkLayout     *layout,
                                   GtkAdjustment *adjustment );

  void gtk_layout_set_vadjustment( GtkLayout     *layout,
                                   GtkAdjustment *adjustment);


GtkFixed

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:


  GtkWidget* gtk_fixed_new( void );

  void gtk_fixed_put( GtkFixed  *fixed,
                      GtkWidget *widget,
                      gint       x,
                      gint       y );


	  void gtk_fixed_move( GtkFixed  *fixed,
                               GtkWidget *widget,
                               gint       x,
                               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.


  void gtk_fixed_set_has_window( GtkFixed  *fixed,
	                         gboolean   has_window );

  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

Ejemplo de uso del GtkFixed


#include <gtk/gtk.h>

/* Para agilizar el trabajo, se usarán algunas variables globales para
 * guardar la posición del widget dentro del contenedor fixed */
gint x = 50;
gint y = 50;

/* Esta función mueve el botón a una nueva posición
 * del contenedor Fixed. */
void move_button( GtkWidget *widget,
                  GtkWidget *fixed )
{
  x = (x + 30) % 300;
  y = (y + 50) % 300;
  gtk_fixed_move (GTK_FIXED (fixed), widget, x, y);
}

int main( int   argc,
          char *argv[] )
{
  /* GtkWidget es el tipo utilizado para widgets */
  GtkWidget *window;
  GtkWidget *fixed;
  GtkWidget *button;
  gint i;

  /* Inicializa GTK */
  gtk_init (&argc, &argv);

  /* Crea una ventana */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Fixed Container");

  /* Conecta el evento "destroy" a un manejador de señales */
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);

  /* Establece la anchura del borde de la ventana. */
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  /* Crea un Contenedor Fixed */
  fixed = gtk_fixed_new ();
  gtk_container_add (GTK_CONTAINER (window), fixed);
  gtk_widget_show (fixed);

  for (i = 1 ; i <= 3 ; i++) {
    /* Crea un botón con la etiqueta "Press me" */
    button = gtk_button_new_with_label ("Press me");

    /* Cuando el botón recive la señal "clicked", se llama a la función
     * move_button() pasando el Contenedor Fixed como argumento */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (move_button), (gpointer) fixed);

    /* Coloca el botón dentro de la ventana de los contenedores fixed. */
    gtk_fixed_put (GTK_FIXED (fixed), button, i*50, i*50);

    /* El último paso es mostrar el nuevo widget que se ha creado. */
    gtk_widget_show (button);
  }

  /* Muestra la ventana */
  gtk_widget_show (window);

  /* Entra en el bucle de eventos */
  gtk_main ();

  return 0;
}

GtkFrame y 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.


GtkWidget * gtk_aspect_frame_new(label, 	 
 	xalign, 	 
 	yalign, 	 
 	ratio, 	 
 	obey_child);

const gchar *label;
gfloat      xalign;
gfloat      yalign;
gfloat      ratio;
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


void gtk_aspect_frame_set(aspect_frame, 
 	xalign, 	 
 	yalign, 	 
 	ratio, 	 
 	obey_child);

GtkAspectFrame  *aspect_frame;
gfloat          xalign;
gfloat  	yalign;
gfloat  	ratio;
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


Ejemplo GtkAspectFrame


#include <gtk/gtk.h>

int main( int argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *aspect_frame;
    GtkWidget *drawing_area;
    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    /* Crea un widget aspect_frame y lo añade a la ventana principal */

    aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
                                         0.5, /* centra x */
                                         0.5, /* centra y */
                                         2, /* el ratio: xsize/ysize = 2 */
                                         FALSE /* este argumento se ignora */);

    gtk_container_add (GTK_CONTAINER (window), aspect_frame);
    gtk_widget_show (aspect_frame);

    /* Se añade un widget child al widget aspect frame */

    drawing_area = gtk_drawing_area_new ();

    /* el tamaño de la ventana se establece a 200x200, aunque el widget AspectFrame
     * establece el tamaño a 200x100 ya que el ratio es de 2x1 */
    gtk_widget_set_size_request (drawing_area, 200, 200);
    gtk_container_add (GTK_CONTAINER (aspect_frame), drawing_area);
    gtk_widget_show (drawing_area);

    gtk_widget_show (window);
    gtk_main ();
    return 0;
}


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:


  GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
                               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.


  GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );

  GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );

  void gtk_viewport_set_hadjustment( GtkViewport   *viewport,
                                     GtkAdjustment *adjustment );

  void gtk_viewport_set_vadjustment( GtkViewport   *viewport,
                                     GtkAdjustment *adjustment );


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


  void gtk_viewport_set_shadow_type( GtkViewport   *viewport,
                                     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
Herramientas personales
Espacios de nombres

Variantes
Acciones
Navegación
Herramientas