Visualización y Entrada de información

Etiquetas

Las etiquetas son widgets que nos permiten mostrar información (texto) en cualquier parte de nuestro interfaz.

GtkLabel

El tipo más básico de etiqueta que podemos crear es GtkLabel, que permite crear porciones de texto (incluido multilínea) en nuestro interfaz.

En GTK+, para crearlas, se usa una de las siguientes funciones:

	  GtkWidget*            gtk_label_new               (const char    *str);
	  GtkWidget*            gtk_label_new_with_mnemonic (const char    *str);
	  

Ambas funciones reciben una cadena como parámetro, pero en cada una su significado es distinto. En gtk_label_new, la cadena de texto representa el texto que queremos que sea mostrado en la etiqueta, mientras que en gtk_label_new_with_mnemonic dicha cadena representa ese mismo texto a representar en pantalla, pero con una diferencia, que es que esa cadena puede contener el carácter '_' delante de algunas letras, lo que causa que dichas letras sean mostradas con el carácter de subrayado. Esto, más adelante veremos para qué puede servir.

GtkAccelLabel

Los widgets GtkAccelLabel se usan principalmente en la construcción de menús. Muestran una etiqueta (como un widget GtkLabel normal) más una combinación de teclas a su derecha. Por ejemplo, en el siguiente trozo de código:

GtkAccelLabel* = gtk_accel_label_new("entrada");
gtk_accel_label_set_accel_widget(label, entry);

creamos un widget de tipo GtkAccelLabel que contendrá la etiqueta "entrada". En la segunda línea de código, indicamos que la etiqueta que acabamo de crear debe vigilar y mostrar constantemente el atajo de teclado que tenga definido el widget entry. Así, si en el código asignas el atajo ctrl+P como la señal de activación de entry, GTK+ mostrará esa combinación de teclas en la etiqueta del widget GtkAccelLabel, al lado del texto "entrada". El GtkAccelLabel vigilará los cambios que se realicen en tiempo de ejecución en la combinación de teclas. Si cambiáramos el atajo de teclado por programación, AccelLabel se enteraría y mostraría el nuevo atajo al lado del texto "entrada". Para ver las diferencias existentes entre GtkAccelLabel y GtkLabel es interesante comparar el trozo de código anterior con el siguiente:

GtkLabel* label = gtk_label_new_with_mnemonic("_something");
gtk_label_set_mnemonic_widget(label, entry);

Este trozo de código crea una etiqueta con la letra s de "something" subrayada, lo que indica que GTK+ creará un atajo de teclado automáticamente con la combinación de teclas alt+s para el widget entry. Es decir, cuando pulsemos alt+s, se activará el widget entry. * FIXME * Debe haber una referencia que indique: para conocer un caso práctico del uso de gtk-accel-label, obsérvese el ejemplo gtk-accel-label-example * FIXME * A continuación, veremos un ejemplo de código que muestra el uso de gtk-accel-label y la creación manual de menús.

#include <gtk/gtk.h>

gint Delete (GtkWindow *widget, gpointer data) {
        gtk_main_quit();
        return FALSE;
}


int main(int argc, char *argv[]) {

        GtkWidget *window;      /* Top-level window */
        GtkWidget *menubar;     /* To hold our sample "File" menu */
        GtkWidget *menuitem;    /* Used for the "File","New","Open" MenuItems */
        GtkWidget *submenu;     /* The actual "menu" holding "New", "Open" */

        GtkAccelGroup *accel;   /* Our accel group */
        GtkWidget *accel_label; /* Our GtkAccelLabel */

        gtk_init(&argc, &argv);

        /* Create top-level window */
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_position(GTK_WINDOW (window), GTK_WIN_POS_CENTER_ALWAYS);
        g_signal_connect(G_OBJECT (window), "delete_event",
                 G_CALLBACK (Delete), NULL);

        /* Create our accel group, attach it to our top-level window */
        accel = gtk_accel_group_new();
        gtk_window_add_accel_group(GTK_WINDOW (window), accel);
        /* The accels are added down below... */

        /* create the menu bar as the only widget in our "window" */
        menubar = gtk_menu_bar_new();
        gtk_container_add(GTK_CONTAINER(window), menubar);
        gtk_widget_show(menubar);

        /* Create the "File" GtkMenuItem */
        menuitem = gtk_menu_item_new_with_label("File");
        gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
        gtk_widget_show(menuitem);

        /* Create the actual drop-down menu (called "submenu") */
        submenu = gtk_menu_new();
        /* set it to be the submenu of "File" */
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);

        /* Create "New" entry in our submenu: */
        menuitem = gtk_menu_item_new_with_label("New");
        gtk_menu_append(GTK_MENU(submenu), menuitem);

        /* Finally, add an accelerator for "New" of CTRL-N */
        gtk_widget_add_accelerator(menuitem,
        "activate", accel, 'N', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

        /* Wrap up the "New" GtkMenuItem by showing it: */
        gtk_widget_show(menuitem);


        /* Create a second GtkMenuItem ("Open") */
        /* Now with GtkAccelLabel... */
        menuitem = gtk_menu_item_new();

        accel_label = gtk_accel_label_new("Open");

        /* We attach it to 'menuitem', which is the GtkMenuItem */

        gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(accel_label), menuitem);
        gtk_container_add(GTK_CONTAINER(menuitem), accel_label);
        gtk_widget_show(accel_label);

        /* All done--add this menuitem to the submenu and show it... */
        gtk_menu_append(GTK_MENU(submenu), menuitem);
        gtk_widget_show(menuitem);

        /* Now, add an accelerator to the Open menuitem */
        gtk_widget_add_accelerator(menuitem,
        "activate", accel, 'P', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);


        gtk_widget_show(window);

        gtk_main();

        return 0;

}
      

Entrada de datos

GtkEntry - a single line text entry field. GtkHScale - a horizontal slider widget for selecting a value from a range. GtkVScale - a vertical slider widget for selecting a value from a range. GtkSpinButton - retrieve an integer or floating-point number from the user. GtkEditable - Interface for text-editing widgets.

GtkEntry

El widget Entry permite mostrar e introducir texto en una línea de un cuadro de diálogo. El texto se puede poner con llamadas a funciones que permiten reemplazar, preañadir o añadir el texto al contenido actual del widget Entry. Hay dos funciones para crear widget Entry:

GtkWidget *gtk_entry_new( void );

GtkWidget *gtk_entry_new_with_max_length( guint16 max );

La primera sirve para crear un nuevo widget Entry, mientras que la segunda crea el widget y además establece un límite en la longitud del texto que irá en el mismo. hay varias funciones que sirven para alterar el que texto que se está mostrando en el widget Entry.

void gtk_entry_set_text( GtkEntry    *entry,
                         const gchar *text );

void gtk_entry_append_text( GtkEntry    *entry,
                            const gchar *text );

void gtk_entry_prepend_text( GtkEntry    *entry,
                             const gchar *text );

La función gtk_entry_set_text cambia el contenido actual del widget Entry. Las funciones gtk_entry_append_text y gtk_entry_prepend_text permiten añadir texto por delante o por detrás. Las función siguientes permiten decir donde poner el punto de inserción.

void gtk_entry_set_position( GtkEntry *entry,
                             gint      position );

Se pueden obtener los contenidos del widget llamando a la función que se describe a continuación. Obtener los contenidos del widget puede ser útil en las funciones de llamada descritas más adelante.

gchar *gtk_entry_get_text( GtkEntry *entry );

Si quiere impedir que alguien cambie el contenido del widget escribiendo en él, utilice la función

void gtk_entry_set_editable( GtkEntry *entry,
                             gboolean  editable );

Esta función permite camiar el estado de edición de un widget Entry, siendo el argumento editable TRUE o FALSE. Si estamos utilizando el widget Entry en un sitio donde no queremos que el texto que se introduce sea visible, como por ejemplo cuando estamos introduciendo una clave, podemos utilizar la función siguiente, que también admite como argumento una bandera booleana.

void gtk_entry_set_visibility( GtkEntry *entry,
                               gboolean  visible );

Se puede seleccionar una región del texto utilizando la siguiente función. Esta función se puede utilizar después de poner algún texto por defecto en el widget, haciéndole fácil al usuario eliminar este texto.

void gtk_entry_select_region( GtkEntry *entry,
                              gint      start,
                              gint      end );

Si queremos saber el momento en el que el usuario ha introducido el texto, podemos conectar con las señales activate o changed. activate se activa cuando el usuario aprieta la tecla enter en el widget. changed se activa cuando cambia algo del texto, p.e. cuando se introduce o se elimina algún carácter.

Ejemplo de código de utilización del widget gtk-entry:



#include <gtk/gtk.h>

void enter_callback(GtkWidget *widget, GtkWidget *entry)
{
  gchar *entry_text;
  entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
  printf("Entry contents: %s\n", entry_text);
}

void entry_toggle_editable (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_editable(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

void entry_toggle_visibility (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_visibility(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

int main (int argc, char *argv[])
{

    GtkWidget *window;
    GtkWidget *vbox, *hbox;
    GtkWidget *entry;
    GtkWidget *button;
    GtkWidget *check;

    gtk_init (&argc, &argv);

    /* crear una nueva ventana */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Entry");
    gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                       (GtkSignalFunc) gtk_exit, NULL);

    vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);

    entry = gtk_entry_new_with_max_length (50);
    gtk_signal_connect(GTK_OBJECT(entry), "activate",
                       GTK_SIGNAL_FUNC(enter_callback),
                       entry);
    gtk_entry_set_text (GTK_ENTRY (entry), "hello");
    gtk_entry_append_text (GTK_ENTRY (entry), " world");
    gtk_entry_select_region (GTK_ENTRY (entry),
                             0, GTK_ENTRY(entry)->text_length);
    gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
    gtk_widget_show (entry);

    hbox = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (vbox), hbox);
    gtk_widget_show (hbox);

    check = gtk_check_button_new_with_label("Editable");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);

    check = gtk_check_button_new_with_label("Visible");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);

    button = gtk_button_new_with_label ("Close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC(gtk_exit),
                               GTK_OBJECT (window));
    gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (button);
    gtk_widget_show (button);

    gtk_widget_show(window);

    gtk_main();
    return(0);
}
      

Ajustes

Existen diferentes widgets en GTK+ que pueden ser ajustados visualmente por el usuario mediante el ratón o el teclado. Un ejemplo son los widgets de selección descritos en la sección “Funciones y señales ”. También hay otros widgets que pueden ser ajustados parcialmente, por ejemplo el widget de texto o el viewport.

Como es lógico el programa tiene que poder reaccionar a los cambios que el usuario realiza. Mediante señales especiales se podría conseguir saber qué y cuánto ha sido ajustado, pero en el caso de que se quiera conectar la señal con diferentes widgets, de manera que todos cambien al mismo tiempo, el proceso puede ser un poco tedioso.

El ejemplo más obvio es conectar una barra de desplazamiento a una región con texto. Si cada widget posee su propia forma de establecer u obtener sus valores de ajuste el programador puede que tenga que escribir sus propios controladores de señales para traducir el resultado de la señal producida por un widget como el argumento de una función usada para determinar valores en otro widget.

Para resolver este problema GTK+ usa objetos del tipo GtkAdjustment. Con ellos se consigue almacenar y traspasar información de una forma abstracta y flexible. El uso más obvio es el de almacenes de parámetros para widgets de escala (barras deslizantes y escalas). Como los GtkAdjustment derivan de GtkObject poseen cualidades intrínsecas que les permiten ser algo más que simples estructuras de datos. Lo más importante es que pueden emitir señales que a su vez pueden ser usadas tanto para reaccionar frente al cambio de datos introducidos por el usuario como para transferir los nuevos valores de forma transparente entre widgets ajustables.

Creando un ajuste

Los ajustes se pueden crear usando:

GtkObject *gtk_adjustment_new( gfloat value,
                               gfloat lower,
                               gfloat upper,
                               gfloat step_increment,
                               gfloat page_increment,
                               gfloat page_size );

El argumento value es el valor inicial que le queremos dar al ajuste. Normalmente se corresponde con las posiciones situadas más arriba y a la izquierda de un widget ajustable. El argumento lower especifica los valores más pequeños que el ajuste puede contener. A su vez con step_increment se especifica el valor más pequeño en el que se puede variar la magnitud en cuestión (valor de paso asociado), mientras que page_increment es el mayor. Con page_size se determina el valor visible de un widget.

Forma sencilla de usar los ajustes

Los widgets ajustables se pueden dividir en dos categorias diferentes, aquellos que necesitan saber las unidades de la cantidad almacenada y los que no. Este último grupo incluye los widgets de tamaño (barras deslizantes, escalas, barras de estado, o botones giratorios). Normalmente estos widgets son ajustados ``directamente'' por el usuario. Los argumentos lower y upper serán los limites dentro de los cuales el usuario puede manipular los ajustes. Por defecto sólo se modificará el value (valor) de un ajuste.

El otro grupo incluye los widgets de texto, la lista compuesta o la ventana con barra deslizante. Estos widgets usan valores en pixels para sus ajustes, y normalmente son ajustados ``indirectamente'' mediante barras deslizantes. Aunque todos los widgets pueden crear sus propios ajustes o usar otros creados por el programador con el segundo grupo suele ser conveniente dejarles que creen sus propios ajustes. Normalmente no tendrán en cuenta ninguno de los valores de un ajuste proporcionado por el programador, excepto value, pero los resultados son, en general, indefinidos (entiéndase que tendrá que leer el código fuente para saber que pasa con cada widget).

Probablemente ya se habrá dado cuenta de que como los widgets de texto (y todos los widgets del segundo grupo), insisten en establecer todos los valores excepto value, mientras que las barras deslizantes sólo modifican value, si se comparte un objeto de ajuste entre una barra deslizante y un widget de texto al manipular la barra se modificará el widget de texto. Ahora queda completamente demostrada la utilidad de los ajustes. Veamos un ejemplo:

  /* creamos un ajuste */
     viewport = gtk_viewport_new (NULL, NULL);
  /* lo usamos con la barra deslizante */
     vscrollbar = gtk_vscrollbar_new (gtk_viewport_get_vadjustment (viewport));

Descripción detallada de los ajustes

Puede que se esté preguntando cómo es posible crear sus propios controladores para responder a las modificaciones producidas por el usuario y cómo obtener el valor del ajuste hecho por este. Para aclarar esto y otras cosas vamos a estudiar la estructura del ajuste

struct _GtkAdjustment
{
  GtkData data;

  gfloat lower;
  gfloat upper;
  gfloat value;
  gfloat step_increment;
  gfloat page_increment;
  gfloat page_size;
};

Lo primero que hay que aclarar es que no hay ninguna macro o función de acceso que permita obtener el value de un GtkAdjustment, por lo que tendrá que hacerlo usted mismo. Tampoco se preocupe mucho porque la macro GTK_ADJUSTMENT (Object) comprueba los tipos durante el proceso de ejecución (como hacen todas las macros de GTK+ que sirven para comprobar los tipos). Cuando se establece el value de un ajuste normalmente se quiere que cualquier widget se entere del cambio producido. Para ello GTK+ posee una función especial:

void gtk_adjustment_set_value( GtkAdjustment *adjustment,
                               gfloat         value );

Tal y como se mencionó antes GtkAdjustment es una subclase de GtkObject y por tanto puede emitir señales. Así se consigue que se actualicen los valores de los ajustes cuando se comparten entre varios widgets. Por tanto todos los widgets ajustables deben conectar controladores de señales a sus señales del tipo value_changed. Esta es la definición de la señal como viene en struct _GtkAdjustmentClass

  void (* value_changed) (GtkAdjustment *adjustment);

Todos los widgets que usan GtkAdjustment deben emitir esta señal cuando cambie el valor de algún ajuste. Esto sucede cuando el usuario cambia algo o el programa modifica los ajustes mediante. Por ejemplo si queremos que rote una figura cuando modificamos un widget de escala habría que usar una respuesta como esta:

void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
{
  set_picture_rotation (picture, adj->value);
...

y conectarla con el ajuste del widget de escala mediante:

gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
                    GTK_SIGNAL_FUNC (cb_rotate_picture), picture);

\x{00BF}Qué pasa cuando un widget reconfigura los valores upper o lower (por ejemplo cuando se añade más texto)? Simplemente que se emite la señal changed, que debe ser parecida a:

  void (* changed)       (GtkAdjustment *adjustment);

Los widgets de tamaño normalmente conectan un controlador a esta señal, que cambia el aspecto de éste para reflejar el cambio. Por ejemplo el tamaño de la guía en una barra deslizante que se alarga o encoge según la inversa de la diferencia de los valores lower y upper.

Probablemente nunca tenga que conectar un controlador a esta señal a no ser que esté escribiendo un nuevo tipo de widget. Pero si cambia directamente alguno de los valores de GtkAdjustment debe hacer que se emita la siguiente señal para reconfigurar todos aquellos widgets que usen ese ajuste:

gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");

Widgets de selección de rango

Este tipo de widgets incluye a las barras de desplazamiento (scrollbar) y la menos conocida escala (scale). Ambos pueden ser usados para muchas cosas, pero como sus funciones y su implementación son muy parecidas los describimos al mismo tiempo. Principalmente se utilizan para permitirle al usuario escoger un valor dentro de un rango ya prefijado.

Todos los widgets de selección comparten elementos gráficos, cada uno de los cuales tiene su propia ventana X window y recibe eventos. Todos contienen una guía y un rectángulo para determinar la posición dentro de la guía (en una procesador de textos con entorno gráfico se encuentra situado a la derecha del texto y sirve para situarnos en las diferentes partes del texto). Con el ratón podemos subir o bajar el rectángulo, mientras que si hacemos `click' dentro de la guía, pero no sobre el rectángulo, este se mueve hacia donde hemos hecho el click. Dependiendo del botón pulsado el rectángulo se moverá hasta la posición del click o una cantidad prefijada de ante mano.

Tal y como se mencionó en “Ajustes” todos los widgets usados para seleccionar un rango estan asociados con un objeto de ajuste, a partir del cual calculan la longitud de la barra y su posición. Cuando el usuario manipula la barra de desplazamiento el widget cambiará el valor del ajuste.

El widget barra de desplazamiento

El widget barra de desplazamiento solamente debe utilizarse para hacer scroll sobre otro widget, como una lista, una caja de texto, o un viewport (y en muchos es más fácil utilizar el widget GtkScrolledWindow). Para el resto de los casos, debería utilizar los widgets de escala, ya que son más sencillos y potentes.

Hay dos tipos separados de barras de desplazamiento, según sea horizontal o vertical. Realmente no hay mucho que añadir. Puede crear estos widgets utilizar las funciones siguientes, definidas en <gtk/gtkhscrollbar.h> y <gtk/gtkvscrollbar.h>:

GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );

GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );

        

y esto es todo lo que hay, como puede comprobarse leyendo los ficheros de cabecera para estas funciones. El argumento adjustment puede ser un puntero a un ajuste ya existente, o puede ser NULL, en cuyo caso se creará uno. Es útil especificar NULL si quiere pasar el ajuste recién creado a la función constructora de algún otro widget (como por ejemplo el widget texto) que se ocupará de configurarlo correctamente por usted.

Creación de un widget de escala

Existen dos tipos de widgets de escala: GtkHScale (que es horizontal) y GtkVscale (vertical). Como funcionan de la misma manera los vamos a describir a la vez. Las funciones definidas en <gtk/gtkvscale.h> y <gtk/gtkhscale.h>, crean widgets de escala verticales y horizontales respectivamente.

	GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );

	GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );

	

El ajuste (adjustment) puede ser tanto un ajuste creado mediante gtk_adjustment_new() como NULL. En este último caso se crea un GtkAdjustment anónimo con todos sus valores iguales a 0.0. Si no ha quedado claro el uso de esta función consulte la sección “Ajustes” para una discusión más detallada.

Funciones y señales

Los widgets de escala pueden indicar su valor actual como un número. Su comportamiento por defecto es mostrar este valor, pero se puede modificar usando:

void gtk_scale_set_draw_value( GtkScale *scale,
                               gint      draw_value );

Los valores posibles de draw_value son TRUE o FALSE. Con el primero se muestra el valor y con el segundo no.

El valor mostrado por un widget de escala por defecto se redondea a un valor decimal (igual que con value en un GtkAdjustment). Se puede cambiar con:

void gtk_scale_set_digits( GtkScale *scale,
                            gint     digits );

donde digits es el número de posiciones decimales que se quiera. En la práctica sólo se mostrarán 13 como máximo.

Por último, el valor se puede dibujar en diferentes posiciones con respecto a la posición del rectangulo que hay dentro de la guía:

void gtk_scale_set_value_pos( GtkScale        *scale,
                              GtkPositionType  pos );

Si ha leido la sección acerca del widget libro de notas entonces ya conoce cuales son los valores posibles de pos. Estan definidos en <gtk/gtkscale.h> como enum GtkPositionType y son auto explicatorios. Si se escoge un lateral de la guía, entonces seguirá al rectángulo a lo largo de la guía.

Todas las funcioenes precedentes se encuentran definidas en: <gtk/gtkscale.h>.

Funciones comunes

La descripción interna de la clase GtkRange es bastante complicada, pero al igual que con el resto de las «clases base» sólo es interesante si se quiere «hackear». Casi todas las señales y funciones sólo son útiles para desarrollar derivados. Para un usuario normal las funciones interesantes son aquellas definidas en: <gtk/gtkrange.h> y funcionan igual en todos los widgets de rango.

Estableciendo cada cúanto se actualizan

La política de actualización de un widget define en que puntos de la interacción con el usuario debe cambiar el valor value en su GtkAdjustment y emitir la señal «value_changed». Las actualizaciones definidas en <gtk/gtkenums.h> como enum GtkUpdateType, son:

  • GTK_UPDATE_POLICY_CONTINUOUS - Este es el valor por defecto.La señal «value_changed» se emite continuamente, por ejemplo cuando la barra deslizante se mueve incluso aunque sea un poquito.

  • GTK_UPDATE_POLICY_DISCONTINUOUS - La señal «value_changed» sólo se emite cuando se ha parado de mover la barra y el usuario ha soltado el botón del ratón.

  • GTK_UPDATE_POLICY_DELAYED - La señal sólo se emite cuando el usuario suelta el botón del ratón o si la barra no se mueve durante un periodo largo de tiempo.

Para establecer la política de actualización se usa la conversión definida en la macro

void gtk_range_set_update_policy( GtkRange      *range,
	                          GtkUpdateType  policy) ;

Obteniendo y estableciendo Ajustes

Para obtener o establecer el ajuste de un widget de rango se usa:

GtkAdjustment* gtk_range_get_adjustment( GtkRange *range );

void gtk_range_set_adjustment( GtkRange      *range,
                               GtkAdjustment *adjustment );

La función gtk_range_get_adjustment() devuelve un puntero al ajuste al que range esté conectado.

La función gtk_range_set_adjustment() no hace nada si se le pasa como argumento el valor range del ajuste que esta siendo usado (aunque se haya modificado algún valor). En el caso de que sea un ajuste nuevo (GtkAdjustment) dejará de usar el antiguo (probablemente lo destruirá) y conectará las señales apropiadas al nuevo. A continuación llamará a la función gtk_range_adjustment_changed() que en teoría recalculará el tamaño y/o la posición de la barra, redibujándola en caso de que sea necesario. Tal y como se mencionó en la sección de los ajustes si se quiere reusar el mismo GtkAdjustment cuando se modifican sus valores se debe emitir la señal «changed». Por ejemplo:

	gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");

Enlaces con el teclado y el ratón

Todos los widgets de rango reaccionan más o menos de la misma manera a las pulsaciones del ratón. Al pulsar el botón 1 sobre el rectángulo de la barra el value del ajuste aumentará o disminuirá según page_increment. Con el botón 2 la barra se desplazará al punto en el que el botón fue pulsado. Con cada pulsación de cualquier botón sobre las flechas el valor del ajuste se modifica una cantidad igual a step_increment.

Acostumbrarse a que tanto las barras deslizantes como los widgets de escala puedan tomar la atención del teclado puede ser un proceso largo. Si que se cree que los usuarios no lo van a entender se puede anular mediante la función GTK_WIDGET_UNSET_FLAGS y con GTK_CAN_FOCUS como argumento:

	GTK_WIDGET_UNSET_FLAGS (scrollbar, GTK_CAN_FOCUS);

Los enlaces entre teclas (que sólo estan activos cuando el widget tiene la atención (focus)) se comportan de manera diferente para los widgets de rango horizontales que para los verticales. También son diferentes para los widgets de escala y para las barras deslizantes. (Simplemente para evitar confusiones entre las teclas de las barras deslizantes horizontales y verticales, ya que ambas actúan sobre la misma área)

Widgets de rango vertical

Todos los widgets de rango pueden ser manipulados con las teclas arriba, abajo, Re Pág, Av Pág. Las flechas mueven las barras la cantidad fijada mediante step_increment, mientras que Re Pág y Av Pag lo hacen según page_increment.

El usuario también puede mover la barra de un extremo al otro de la guía mediante el teclado. Con el widget GtkVScale podemos ir a los extremos utilizando las teclas Inicio y Final mientras que con el widget GtkVScrollbar habrá que utilizar Control-Re Pág y Control-Av Pág.

Widgets de rango horizontal

Las teclas izquierda y derecha funcionan tal y como espera que funcionen en estos widgets: mueven la barra una cantidad dada por step_increment. A su vez Inicio y Final sirven para pasar de un extremo al otro de la guía. Para el widget GtkHScale el mover la barra una cantidad dada por page_increment se consigue mediante Control-Izquierda y Control-derecha, mientras que para el widget GtkHScrollbar se consigue con Control-Inicio y Control-Final.

Ejemplo

Este ejemplo es una versión modificada del test «range controls» que a su vez forma parte de testgtk.c. Simplemente dibuja una ventana con tres widgets de rango conectados al mismo ajuste, y un conjunto de controles para ajustar algunos de los parámetros ya mencionados. Así se consigue ver como funcionan estos widgets al ser manipulados por el usuario.


#include <gtk/gtk.h>

GtkWidget *hscale, *vscale;

void cb_pos_menu_select( GtkWidget       *item,
                         GtkPositionType  pos )
{
    /* Establece el valor position en los widgets de escala */
    gtk_scale_set_value_pos (GTK_SCALE (hscale), pos);
    gtk_scale_set_value_pos (GTK_SCALE (vscale), pos);
}

void cb_update_menu_select( GtkWidget     *item,
                            GtkUpdateType  policy )
{
    /* Establece la política de actualización para los widgets
     * de escala */
    gtk_range_set_update_policy (GTK_RANGE (hscale), policy);
    gtk_range_set_update_policy (GTK_RANGE (vscale), policy);
}

void cb_digits_scale( GtkAdjustment *adj )
{
    /* Establece el número de cifras decimales a las que se
     * redondeará adj->value */
    gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
    gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
}

void cb_page_size( GtkAdjustment *get,
                   GtkAdjustment *set )
{
    /* Establece el tamaño de la página y el incremento del
     * ajuste al valor especificado en la escala "Page Size" */
    set->page_size = get->value;
    set->page_increment = get->value;
    /* Ahora emite la señal "changed" para reconfigurar todos los
     * widgets que están enlazados a este ajuste */
    gtk_signal_emit_by_name (GTK_OBJECT (set), "changed");
}

void cb_draw_value( GtkToggleButton *boton )
{
    /* Activa o desactiva el valor display en los widgets de escala
     * dependiendo del estado del botón de comprobación */
    gtk_scale_set_draw_value (GTK_SCALE (hscale), boton->active);
    gtk_scale_set_draw_value (GTK_SCALE (vscale), boton->active);
}

/* Funciones varias */

GtkWidget *make_menu_item( gchar         *name,
                           GtkSignalFunc  callback,
                           gpointer       data )
{
    GtkWidget *item;

    item = gtk_menu_item_new_with_label (name);
    gtk_signal_connect (GTK_OBJECT (item), "activate",
                        callback, data);
    gtk_widget_show (item);

    return(item);
}

void scale_set_default_values( GtkScale *scale )
{
    gtk_range_set_update_policy (GTK_RANGE (scale),
                                 GTK_UPDATE_CONTINUOUS);
    gtk_scale_set_digits (scale, 1);
    gtk_scale_set_value_pos (scale, GTK_POS_TOP);
    gtk_scale_set_draw_value (scale, TRUE);
}

/* crea la ventana principal */

void create_range_controls( void )
{
    GtkWidget *ventana;
    GtkWidget *caja1, *caja2, *caja3;
    GtkWidget *boton;
    GtkWidget *scrollbar;
    GtkWidget *separator;
    GtkWidget *opt, *menu, *item;
    GtkWidget *etiqueta;
    GtkWidget *scale;
    GtkObject *adj1, *adj2;

    /* creación estándar de una ventana */
    ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
                        GTK_SIGNAL_FUNC(gtk_main_quit),
                        NULL);
    gtk_window_set_title (GTK_WINDOW (ventana), "range controls");

    caja1 = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (ventana), caja1);
    gtk_widget_show (caja1);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);
    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    /* value, lower, upper, step_increment, page_increment, page_size */
    /* Observe que el valor de page_size solo sirve para los widgets
     * barras de desplazamiento (scrollbar), y que el valor más
     * alto que obtendrá será (upper - page_size). */
    adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0);

    vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1));
    scale_set_default_values (GTK_SCALE (vscale));
    gtk_box_pack_start (GTK_BOX (caja2), vscale, TRUE, TRUE, 0);
    gtk_widget_show (vscale);

    caja3 = gtk_vbox_new (FALSE, 10);
    gtk_box_pack_start (GTK_BOX (caja2), caja3, TRUE, TRUE, 0);
    gtk_widget_show (caja3);

    /* Reutilizamos el mismo ajuste */
    hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
    gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30);
    scale_set_default_values (GTK_SCALE (hscale));
    gtk_box_pack_start (GTK_BOX (caja3), hscale, TRUE, TRUE, 0);
    gtk_widget_show (hscale);

    /* Reutilizamos de nuevo el mismo ajuste */
    scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1));
    /* Observe que con esto conseguimos que la escala siempre se
     * actualice de una forma continua cuando se mueva la barra de
     * desplazamiento */
    gtk_range_set_update_policy (GTK_RANGE (scrollbar),
                                 GTK_UPDATE_CONTINUOUS);
    gtk_box_pack_start (GTK_BOX (caja3), scrollbar, TRUE, TRUE, 0);
    gtk_widget_show (scrollbar);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);
    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    /* Un botón para comprobar si el valor se muestra o no*/
    boton = gtk_check_button_new_with_label("Display value on scale widgets");
    gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
    gtk_signal_connect (GTK_OBJECT (boton), "toggled",
                        GTK_SIGNAL_FUNC(cb_draw_value), NULL);
    gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
    gtk_widget_show (boton);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);

    /* Una opción en el menú para cambiar la posición del
     * valor */
    etiqueta = gtk_label_new ("Scale Value Position:");
    gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
    gtk_widget_show (etiqueta);

    opt = gtk_option_menu_new();
    menu = gtk_menu_new();

    item = make_menu_item ("Top",
                           GTK_SIGNAL_FUNC(cb_pos_menu_select),
                           GINT_TO_POINTER (GTK_POS_TOP));
    gtk_menu_append (GTK_MENU (menu), item);

    item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                           GINT_TO_POINTER (GTK_POS_BOTTOM));
    gtk_menu_append (GTK_MENU (menu), item);

    item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                           GINT_TO_POINTER (GTK_POS_LEFT));
    gtk_menu_append (GTK_MENU (menu), item);

    item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                            GINT_TO_POINTER (GTK_POS_RIGHT));
    gtk_menu_append (GTK_MENU (menu), item);

    gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
    gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
    gtk_widget_show (opt);

    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);

    /* Sí, otra opción de menú, esta vez para la política
     * de actualización de los widgets */
    etiqueta = gtk_label_new ("Scale Update Policy:");
    gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
    gtk_widget_show (etiqueta);

    opt = gtk_option_menu_new();
    menu = gtk_menu_new();

    item = make_menu_item ("Continuous",
                           GTK_SIGNAL_FUNC (cb_update_menu_select),
                           GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS));
    gtk_menu_append (GTK_MENU (menu), item);

    item = make_menu_item ("Discontinuous",
                            GTK_SIGNAL_FUNC (cb_update_menu_select),
                            GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS));
    gtk_menu_append (GTK_MENU (menu), item);

    item = make_menu_item ("Delayed",
                           GTK_SIGNAL_FUNC (cb_update_menu_select),
                           GINT_TO_POINTER (GTK_UPDATE_DELAYED));
    gtk_menu_append (GTK_MENU (menu), item);

    gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
    gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
    gtk_widget_show (opt);

    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);

    /* Un widget GtkHScale para ajustar el número de dígitos en
     * la escala. */
    etiqueta = gtk_label_new ("Scale Digits:");
    gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
    gtk_widget_show (etiqueta);

    adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
    gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
                        GTK_SIGNAL_FUNC (cb_digits_scale), NULL);
    scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
    gtk_scale_set_digits (GTK_SCALE (scale), 0);
    gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
    gtk_widget_show (scale);

    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    caja2 = gtk_hbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);

    /* Y un último widget GtkHScale para ajustar el tamaño de la
     * página de la barra de desplazamiento. */
    etiqueta = gtk_label_new ("Scrollbar Page Size:");
    gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
    gtk_widget_show (etiqueta);

    adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
    gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
                        GTK_SIGNAL_FUNC (cb_page_size), adj1);
    scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
    gtk_scale_set_digits (GTK_SCALE (scale), 0);
    gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
    gtk_widget_show (scale);

    gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
    gtk_widget_show (caja2);

    separator = gtk_hseparator_new ();
    gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
    gtk_widget_show (separator);

    caja2 = gtk_vbox_new (FALSE, 10);
    gtk_container_border_width (GTK_CONTAINER (caja2), 10);
    gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
    gtk_widget_show (caja2);

    boton = gtk_button_new_with_label ("Quit");
    gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
                               GTK_SIGNAL_FUNC(gtk_main_quit),
                               NULL);
    gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
    GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (boton);
    gtk_widget_show (boton);

    gtk_widget_show (ventana);
}

int main( int   argc,
          char *argv[] )
{
    gtk_init(&argc, &argv);

    create_range_controls();

    gtk_main();

    return(0);
}

    

Observe que el programa no llama a gtk_signal_connect para conectar el "delete_event", y que sólo conecta la señal "destroy". Con esto seguimos realizando la función deseada, ya que un "delete_event" no manejado desemboca en una señal "destroy" para la ventana.

GtkSpinButton

El widget botón spin se utiliza normalmente para permitir que el usuario elija un valor de un rango de valores. Consiste en una caja para la entrada de texto con una flecha para arriba y otra para abajo justo al lado de la caja. Si utilizamos alguna de las flechas haremos que el valor suba o baje dentro del rango de los valores posibles. También podemos introducir directamente un valor específico (utilizando la caja de texto).

El widget botón spin permite tener valores con un número de cifras decimales (o sin cifras decimales) y la posibilidad de incrementarlo/decrementarlo en pasos configurables. La acción de matener pulsado uno de los botones puede resultar (es opcional) en una aceleración del cambio en el valor, de acuerdo con el tiempo que se mantenga pulsado.

El botón spin utiliza un objeto Ajuste para conservar la información referente al rango de valores que puede tomar el botón spin. Esto hace que el widget botón spin sea muy poderoso.

Recuerde que un widget ajuste puede crearse con la siguiente función, que ilustra la información que se guarda:

	GtkObject *gtk_adjustment_new( gfloat valor,
                               gfloat inferior,
                               gfloat superior,
                               gfloat paso,
                               gfloat incremento_pagina,
                               gfloat tamano_pagina );
        

Estos atributos de un ajuste se utilizan en un botón spin de la forma siguiente:

  • valor: valor inicial del botón spin

  • inferior: valor inferior del rango

  • superior: valor superior del rango

  • paso: valor a incrementar/decrementar cuando pulsemos el botón 1 en una flecha

  • incremento_pagina: valor a incrementar/decrementar cuando pulsemos el botón 2 en una flecha

  • tamano_pagina: no se utiliza

Además, se puede utilizar el botón 3 para saltar directamente a los valores superior o inferior cuando se pulsa en una de las flechas. Veamos como crear un botón spin:

	  GtkWidget *gtk_spin_button_new( GtkAdjustment *ajuste,
                                gfloat         aceleracion,
                                guint          digitos );
	

El argumento aceleracion toma un valor entre 0.0 y 1.0 e indica la aceleración que tendrá el botón spin. El argumento digitos especifica el número de cifras decimales con que se mostrará el valor.

Se puede reconfigurar un botón spin después de su creación utilizando la función:

	void gtk_spin_button_configure( GtkSpinButton *boton_spin,
                                GtkAdjustment *ajuste,
                                gfloat         aceleracion,
                                guint          digitos );
	

El argumento boton_spin especifica el botón spin que va a reconfigurarse. El resto de argumentos son los que acabamos de explicar.

Podemos establecer y obtener el ajuste utilizando las dos funciones siguientes:

	void gtk_spin_button_set_adjustment( GtkSpinButton  *boton_spin,
                                     GtkAdjustment  *ajuste );
	

	GtkAdjustment *gtk_spin_button_get_adjustment( GtkSpinButton *boton_spin );
	

El número de cifras decimales también puede alterarse utilizando:

	void gtk_spin_button_set_digits( GtkSpinButton *boton_spin,
                                 guint          digitos) ;
	

El valor que un botón spin está mostrando actualmente puede cambiarse utilizando las siguientes funciones:

	void gtk_spin_button_set_value( GtkSpinButton *boton_spin,
                                gfloat         valor );
	

El valor actual de un botón spin puede obtenerse como un entero o como un flotante con las funciones siguientes:

		gfloat gtk_spin_button_get_value_as_float( GtkSpinButton *boton_spin );

		gint gtk_spin_button_get_value_as_int( GtkSpinButton *boton_spin );
	

Si quiere alterar el valor de un spin de forma relativa a su valor actual, puede utilizar la siguiente función:

	void gtk_spin_button_spin( GtkSpinButton *boton_spin,
                           GtkSpinType    direccion,
                           gfloat         incremento );
	

El parámetro direccion puede tomar uno de los valores siguientes:

  • GTK_SPIN_STEP_FORWARD

  • GTK_SPIN_STEP_BACKWARD

  • GTK_SPIN_PAGE_FORWARD

  • GTK_SPIN_PAGE_BACKWARD

  • GTK_SPIN_HOME

  • GTK_SPIN_END

  • GTK_SPIN_USER_DEFINED

Trataré de explicar todas las posibilidades que ofrece esta función. Algunos de los valores que puede utilizar direccion hacen que se utilicen valores que están almacenados en el objeto Ajuste que está asociado con el botón spin.

GTK_SPIN_STEP_FORWARD y GTK_SPIN_STEP_BACKWARD aumentan o disminuyen (respectivamente) el valor del botón spin por la cantidad especificada por incremento, a menos que incremento sea igual a 0, en cuyo caso el valor se aumentará o disminuirá por el valor especificado en paso dentro del Ajuste.

GTK_SPIN_PAGE_FORWARD y GTK_SPIN_PAGE_BACKWARD sencillamente alteran el valor del botón spin por la cantidad incremento.

GTK_SPIN_HOME hace que el botón spin tenga el mismo valor que el valor inferior del rango Ajuste.

GTK_SPIN_END hace que el botón spin tenga el mismo valor que el valor superior del rango Ajuste.

GTK_SPIN_USER_DEFINED cambia el valor del botón spin por la cantidad especificada.

Ahora vamos a dejar de lado las funciones para establecer y obtener el rango de los atributos del botón spin, y vamos a pasar a las funciones que afectan a la apariencia y al comportamiento del widget botón spin en sí mismo.

La primera de estas funciones se utiliza para restringir el contenido de la caja de texto de un botón spin a un valor numérico. Esto evita que un usuario introduzca cualquier valor no númerico.

	void gtk_spin_button_set_numeric( GtkSpinButton *boton_spin,
                                  gboolean       numerico );
	

Puede indicar si un botón spin pasará del límite superior al inferior utilizando la siguiente función:

	void gtk_spin_button_set_wrap( GtkSpinButton *boton_spin,
				       gboolean       wrap );
	

Puede hacer que un botón spin redondee su valor al paso más cercano, que se indica cuando creamos el Ajuste que se utiliza con el botón spin. Para hacer que redondee tenemos que utilizar la función siguiente:

	void gtk_spin_button_set_snap_to_ticks( GtkSpinButton  *boton_spin,
						gboolean        redondear );
	

Para política de actualización de un botón spin puede cambiarse con la siguiente función:

	void gtk_spin_button_set_update_policy( GtkSpinButton  *boton_spin,
					    GtkSpinButtonUpdatePolicy politica );
	

Los valores posibles de politica son o GTK_UPDATE_ALWAYS o GTK_UPDATE_IF_VALID.

Estas políticas afectan al comportamiento de un botón spin cuando se lee el texto insertado en la caja de texto y se sincroniza con los valores del Ajuste.

En el caso de GTK_UPDATE_IF_VALID el valor de un botón spin cambiará si el texto introducido es un valor numérico contenido dentro del rango especificado por el Ajuste. En caso contrario el texto introducido se convierte al valor del botón spin.

En caso de utilizar GTK_UPDATE_ALWAYS se ignorarán los errores que puedan ocurrir en la conversión del texto en un valor numérico.

El aspecto de los botones utilizados en un botón spin pueden cambiarse utilizando las siguientes funciones:

	void gtk_spin_button_set_shadow_type( GtkSpinButton *boton_spin,
					      GtkShadowType  tipo_sombra );
	

Como siempre, el tipo_sombra puede ser uno de los siguientes:

  • GTK_SHADOW_IN

  • GTK_SHADOW_OUT

  • GTK_SHADOW_ETCHED_IN

  • GTK_SHADOW_ETCHED_OUT

Finalmente, puede pedir de forma explícita que un botón spin se actualice a sí mismo:

	void gtk_spin_button_update( GtkSpinButton  *boton_spin );
	

Es hora de un nuevo ejemplo.

Figura 9. Todos los ajustes de un botón spin al descubierto

Todos los ajustes de un botón spin al descubierto


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

static GtkWidget *spinner1;

static void toggle_snap( GtkWidget     *widget,
                         GtkSpinButton *spin )
{
  gtk_spin_button_set_snap_to_ticks (spin, GTK_TOGGLE_BUTTON (widget)->active);
}

static void toggle_numeric( GtkWidget *widget,
                            GtkSpinButton *spin )
{
  gtk_spin_button_set_numeric (spin, GTK_TOGGLE_BUTTON (widget)->active);
}

static void change_digits( GtkWidget *widget,
                           GtkSpinButton *spin )
{
  gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner1),
                              gtk_spin_button_get_value_as_int (spin));
}

static void get_value( GtkWidget *widget,
                       gpointer data )
{
  gchar *buf;
  GtkLabel *label;
  GtkSpinButton *spin;

  spin = GTK_SPIN_BUTTON (spinner1);
  label = GTK_LABEL (g_object_get_data (G_OBJECT (widget), "user_data"));
  if (GPOINTER_TO_INT (data) == 1)
    buf = g_strdup_printf ("%d", gtk_spin_button_get_value_as_int (spin));
  else
    buf = g_strdup_printf ("%0.*f", spin->digits,
                           gtk_spin_button_get_value (spin));
  gtk_label_set_text (label, buf);
  g_free (buf);
}


int main( int   argc,
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *frame;
  GtkWidget *hbox;
  GtkWidget *main_vbox;
  GtkWidget *vbox;
  GtkWidget *vbox2;
  GtkWidget *spinner2;
  GtkWidget *spinner;
  GtkWidget *button;
  GtkWidget *label;
  GtkWidget *val_label;
  GtkAdjustment *adj;

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

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

  gtk_window_set_title (GTK_WINDOW (window), "Spin Button");

  main_vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);
  
  frame = gtk_frame_new ("Not accelerated");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
  
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  
  /* Day, month, year spinners */
  
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
  
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
  
  label = gtk_label_new ("Day :");
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0);
  
  adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 31.0, 1.0,
                                              5.0, 0.0);
  spinner = gtk_spin_button_new (adj, 0, 0);
  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
  
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
  
  label = gtk_label_new ("Month :");
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0);
  
  adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 12.0, 1.0,
                                              5.0, 0.0);
  spinner = gtk_spin_button_new (adj, 0, 0);
  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
  
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
  
  label = gtk_label_new ("Year :");
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0);
  
  adj = (GtkAdjustment *) gtk_adjustment_new (1998.0, 0.0, 2100.0,
                                              1.0, 100.0, 0.0);
  spinner = gtk_spin_button_new (adj, 0, 0);
  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), FALSE);
  gtk_widget_set_size_request (spinner, 55, -1);
  gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
  
  frame = gtk_frame_new ("Accelerated");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
  
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
  
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
  
  label = gtk_label_new ("Value :");
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0);
  
  adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -10000.0, 10000.0,
                                              0.5, 100.0, 0.0);
  spinner1 = gtk_spin_button_new (adj, 1.0, 2);
  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner1), TRUE);
  gtk_widget_set_size_request (spinner1, 100, -1);
  gtk_box_pack_start (GTK_BOX (vbox2), spinner1, FALSE, TRUE, 0);
  
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
  
  label = gtk_label_new ("Digits :");
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0);
  
  adj = (GtkAdjustment *) gtk_adjustment_new (2, 1, 5, 1, 1, 0);
  spinner2 = gtk_spin_button_new (adj, 0.0, 0);
  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner2), TRUE);
  g_signal_connect (G_OBJECT (adj), "value_changed",
                    G_CALLBACK (change_digits),
                    (gpointer) spinner2);
  gtk_box_pack_start (GTK_BOX (vbox2), spinner2, FALSE, TRUE, 0);
  
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
  
  button = gtk_check_button_new_with_label ("Snap to 0.5-ticks");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (toggle_snap),
                    (gpointer) spinner1);
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  
  button = gtk_check_button_new_with_label ("Numeric only input mode");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (toggle_numeric),
                    (gpointer) spinner1);
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  
  val_label = gtk_label_new ("");
  
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
  button = gtk_button_new_with_label ("Value as Int");
  g_object_set_data (G_OBJECT (button), "user_data", val_label);
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (get_value),
                    GINT_TO_POINTER (1));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 5);
  
  button = gtk_button_new_with_label ("Value as Float");
  g_object_set_data (G_OBJECT (button), "user_data", val_label);
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (get_value),
                    GINT_TO_POINTER (2));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 5);
  
  gtk_box_pack_start (GTK_BOX (vbox), val_label, TRUE, TRUE, 0);
  gtk_label_set_text (GTK_LABEL (val_label), "0");
  
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
  
  button = gtk_button_new_with_label ("Close");
  g_signal_connect_swapped (G_OBJECT (button), "clicked",
                            G_CALLBACK (gtk_widget_destroy),
                            G_OBJECT (window));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 5);

  gtk_widget_show_all (window);

  /* Enter the event loop */
  gtk_main ();
    
  return 0;
}
		

Imágenes (GtkImage)

GtkImage es un widget que nos permitirá mostrar una imagen por pantalla. Existen varios tipos de objeto que pueden ser mostrados como una imagen; lo más típico es cargar una imagen ya preexistente desde fichero, y mostrarla a continuación. Existe una función muy útil para hacer esto, gtk_image_new_from_file(), que podemos usar así:

	  GtkWidget *image;
	  image = gtk_image_new_from_file ("myfile.png");
	

Internamente, gtk_image_new_from_file está haciendo uso de GdkPixbuf para cargar un buffer de pixels, proveniendo este buffer de imagenes de tipo GIF, PNG, JPEG, BMP, etc.

Si en el ejemplo anterior el fichero no se carga correctamente, la imagen contendrá un icono de "imagen rota" similiar al que muestran muchos navegadores web cuando no encuentran en el servidor la imagen a mostrar. Si deseáramos gestionar nosotros mismos posibles errores al cargar una imagen, por ejemplo, si quisiéramos mostrar un mensaje de error cuando detectaramos algún problema, deberíamos cargar la imagen con gdk_pixbuf_new_from_file(), y luego crear el widget GtkImage con gtk_image_new_from_pixbuf().

El fichero de imagen que cargamos podría contener alguna animación y, en ese caso, GtkImage también la mostrará, usando GdkPixbufAnimation en lugar de una imagen estática.

GtkImage es una subclase de GtkMisc, por lo que es posible alinear la imagen (center, left, right) y añadirle padding, con los métodos de GtkMisc.

Veamos un pequeño ejemplo de programa que carga una pequeña imagen de nombre gnome.png y la muestra en una ventana:

En nuestro programa de ejemplo, crearemos un objeto GtkImage usando la función gtk_image_new_from_file(). El objeto GtkImage cargará nuestra imagen automáticamente, usando internamente GdkPixbuf:

		#include <gtk/gtk.h>
		
		int
		main (int argc, char **argv) 
		{
		  GtkWidget *window;
		  GtkWidget *image;   
		
		  gtk_init (&argc, &argv);
		  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
		  g_signal_connect (G_OBJECT (window), "destroy", gtk_main_quit, NULL);
		
		  image = gtk_image_new_from_file ("gnome.gif");
		  gtk_container_add (GTK_CONTAINER (window), image);
		
		  gtk_widget_show_all (window);
			
		  gtk_main ();
		    
		  return 0;
		}
    

Figura 10. Resultado de ejecutar el programa GtkImage.c

Resultado de ejecutar el programa GtkImage.c

GtkImage es un widget sin ventana asoaciada, (no tiene un GdkWindow propio), por lo que, por defecto, no recibe eventos. Si deseamos programar algo con un GtkImage que reciba eventos, como clics de botón, deberemos poner la imagen dentro de GtkEventBox, y luego conectar la imagen a los eventos de dicha caja. Veamos un ejemplo:

  static gboolean
  button_press_callback (GtkWidget      *event_box, 
                         GdkEventButton *event,
                         gpointer        data)
  {
    g_print ("Event box clicked at coordinates %f,%f\n", 
             event->x, event->y);

    /* Returning TRUE means we handled the event, so the signal 
     * emission should be stopped (don't call any further 
     * callbacks that may be connected). Return FALSE
     * to continue invoking callbacks.
     */
    return TRUE;
  }

  static GtkWidget*
  create_image (void)
  {
    GtkWidget *image;
    GtkWidget *event_box;

    image = gtk_image_new_from_file ("myfile.png");

    event_box = gtk_event_box_new ();

    gtk_container_add (GTK_CONTAINER (event_box), image);

    g_signal_connect (G_OBJECT (event_box), 
                      "button_press_event",
                      G_CALLBACK (button_press_callback),
                      image);

    return image;
  }
  

Al gestionar eventos generados en un GtkEventBox, recuerda que las coordenadas en la imagen podrían ser diferentes a las de la caja, debido al alineamiento y el padding de la imagen dentro de la caja. La forma más simple de arreglar este detalle consiste en ajustar el alineamiento a 0.0 (izquierda/arriba), y establecer el padding a cero. En ese caso, el origen de la imagen será el mismo que el origen de la caja que recibe eventos.

En ocasiones, una aplicación podría querer evitar tener dependencias de ficheros de datos externos, como los ficheros de imágenes que carga dentro de un menú. En este caso, GTK+ ofrece un programa para evitar estas dependencias: gdk-pixbuf-csource. Este programa permite convertir una imagen en una declaración de variable en C, que luego puede ser cargada en un objeto GdkPixbuf, usando la función gdk_pixbuf_new_from_inline().

Barras de progreso (GtkProgressBar)

Las barras de progreso se usan para mostrar el estado de una operación. Son muy fáciles de usar, como puedes observar con el códig que mostramos a continuación. Pero comencemos primero con las llamadas para crear una nueva barra de progreso.

GtkWidget *gtk_progress_bar_new( void );

Ahora que se ha creado la barra de progreso, podremos usarla con funciones como la siguiente:

void gtk_progress_bar_set_fraction ( GtkProgressBar *pbar,
                                     gdouble        fraction );

El primer argumento es la barra de progreso con la que deseas operar, y el segundo argumento es la cantidad "completada", es decir, la cantidad de relleno de la barra de progreso, un valor de 0 a 100%. Se le pasa como parámetro a la función, en forma de un número real entre 0 y 1.

GTK v1.2 ha añadido nueva fucionalidad a la barra de progreso que le permite mostrar su valor de diferentes formas, e informar al usuario de su valor actual y su rango.

Una barra de progreso puede ser definida con una orientación determinada, usando la función:

void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
                                       GtkProgressBarOrientation orientation );

El argumento orientación puede tomar cualquiera de los siguientes valores, para indicar la dirección en la que dicha barra de progreso se mueve:

  • GTK_PROGRESS_LEFT_TO_RIGHT

  • GTK_PROGRESS_RIGHT_TO_LEFT

  • GTK_PROGRESS_BOTTOM_TO_TOP

  • GTK_PROGRESS_TOP_TO_BOTTOM

Además de indicar la cantidad de progreso ocurrido, la barra de progreso puede definirse simplemente para indicar que hay algún tipo de actividad. Esto puede ser útil en situaciones donde el progreso realizado no puede ser medido en un rango de valores. La siguiente función indica que se ha realizado algun tipo de progreso:

void gtk_progress_bar_pulse ( GtkProgressBar *progress );

Este tipo de barra permite definir el tamañoo del paso que se dibujará en cada pulso:

void gtk_progress_bar_set_pulse_step( GtkProgressBar *pbar,
                                      gdouble         fraction );

Cuando no se está en modo actividad, la barra de progreso también puede mostrar una cadena de texto configurable, usando la siguiente función:

void gtk_progress_bar_set_text( GtkProgressBar *progress,
                                const gchar    *text );

Obsérvese que

 gtk_progress_set_text() 

no soporta el formateo de texto usando printf() como lo hacía la barra de la versión GTK+ 1.2.

Puedes evitar mostrar esta cadena de texto llamando a la función

gtk_progress_bar_set_text()

otra vez con NULL como segundo argumento.

Puedes obtener la cadena de texto actualmente en uso por la barra de progreso usando la siguiente función (no liberar la memoria de la cadena devuelta):

 const gchar *gtk_progress_bar_get_text( GtkProgressBar *pbar ); 

Las barras de progreso se usan normalmente con timeouts u otras funciones para dar la sensación de multitarea (ver sección sobre "Timeouts, I/O y funciones Idle). Todas ellas usarán gtk_progress_bar_set_fraction() ó gtk_progress_bar_pulse de la misma forma.

En el siguiente ejemplo podemos ver cómo mostrar una barra de progreso, actualizada usando timeouts. Este código muestra cómo resetear la barra de progreso.

Figura 11. Barra de progreso, actualizada mediante el uso de timeouts.

Barra de progreso, actualizada mediante el uso de timeouts.


#include <gtk/gtk.h>

typedef struct _ProgressData {
  GtkWidget *window;
  GtkWidget *pbar;
  int timer;
  gboolean activity_mode;
} ProgressData;

/* Update the value of the progress bar so that we get
 * some movement */
static gboolean progress_timeout( gpointer data )
{
  ProgressData *pdata = (ProgressData *)data;
  gdouble new_val;
  
  if (pdata->activity_mode) 
    gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pdata->pbar));
  else 
    {
      /* Calculate the value of the progress bar using the
       * value range set in the adjustment object */
      
      new_val = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (pdata->pbar)) + 0.01;
      
      if (new_val > 1.0)
        new_val = 0.0;
      
      /* Set the new value */
      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pdata->pbar), new_val);
    }
  
  /* As this is a timeout function, return TRUE so that it
   * continues to get called */
  return TRUE;
} 

/* Callback that toggles the text display within the progress bar trough */
static void toggle_show_text( GtkWidget    *widget,
                              ProgressData *pdata )
{
  const gchar *text;
  
  text = gtk_progress_bar_get_text (GTK_PROGRESS_BAR (pdata->pbar));
  if (text && *text)
    gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pdata->pbar), "");
  else 
    gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pdata->pbar), "some text");
}

/* Callback that toggles the activity mode of the progress bar */
static void toggle_activity_mode( GtkWidget    *widget,
                                  ProgressData *pdata )
{
  pdata->activity_mode = !pdata->activity_mode;
  if (pdata->activity_mode) 
      gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pdata->pbar));
  else
      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pdata->pbar), 0.0);
}

 
/* Callback that toggles the orientation of the progress bar */
static void toggle_orientation( GtkWidget    *widget,
                                ProgressData *pdata )
{
  switch (gtk_progress_bar_get_orientation (GTK_PROGRESS_BAR (pdata->pbar))) {
  case GTK_PROGRESS_LEFT_TO_RIGHT:
    gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (pdata->pbar), 
                                      GTK_PROGRESS_RIGHT_TO_LEFT);
    break;
  case GTK_PROGRESS_RIGHT_TO_LEFT:
    gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (pdata->pbar), 
                                      GTK_PROGRESS_LEFT_TO_RIGHT);
    break;
  default:;
    /* do nothing */
  }
}

 
/* Clean up allocated memory and remove the timer */
static void destroy_progress( GtkWidget    *widget,
                              ProgressData *pdata)
{
    g_source_remove (pdata->timer);
    pdata->timer = 0;
    pdata->window = NULL;
    g_free (pdata);
    gtk_main_quit ();
}

int main( int   argc,
          char *argv[])
{
    ProgressData *pdata;
    GtkWidget *align;
    GtkWidget *separator;
    GtkWidget *table;
    GtkWidget *button;
    GtkWidget *check;
    GtkWidget *vbox;

    gtk_init (&argc, &argv);

    /* Allocate memory for the data that is passed to the callbacks */
    pdata = g_malloc (sizeof (ProgressData));
  
    pdata->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_resizable (GTK_WINDOW (pdata->window), TRUE);

    g_signal_connect (G_OBJECT (pdata->window), "destroy",
                      G_CALLBACK (destroy_progress),
                      (gpointer) pdata);
    gtk_window_set_title (GTK_WINDOW (pdata->window), "GtkProgressBar");
    gtk_container_set_border_width (GTK_CONTAINER (pdata->window), 0);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
    gtk_container_add (GTK_CONTAINER (pdata->window), vbox);
    gtk_widget_show (vbox);
  
    /* Create a centering alignment object */
    align = gtk_alignment_new (0.5, 0.5, 0, 0);
    gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
    gtk_widget_show (align);

    /* Create the GtkProgressBar */
    pdata->pbar = gtk_progress_bar_new ();

    gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
    gtk_widget_show (pdata->pbar);

    /* Add a timer callback to update the value of the progress bar */
    pdata->timer = g_timeout_add (100, progress_timeout, pdata);

    separator = gtk_hseparator_new ();
    gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
    gtk_widget_show (separator);

    /* rows, columns, homogeneous */
    table = gtk_table_new (2, 3, FALSE);
    gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
    gtk_widget_show (table);

    /* Add a check button to select displaying of the trough text */
    check = gtk_check_button_new_with_label ("Show text");
    gtk_table_attach (GTK_TABLE (table), check, 0, 1, 0, 1,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    g_signal_connect (G_OBJECT (check), "clicked",
                      G_CALLBACK (toggle_show_text),
                      (gpointer) pdata);
    gtk_widget_show (check);

    /* Add a check button to toggle activity mode */
    check = gtk_check_button_new_with_label ("Activity mode");
    gtk_table_attach (GTK_TABLE (table), check, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    g_signal_connect (G_OBJECT (check), "clicked",
                      G_CALLBACK (toggle_activity_mode),
                      (gpointer) pdata);
    gtk_widget_show (check);

    /* Add a check button to toggle orientation */
    check = gtk_check_button_new_with_label ("Right to Left");
    gtk_table_attach (GTK_TABLE (table), check, 0, 1, 2, 3,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    g_signal_connect (G_OBJECT (check), "clicked",
                      G_CALLBACK (toggle_orientation),
                      (gpointer) pdata);
    gtk_widget_show (check);

    /* Add a button to exit the program */
    button = gtk_button_new_with_label ("close");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (pdata->window));
    gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

    /* This makes it so the button is the default. */
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

    /* This grabs this button to be the default button. Simply hitting
     * the "Enter" key will cause this button to activate. */
    gtk_widget_grab_default (button);
    gtk_widget_show (button);

    gtk_widget_show (pdata->window);

    gtk_main ();
    
    return 0;
}
    

Barras de estado (GtkStatusBar)

Las barras de estado son simples widgets usados para mostrar un mensaje de texto. Funcionan como una pila (estructura software) que guarda los mensajes que metemos en ella, de tal forma que si extraermos el último (pop) la barra de estado mostrará el anterior mensaje de texto de la pila.

Para permitir que diferentes partes de una misma aplicación usen la misma barra de estado para mostrar sus mensajes, el widget barra de estado asigna Identificadores de Contexto, que son usados para identificar a los diferentes "usuarios". El mensaje que se encuentre en lo alto de la pila será el que se muestre en pantalla, sin importar a qué Contexto pertenece. Los mensajes se ordenan como en toda pila, es decir, el último en entrar es el primero en salir, no en función del Identificador de Contexto.

Podemos crear una nueva barra de estado con la función: GtkWidget *gtk_statusbar_new( void );

Podemos solicitar un nuevo Identificador de Contexto mediante una llamada a la siguiente función, con una pequeña descripción textual del contexto: guint gtk_statusbar_get_context_id( GtkStatusbar *statusbar, const gchar *context_description );

Disponemos de tres funciones distintas para operar con una barra de estado: guint gtk_statusbar_push( GtkStatusbar *statusbar, guint context_id, const gchar *text );

void gtk_statusbar_pop( GtkStatusbar *statusbar) guint context_id );

void gtk_statusbar_remove( GtkStatusbar *statusbar, guint context_id, guint message_id );

La primera, gtk_statusbar_push(), se usa para añadir un nuevo mensaje a la barra de estado. Devuelve un Identificador de Mensaje, que puede ser pasado más adelante como parámetro a la función gtk_statusbar_remove para eliminar el mensaje que se asocie con el Identificador de Contexto y de Mensaje de la pila de la barra de estado.

La función gtk_statusbar_pop() elimina el mensaje más alto en la pila asociado al Identificador de Contexto que le llega como parámetro.

Además de mostrar mensajes, una barra de estado también puede mostrar un control para aumentar o disminuir el tamaño de la barra, al igual que redimensionamos el tamaño de una ventana, estirando con el ratón del borde de la misma.

Las siguientes funciones controlan la visualización de este control: void gtk_statusbar_set_has_resize_grip( GtkStatusbar *statusbar, gboolean setting );

gboolean gtk_statusbar_get_has_resize_grip( GtkStatusbar *statusbar );

El siguiente ejemplo crea una barra de estado y dos botones, uno para insertar elementos en la barra de estado y el otro para sacar el último insertado.

Figura 12. Ejemplo de utilización del widget statusbar

Ejemplo de utilización del widget statusbar


#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib.h>

GtkWidget *status_bar;

static void push_item( GtkWidget *widget,
                       gpointer   data )
{
  static int count = 1;
  gchar *buff;

  buff = g_strdup_printf ("Item %d", count++);
  gtk_statusbar_push (GTK_STATUSBAR (status_bar), GPOINTER_TO_INT (data), buff);
  g_free (buff);
}

static void pop_item( GtkWidget *widget,
                      gpointer   data )
{
  gtk_statusbar_pop (GTK_STATUSBAR (status_bar), GPOINTER_TO_INT (data));
}

int main( int   argc,
          char *argv[] )
{

    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *button;

    gint context_id;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request (GTK_WIDGET (window), 200, 100);
    gtk_window_set_title (GTK_WINDOW (window), "GTK Statusbar Example");
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (exit), NULL);
 
    vbox = gtk_vbox_new (FALSE, 1);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);
          
    status_bar = gtk_statusbar_new ();      
    gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
    gtk_widget_show (status_bar);

    context_id = gtk_statusbar_get_context_id(
                          GTK_STATUSBAR (status_bar), "Statusbar example");

    button = gtk_button_new_with_label ("push item");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (push_item), GINT_TO_POINTER (context_id));
    gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 2);
    gtk_widget_show (button);              

    button = gtk_button_new_with_label ("pop last item");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (pop_item), GINT_TO_POINTER (context_id));
    gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 2);
    gtk_widget_show (button);

    /* always display the window as the last step so it all splashes on
     * the screen at once. */
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}