![]() | ![]() | ![]() | Programación en el entorno GNOME | ![]() |
---|
Las etiquetas son widgets que nos permiten mostrar información (texto) en cualquier parte de nuestro interfaz.
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.
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; }
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.
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); }
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.
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.
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));
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");
Este tipo de widgets incluye a las barras de desplazamiento (scroollbar) 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 ??? 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 solamente debe utilizarse para hacer scroll sobre otro widget, como una lista, una caja de texto, o un puerto de visión (y en muchos es más fácil utilizar el widget scrolled window). Para el resto de los casos, debería utilizar los widgets de escala, ya son más sencillos de usar y más 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 (si no me cree, ¡mire los ficheros de cabecera!). 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.
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.
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>.
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.
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) ;
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");
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)
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.
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.
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.
<< Ventanas | Botones >> |