Página siguiente Página anterior Índice general

10. Widgets Contenedores

10.1 Libros de notas (Notebooks)

El widget Notebook es una colección de `páginas' que se solapan las unas a las otras, cada unas con un contenido diferente. Este widget se ha vuelto cada vez más común últimamente en la programación de interfaces gráficos de usuario (GUI en inglés), y es una buena forma de mostrar bloques de información similar que necesitan aparecer de forma separada.

La primera función que necesita conocer, como probablemente ya habrá adivinado, se utiliza para crear un nuevo widget notebook.

GtkWidget *gtk_notebook_new( void );

Una vez haya crear el libro de notas, hay 12 funciones que se pueden utilizar para trabajar con él. Echémosles un vistazo una a una.

La primera que estudiaremos será la que nos permita establecer la posición de los indicadores de la página. Estos indicadores se pueden poner en cuatro lugares diferentes: arriba, abajo, a la derecha o a la izquierda.

void gtk_notebook_set_tab_pos( GtkNotebook     *notebook,
                               GtkPositionType  pos );

GtkPositionType debe tener uno de los valores siguientes (su significado está bastante claro):

GTK_POS_TOP es el valor por defecto.

Lo siguiente que estudiaremos es como añadir páginas al libro de notas. Hay tres formas de añadirle páginas al widget. Veamos las dos primeras formas (son muy parecidas).

void gtk_notebook_append_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label );

void gtk_notebook_prepend_page( GtkNotebook *notebook,
                                GtkWidget   *child,
                                GtkWidget   *tab_label );

Estas funciones le añaden páginas al libro de notas insertándolas desde el fondo del libro (añadiéndolas), o desde parte superior del libro (preañadiéndolas). child es el widget que se mete en la página del libro de notas, y tab_label es la etiqueta para la página que estamos añadiendo.

La función que queda que sirve para añadir una página contiene todas las propiedades de las anteriores, pero además permite especificar en que posición quiere que esté la página dentro del libro de notas.

void gtk_notebook_insert_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label,
                               gint         position );

Los parámetros son los mismos que habían en las funciones _append_ y _prepend_ excepto que hay uno más que antes, position. Este parámetro se utiliza para especificar en que lugar debe introducirse la página.

Ahora que sabemos como añadir un página, veamos como podemos eliminar una página del libro de notas.

void gtk_notebook_remove_page( GtkNotebook *notebook,
                               gint         page_num );

Esta función coge la página especificada por page_num y la elimina del widget al que apunta notebook.

Para saber cual es la página actual del libro de notas utilice la función:

gint gtk_notebook_current_page( GtkNotebook *notebook );

Las dos funciones siguientes sirven para ir a la página siguiente o a la anterior del libro de notas. Para utilizarlas sólo hay que proporcionar el widget notebook que queremos manipular. Nota: cuando el libro de notas está en la última página y se llama a gtk_notebook_next_page, se pasará a la primera página. Sin embargo, si el libro de notas está en la primera página, y se llama a gtk_notebook_prev_page, no se pasará a la última página.

void gtk_notebook_next_page( GtkNoteBook *notebook );

void gtk_notebook_prev_page( GtkNoteBook *notebook );

La siguiente función establece la página `activa'. Si quiere que se abra el libro de notas por la página 5, por ejemplo, debe utilizar esta función. Si no utiliza esta función el libro de notas empezará por defecto en la primera página.

void gtk_notebook_set_page( GtkNotebook *notebook,
                            gint         page_num );

Las dos funciones siguientes añaden o eliminan los indicadores de las páginas o el borde del libro, respectivamente.

void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
                                 gint         show_tabs);

void gtk_notebook_set_show_border( GtkNotebook *notebook,
                                   gint         show_border );

show_tabs y show_border puede ser TRUE o FALSE.

Ahora echémosle un vistaza a un ejemplo, sacado del código de testgtk.c que viene con la distribución de GTK, y que muestra la utilización de las 13 funciones. Este pequeño programa crea una ventana con un libro de notas y seis botones. El libro de notas contiene 11 páginas, incluidas de tres formas diferentes, añadidas, insertadas, y preañadidas. Los botones le permiten rotar las posiciones de los indicadores, añadir y eliminar los indicadores y el borde, eliminar una página, cambiar páginas hacia delante y hacia detrás, y salir del programa.

/* principio del ejemplo notebook notebook.c */

#include <gtk/gtk.h>

/* Esta función rota la posición de los indicadores */
void rotate_book (GtkButton *button, GtkNotebook *notebook)
{
    gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
}

/* Añade/Elimina los indicadores de la página y los bordes */
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);
}

/* Elimina una página del libro de notas */
void remove_book (GtkButton *button, GtkNotebook *notebook)
{
    gint page;
    
    page = gtk_notebook_current_page(notebook);
    gtk_notebook_remove_page (notebook, page);
    /* Hay que redibujar el widget --
     Esto fuerza que el widget se autoredibuje */
    gtk_widget_draw(GTK_WIDGET(notebook), NULL);
}

void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
{
    gtk_main_quit ();
}

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);
    
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (delete), NULL);
    
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    table = gtk_table_new(2,6,TRUE);
    gtk_container_add (GTK_CONTAINER (window), table);
    
    /* Crea un nuevo libro de notas, indicando la posición de los
       indicadores */
    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);
    
    /* le añadimos un montón de páginas al libro de notas */
    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_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_usize (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);
    }
    
    
    /* Ahora añadimos una página en punto específico */
    checkbutton = gtk_check_button_new_with_label ("Check me please!");
    gtk_widget_set_usize(checkbutton, 100, 75);
    gtk_widget_show (checkbutton);
    
    label = gtk_label_new ("Add spot");
    gtk_container_add (GTK_CONTAINER (checkbutton), label);
    gtk_widget_show (label);
    label = gtk_label_new ("Add page");
    gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
    
    /* Y finalmente preañadimos páginas en el libro de notas */
    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_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_usize (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);
    }
    
    /* Decimos en que página empezar (página 4) */
    gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
    
    
    /* creamos un montón de botones */
    button = gtk_button_new_with_label ("close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (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");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_notebook_next_page,
                               GTK_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");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_notebook_prev_page,
                               GTK_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");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) rotate_book, GTK_OBJECT(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");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) tabsborder_book,
                               GTK_OBJECT (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");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) remove_book,
                               GTK_OBJECT(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;
}
/* fin del ejemplo */

Espero que la explicación le ayude de alguna manera a crear libros de notas en sus aplicaciones GTK.

10.2 Ventanas con barras de desplazamiento

Las ventanas con barras de desplazamiento se utilizan para crear una zona con barras de desplazamiento dentro de una ventana real. Puede insertar cualquier tipo de widget en una ventana con barras de desplazamiento, y podrá utilizarlo sin importar su tamaño gracias a las barras de desplazamiento.

La función siguiente se utiliza para crear una nueva ventana con barras de desplazamiento.

GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
                                    GtkAdjustment *vadjustment );

Donde el primer argumento es el ajuste para la dirección horizontal, y el segundo es el ajuste para la dirección vertical. Casi siempre valen NULL.

void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
                                     GtkPolicyType      hscrollbar_policy,
                                     GtkPolicyType      vscrollbar_policy );

Esta función establece la política que se utilizará con respecto a las barras de desplazamiento. El primer argumento es la ventana con barras de desplazamiento sobre la que queremos actuar. El segundo establece la política para la barra de desplazamiento horizontal, y el tercero la política para la barra de desplazamiento vertical.

La política puede ser GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC decidirá automáticamente si necesita barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá siempre las barras de desplazamiento.

Aquí tenemos un ejemplo sencillo que empaqueta 100 botones de selección en una ventana con barras de desplazamiento. Solamente he comentado las partes que debería ser nuevas para usted.

/* principio del ejemplo scrolledwin scrolledwin.c */

#include <gtk/gtk.h>

void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}

int main (int argc, char *argv[])
{
    static GtkWidget *window;
    GtkWidget *scrolled_window;
    GtkWidget *table;
    GtkWidget *button;
    char buffer[32];
    int i, j;
    
    gtk_init (&argc, &argv);
    
    /* Crea un nuevo cuadro de diálogo para que la ventana con barras de
     * desplazamiento se meta dentro. Un cuadro de diálogo es como una
     * ventana normal excepto que tiene dentro una vbox y un separador
     * horizontal. Es sólo un atajo para crear cuadros de diálogo */
    window = gtk_dialog_new ();
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        (GtkSignalFunc) destroy, NULL);
    gtk_window_set_title (GTK_WINDOW (window), "dialog");
    gtk_container_border_width (GTK_CONTAINER (window), 0);
    gtk_widget_set_usize(window, 300, 300);
    
    /* crea una nueva ventana con barras de desplazamiento. */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    
    gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);

    /* la política es GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS.
     * GTK_POLICY_AUTOMATIC decidirá automáticamente si necesita
     * barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
     * siempre las barras de desplazamiento. El primer argumento se
     * refiere a la barra horizontal, el segundo a la vertical. */
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    /* El cuadro de diálogo se crea con una vbox dentro de él. */
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, 
                        TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);
    
    /* crea una tabla de 10 por 10 casillas. */
    table = gtk_table_new (10, 10, FALSE);
    
    /* pone el espacio en x y en y a 10 */
    gtk_table_set_row_spacings (GTK_TABLE (table), 10);
    gtk_table_set_col_spacings (GTK_TABLE (table), 10);
    
    /* empaqueta la tabla en la ventana con barras de desplazamiento */
    gtk_container_add (GTK_CONTAINER (scrolled_window), table);
    gtk_widget_show (table);
    
    /* crea una rejilla de botones de selección en la tabla para
     * demostrar la ventana con barras de desplazamiento. */
    for (i = 0; i < 10; i++)
       for (j = 0; j < 10; j++) {
          sprintf (buffer, "button (%d,%d)\n", i, j);
          button = gtk_toggle_button_new_with_label (buffer);
          gtk_table_attach_defaults (GTK_TABLE (table), button,
                                     i, i+1, j, j+1);
          gtk_widget_show (button);
       }
    
    /* Añade un botón "close" en la parte de abajo del cuadro de
     * diálogo */
    button = gtk_button_new_with_label ("close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_widget_destroy,
                               GTK_OBJECT (window));
    
    /* hace que el botón puede ser elegido por defecto. */
    
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
    
    /* Hace que el botón sea el elegido por defecto. Con pulsar la
     * tecla "Enter" se activará este botón. */
    gtk_widget_grab_default (button);
    gtk_widget_show (button);
    
    gtk_widget_show (window);
    
    gtk_main();
    
    return(0);
}
/* fin del ejemplo */

Juegue un poco redimensionando la ventana. Vea como actuan las barras de desplazamiento. También puede utilizar la función gtk_widget_set_usize() para poner el tamaño por defecto de la ventana o de cualquier otro widget.

10.3 El widget ``ventana dividida'' (Paned Window)

El widget ventana dividida es útil para cuando se quiere dividir una zona en dos partes, con un tamaño relativo controlado por el usuario. Entre las dos porciones de la ventana se dibuja un separador con un botoncito que el usuario puede arrastrar para cambiar el tamaño de cada zona. La división puede ser horizontal (HPaned) o vertical (VPaned).

Para crear una nueva ventana dividida, utilice una de las siguientes funciones:

GtkWidget *gtk_hpaned_new (void);

GtkWidget *gtk_vpaned_new (void);

Después de crear el widget ventana dividida, tiene que añadirle un widget hijo a cada mitad. Para hacerlo, utilice:

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

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

gtk_paned_add1() añade el widget hijo a la mitad que se encuentra en la parte izquierda o superior de la ventana dividida. gtk_paned_add2() añade el widget a la mitad que hay en la parte derecha o inferior de la ventana.

Por ejemplo, si queremos crear una parte del interface de usuario de un programa de correo-e imaginario. Dividiremos verticalmente una ventana en dos partes, teniendo en la parte superior una lista de los mensajes de correo-e y en la parte inferior el texto de uno de estos mensajes. El programa es bastante fácil de entender. Solo un par de cosillas: no se puede añadir texto en un widget de texto (Text) si no se ha hecho antes gtk_widget_realize(), pero como demostración de una técnica alternativa, para añadir el texto conectaremos un manipulador a la señal ``realize''. Y tenemos que añadir la opción GTK_SHRINK a algunos de los elementos que hay en la tabla con la ventana de texto y sus barras de desplazamiento, así cuando la porción de abajo se haga más pequeña, se encogerá correctamente en lugar de desaparecer por la parte de abajo de la ventana.

/* principio del ejemplo paned paned.c */

#include <gtk/gtk.h>
   
/* Crear la lista de "messages" */
GtkWidget *
create_list (void)
{

    GtkWidget *scrolled_window;
    GtkWidget *list;
    GtkWidget *list_item;
   
    int i;
    char buffer[16];
   
    /* Crear una nueva ventana con barras de desplazamiento si hacen
       falta */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, 
                                    GTK_POLICY_AUTOMATIC);
   
    /* Crear una nueva lista y poner en ella la ventana con barras */
    list = gtk_list_new ();
    gtk_container_add (GTK_CONTAINER(scrolled_window), list);
    gtk_widget_show (list);
   
    /* Añadir algunos mensajes a la ventana */
    for (i=0; i<10; i++) {

        sprintf(buffer,"Message #%d",i);
        list_item = gtk_list_item_new_with_label (buffer);
        gtk_container_add (GTK_CONTAINER(list), list_item);
        gtk_widget_show (list_item);

    }
   
    return scrolled_window;
}
   
/* Añadir algún texto a nuestro widget de texto - esta función se
   invoca cada vez que se produce una señal realize en la
   ventana. Podemos forzar esta señal mediante gtk_widget_realize, pero
   primero tiene que formar parte de una jerarquía */

void
realize_text (GtkWidget *text, gpointer data)
{
    gtk_text_freeze (GTK_TEXT (text));
    gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
    "From: pathfinder@nasa.gov\n"
    "To: mom@nasa.gov\n"
    "Subject: Made it!\n"
    "\n"
    "We just got in this morning. The weather has been\n"
    "great - clear but cold, and there are lots of fun sights.\n"
    "Sojourner says hi. See you soon.\n"
    " -Path\n", -1);
   
    gtk_text_thaw (GTK_TEXT (text));
}
   
/* Creamos una zona con texto que muestra un "message" */
GtkWidget *
create_text (void)
{
    GtkWidget *table;
    GtkWidget *text;
    GtkWidget *hscrollbar;
    GtkWidget *vscrollbar;
   
    /* Crea una tabla para contener el widget de texto y las barras de
       desplazamiento */
    table = gtk_table_new (2, 2, FALSE);
   
    /* Pone un widget de texto en la esquina superior izquierda.
       Observe la utilización de GTK_SHRINK en la dirección y */
    text = gtk_text_new (NULL, NULL);
    gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
                      GTK_FILL | GTK_EXPAND,
                      GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
    gtk_widget_show (text);
   
    /* Pone una HScrollbar en la esquina inferior izquierda */
    hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
    gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    gtk_widget_show (hscrollbar);
   
    /* Y una VScrollbar en la esquina superior derecha */
    vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
    gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
                      GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
    gtk_widget_show (vscrollbar);
   
    /* Y un manejador para poner un mensaje en el widget de texto
       cuando reciba realize */
    gtk_signal_connect (GTK_OBJECT (text), "realize",
                        GTK_SIGNAL_FUNC (realize_text), NULL);
   
    return table;
}
   
int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *vpaned;
    GtkWidget *list;
    GtkWidget *text;

    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Paned Windows");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* crea un widget vpaned y lo añade a nuestra ventana superior */
   
    vpaned = gtk_vpaned_new ();
    gtk_container_add (GTK_CONTAINER(window), vpaned);
    gtk_widget_show (vpaned);
   
    /* Ahora crea los contenidos de las dos mitades de la ventana */
   
    list = create_list ();
    gtk_paned_add1 (GTK_PANED(vpaned), list);
    gtk_widget_show (list);
   
    text = create_text ();
    gtk_paned_add2 (GTK_PANED(vpaned), text);
    gtk_widget_show (text);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}
/* fin del ejemplo */

10.4 Barras de herramientas

Las barras de herramientas acostumbran a agrupar un conjunto de widgets para hacer más sencilla la personalización de su aspecto y composición. Típicamente una barra de herramientas consiste en botones con iconos, etiquetas y tips para los iconos (pequeño texto descriptivo que aparece cuando se mantiene el ratón sobre el icono), pero en realidad en una barra se puede poner cualquier tipo de widget. Finalmente, los elementos se pueden disponer de forma horizontal o vertical, y los botones pueden mostrar iconos, etiquetas o ambos.

La creación de una barra de herramientas se hace (como puede que ya haya sospechado) mediante la función siguiente:

GtkWidget *gtk_toolbar_new( GtkOrientation orientation,
                            GtkToolbarStyle  style );

donde orientation puede ser:

  GTK_ORIENTATION_HORIZONTAL    
  GTK_ORIENTATION_VERTICAL

y style:

  GTK_TOOLBAR_TEXT
  GTK_TOOLBAR_ICONS
  GTK_TOOLBAR_BOTH

La variable style se aplica a todos los botones que se crean con las funciones `item' (pero no a los botones insertados en la barra de herramientas como widgets separados).

Después de crear una barra de herramientas, se pueden añadir, preañadir e insertar elementos (o sea, botones) en la misma. Los campos que describen un elemento son el texto de la etiqueta, el texto del tip, un texto para el tip privado, un icono para el botón y una función de llamada para el mismo. Por ejemplo, para añadir un elemento puede utilizar la siguiente función:

GtkWidget *gtk_toolbar_append_item( GtkToolbar    *toolbar,
                                    const char    *text,
                                    const char    *tooltip_text,
                                    const char    *tooltip_private_text,
                                    GtkWidget     *icon,
                                    GtkSignalFunc  callback,
                                    gpointer       user_data );

Si quiere utilizar gtk_toolbar_insert_item, el único parámetro adicional que debería especificar es la posición en la que quiere que se introduzca el elemento.

Para añadir un espacio en blanco entre los elementos de la barra de herramientas, puede utilizar la función siguiente:

void gtk_toolbar_append_space( GtkToolbar *toolbar );

void gtk_toolbar_prepend_space( GtkToolbar *toolbar );

void gtk_toolbar_insert_space( GtkToolbar *toolbar,
                               gint        position );
 

Y el tamaño del espacio en blanco puede establecerse globalmente para toda una barra de herramientas con la función:

void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
                                 gint        space_size) ;

Si tiene que establecer la orientación de una barra de herramientas y su estilo, puede hacerlo `al vuelo' con las funciones siguientes:

void gtk_toolbar_set_orientation( GtkToolbar     *toolbar,
                                  GtkOrientation  orientation );

void gtk_toolbar_set_style( GtkToolbar      *toolbar,
                            GtkToolbarStyle  style );

void gtk_toolbar_set_tooltips( GtkToolbar *toolbar,
                               gint        enable );

Para mostrar algunas otras cosas que pueden hacerse con una barra de herramientas, vamos a ver el siguiente programa (interrumpiremos el listado con alguna explicación adicional):

#include <gtk/gtk.h>

#include "gtk.xpm"

/* Esta función está conectada al botón Close o a la acción de cerrar
 * la ventana desde el WM */
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_main_quit ();
}

Este principio ya debería de sonarle familiar, a no ser que éste sea su primer programa GTK. En nuestro programa no habrá ninguna novedad, salvo un bonito dibujo XPM que utilizaremos como icono para todos los botones.

GtkWidget* close_button; // este botón emitirá la señal de cerrar el programa
GtkWidget* tooltips_button; // para activar/desactivar los tooltips
GtkWidget* text_button,
         * icon_button,
         * both_button; // botones circulares para el estilo de la barra
GtkWidget* entry; // un widget para meter texto para mostrar como
                  // empaquetar widgets en la barra de herramientas

En realidad no necesitamos todos los widgets que acabo de poner, pero para aclarar las cosas un poco más los he puesto todos.

/* Esto es fácil... cuando uno de los botones cambia, sólo
 * tenemos que comprobar quien está activo y hacer que el estilo
 * de la barra de herramientas esté acorde con la elección
 * ATENCIÓN: ˇnuestra barra de herramientas es data !
void radio_event (GtkWidget *widget, gpointer data)
{
  if (GTK_TOGGLE_BUTTON (text_button)->active) 
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_TEXT);
  else if (GTK_TOGGLE_BUTTON (icon_button)->active)
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_ICONS);
  else if (GTK_TOGGLE_BUTTON (both_button)->active)
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_BOTH);
}

/* todavía más fácil, sólo hay que comprobar el botón de selección
 * y activar/desactivar los tooltips */
void toggle_event (GtkWidget *widget, gpointer data)
{
  gtk_toolbar_set_tooltips (GTK_TOOLBAR ( data ),
                            GTK_TOGGLE_BUTTON (widget)->active );
}

Lo de arriba son sólo dos funciones de llamada que se invocarán cuando se presione uno de los botones de la barra de herramientas. Todo esto ya debería resultarle familiar si ha utilizado alguna vez los botones de selección (o los botones circulares)

int main (int argc, char *argv[])
{
  /* Aquí está nuestra ventana principal (un cuadro de diálogo) y una
   * caja flotante */
  GtkWidget* dialog;
  GtkWidget* handlebox;

  /* De acuerdo, necesitamos una barra de herramientas, un icono con
   * una máscara (una para todos los botones) y un widget icono donde
   * meter el icono (crearemos un widget diferente para cada botón) */
  GtkWidget * toolbar;
  GdkPixmap * icon;
  GdkBitmap * mask;
  GtkWidget * iconw;

  /* a esta función se le llama en todas las aplicación GTK */
  gtk_init (&argc, &argv);
  
  /* crear una ventana nueva con un título y el tamaño adecuado */
  dialog = gtk_dialog_new ();
  gtk_window_set_title ( GTK_WINDOW ( dialog ) , "GTKToolbar Tutorial");
  gtk_widget_set_usize( GTK_WIDGET ( dialog ) , 600 , 300 );
  GTK_WINDOW ( dialog ) ->allow_shrink = TRUE;

  /* salimos si alguien intenta cerrarnos */
  gtk_signal_connect ( GTK_OBJECT ( dialog ), "delete_event",
                       GTK_SIGNAL_FUNC ( delete_event ), NULL);

  /* tenemos que mandar la señalo realize porque utilizamos pixmaps
   * para los elementos que hay en la barra de herramientas */
  gtk_widget_realize ( dialog );

  /* para hacerlo más bonito ponemos la barra de herramientas en la
   * caja flotante, para que así se pueda desatar de la ventana
   * principal */
  handlebox = gtk_handle_box_new ();
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG(dialog)->vbox ),
                       handlebox, FALSE, FALSE, 5 );

Lo de arriba debería ser parecido en cualquier aplicación GTK. Sólo está la inicialización de GTK, la creación de la ventana, etc... Solamente hay una cosa que probablemente necesite una explicación: una barra de herramientas flotante. Una barra de herramientas flotante sólo es otra barra donde pueden empaquetarse widgets. La diferencia que tiene con una barra típica es que puede desatarse de la ventana padre (o, de hecho, la barra de herramientas flotante permanece en el padre, pero reducida a un rectángulo muy pequeño, mientras que todos sus contenidos se pasan a una nueva ventana flotante). Es bonito tener una barra de herramientas flotante, por lo que estos dos widgets suelen aparecer juntos.

  /* la barra de herramientas será horizontal, con iconos y texto, y
   * con un espacio de 5pxl entre elementos y finalmente, la ponemos en
   * nuestra caja flotante */
  toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
                              GTK_TOOLBAR_BOTH );
  gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
  gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
  gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );

  /* ahora creamos el icono con la máscara: utilizaremos el widget
   * icon con todos los elementos de la barra de herramientas */
  icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &mask,
      &dialog->style->white, gtk_xpm );

Bien, lo que acabamos de escribir es la inicialización del widget de la barra de herramientas y la creación de un pixmap GDK con su máscara. Si quiere saber algo más sobre la utilización de pixmaps, vea la documentación de GDK o la sección Pixmaps en este tutorial.

  /* nuestro primer elemento es el botón <close> */
  iconw = gtk_pixmap_new ( icon, mask ); // icon widget
  close_button = 
    gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), // nuestra barra
                              "Close",               // etiqueta del botón
                              "Closes this app",     // tooltip para el botón
                              "Private",             // cadena privada del tooltip
                              iconw,                 // widget del icono
                              GTK_SIGNAL_FUNC (delete_event), // una señal
                              NULL );
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // espacio después del elemento

En el trozo de código de arriba puede ver como se hace la acción más simple: añadir un botón a la barra de herramientas. Justo antes de añadir un nuevo elemento, tenemos que construir un widget pixmap para que sirva como icono para este elemento; este paso tendrá que repetirse para cada nuevo elemento. Después del elemento añadiremos un espacio en blanco en la barra de herramientas, para que los elementos que añadamos a continuación no se toquen los unos a los otros. Como puede ver, gtk_toolbar_append_item devuelve un puntero al widget de nuestro nuevo botón recien creado, por lo que podremos trabajar con él como siempre.

  /* ahora, vamos a hacer nuestro grupo de botones circulares... */
  iconw = gtk_pixmap_new ( icon, mask );
  icon_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON, // un tipo de elemento
                               NULL,                          // puntero al widget
                               "Icon",                        // etiqueta
                               "Only icons in toolbar",       // tooltip
                               "Private",                     // cadena privada del tooltip
                               iconw,                         // icono
                               GTK_SIGNAL_FUNC (radio_event), // señal
                               toolbar);                      // dato para la señal
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );

Aquí empezamos creando un grupo de botones circulares. Para hacerlo hemos utilizado gtk_toolbar_append_element. De hecho, utilizando esta función se pueden añadir tanto elementos simples como espacios en blanco (tipo = GTK_TOOLBAR_CHILD_SPACE o GTK_TOOLBAR_CHILD_BUTTON). En el caso de arriba, hemos empezado creando un grupo de botones circulares. Para crear más botones circulares para este grupo necesitaremos un puntero al botón anterior del grupo, mediante el que podremos construir fácilmente una lista de botones (ver la sección Botones circulares que se encuentra más adelante en este tutorial).

  /* following radio buttons refer to previous ones */
  iconw = gtk_pixmap_new ( icon, mask );
  text_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON,
                               icon_button,
                               "Text",
                               "Only texts in toolbar",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (radio_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
                                          
  iconw = gtk_pixmap_new ( icon, mask );
  both_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON,
                               text_button,
                               "Both",
                               "Icons and text in toolbar",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (radio_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(both_button),TRUE);

Al final hemos activado manualmente uno de los botones (en caso contrario los botones permanecerían todos en estado activo, impidiéndonos poder cambiar de uno a otro).

  /* aquí tenemos un sencillo botón de selección */
  iconw = gtk_pixmap_new ( icon, mask );
  tooltips_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
                               NULL,
                               "Tooltips",
                               "Toolbar with or without tips",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (toggle_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(tooltips_button),TRUE);

Un botón de selección puede crearse de una forma obvia (si ya sabe como crear botones circulares).

  /* para empaquetar un widget en la barra de herramientas, sólo
   * tenemos que crearlo y añadirlo en la barra con el tooltip
   * apropiado */
  entry = gtk_entry_new ();
  gtk_toolbar_append_widget( GTK_TOOLBAR (toolbar), 
                             entry, 
                             "This is just an entry", 
                             "Private" );

  /* bien, no se ha creado con la barra, así que debemos mostrarlo
   * explicitamente */
  gtk_widget_show ( entry );

Como puede ver, añadir cualquier tipo de widget a la barra de herramientas es fácil. Lo único que debe recordar es que este widget debe mostrarse manualmente (al contrario que los demás elementos que se mostrarán junto con la barra de herramientas).

  /* ˇ Eso es ! mostremos algo. */
  gtk_widget_show ( toolbar );
  gtk_widget_show (handlebox);
  gtk_widget_show ( dialog );

  /* quedémonos en gtk_main y ˇesperemos a que empiece la diversión! */
  gtk_main ();
  
  return 0;
}

Y ya estamos en el final del tutorial sobre la barra de herramientas. Por supuesto, para apreciar completamente el ejemplo, necesita además del código este precioso icono XPM que le mostramos a continuación:

/* XPM */
static char * gtk_xpm[] = {
"32 39 5 1",
".      c none",
"+      c black",
"@      c #3070E0",
"#      c #F05050",
"$      c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."};

10.5 Marcos con proporciones fijas

El widget aspect frame (marco proporcional) es como el widget frame (marco), excepto que conserva las proporciones (esto es, la relación entre el ancho y el alto) del widget hijo, añadiendo espacio extra en caso de ser necesario. Esto es útil, por ejemplo, si quiere hacer una vista previa de una gran imagen. El tamaño de la vista previa debería variar cuando el usuario redimensione la ventana, pero la proporción tiene que coincidir con la de la imagen original.

Para crear un nuevo marco proporcional utilice:

GtkWidget *gtk_aspect_frame_new( const gchar *label,
                                 gfloat       xalign,
                                 gfloat       yalign,
                                 gfloat       ratio,
                                 gint         obey_child);

xalign e yalign indican la alineación exactamente igual que con los widgets Alignment. Si obey_child es TRUE, la proporción de un widget hijo será la misma que la proporción del tamaño ideal que éste pida. En caso contrario, vendrá dada por ratio.

Para cambiar las opciones de un marco proporcional ya existente, puede utilizar:

void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
                           gfloat          xalign,
                           gfloat          yalign,
                           gfloat          ratio,
                           gint            obey_child);

Como por ejemplo, el siguiente programa utiliza un marco proporcional para mostrar una zona de dibujo cuyas proporciones siempre será de 2:1, no importa como el usuario redimensione la ventana.

/* principio del ejemplo aspectframe aspectframe.c */

#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");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* Crear un aspect_frame y añadirlo a nuestra ventana superior */
   
    aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
                                         0.5, /* centro x */
                                         0.5, /* centro y */
                                         2, /* tamañox/tamañoy = 2 */
                                         FALSE /* ignorar el aspecto del hijo */);
   
    gtk_container_add (GTK_CONTAINER(window), aspect_frame);
    gtk_widget_show (aspect_frame);
   
    /* Añadir un widget hijo al marco proporcional */
   
    drawing_area = gtk_drawing_area_new ();
   
    /* Pediremos una ventana de 200x200, pero el marco proporcional
     * sólo no dejará una ventana de 200x100, ya que tenemos una
     * relación de 2x1 */
    gtk_widget_set_usize (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;
}
/* fin del ejemplo */


Página siguiente Página anterior Índice general