El Tutorial GTK+ lista los siguientes widgets:
GtkObject +GtkData | +GtkAdjustment | `GtkTooltips `GtkWidget +GtkContainer | +GtkBin | | +GtkAlignment | | +GtkEventBox | | +GtkFrame | | | `GtkAspectFrame | | +GtkHandleBox | | +GtkItem | | | +GtkListItem | | | +GtkMenuItem | | | | `GtkCheckMenuItem | | | | `GtkRadioMenuItem | | | `GtkTreeItem | | +GtkViewport | | `GtkWindow | | +GtkColorSelectionDialog | | +GtkDialog | | | `GtkInputDialog | | `GtkFileSelection | +GtkBox | | +GtkButtonBox | | | +GtkHButtonBox | | | `GtkVButtonBox | | +GtkHBox | | | +GtkCombo | | | `GtkStatusbar | | `GtkVBox | | +GtkColorSelection | | `GtkGammaCurve | +GtkButton | | +GtkOptionMenu | | `GtkToggleButton | | `GtkCheckButton | | `GtkRadioButton | +GtkCList | `GtkCTree | +GtkFixed | +GtkList | +GtkMenuShell | | +GtkMenuBar | | `GtkMenu | +GtkNotebook | +GtkPaned | | +GtkHPaned | | `GtkVPaned | +GtkScrolledWindow | +GtkTable | +GtkToolbar | `GtkTree +GtkDrawingArea | `GtkCurve +GtkEditable | +GtkEntry | | `GtkSpinButton | `GtkText +GtkMisc | +GtkArrow | +GtkImage | +GtkLabel | | `GtkTipsQuery | `GtkPixmap +GtkPreview +GtkProgressBar +GtkRange | +GtkScale | | +GtkHScale | | `GtkVScale | `GtkScrollbar | +GtkHScrollbar | `GtkVScrollbar +GtkRuler | +GtkHRuler | `GtkVRuler `GtkSeparator +GtkHSeparator `GtkVSeparator |
La librería GLib puede ser utilizada en un modo seguro-con-hilos, al llamar g_thread_init() antes de hacer cualquier otra llamada GLib. En este modo GLib tranca automáticamente todas las estructuras de datos internas que necesite trancar. Esto no significa que dos hilos puedan acceder simultáneamente, por ejemplo, un tabla hash, pero pueden acceder dos tablas hash diferentes simultáneamente. Si dos hilos distintos necesitan acceder la misma tabla hash, la aplicación es responsable de asegurarse a sí misma.
Cuando GLib se inicializa en modo seguro-con-hilos, GTK+ esta atento a los hilos. Hay una cerradura global que debes obtener con gdk_threads_enter() antes de hacer cualquier llamada GDK, y debes liberar con gdk_threads_leave().
Un programa main mínimo para una aplicación GTK+ con hilos luce como:
int main (int argc, char *argv[]) { GtkWidget *window; g_thread_init(NULL); gtk_init(&argc, &argv); window = create_window(); gtk_widget_show(window); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return(0); } |
Los callbacks requieren un poco de atención. Los callbacks de (señales) GTK+ se hacen dentro de la cerradura GTK+. Sin embargo, las llamadas desde GLib (timeouts, IO callbacks, y funciones inactivas) se hacen fuera de la cerradura GTK+. Así que, dentro de un manejador de señales no necesitas llamar a gdk_threads_enter(), pero desde los otros tipos de callbacks, si tienes que hacerlo.
Erik Mouw contribuyó el siguiente código de ejemplo para ilustrar cómo utilizar hilos dentro de programas GTK+.
/*------------------------------------------------------------------------- * Nombre de archivo: gtk-thread.c * Versión: 0.99.1 * Copyright: Copyright (C) 1999, Erik Mouw * Autor: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Descripción: Ejemplo de hilos GTK. * Creado en: Dom 17 Oct 21:27:09 1999 * Modificado por: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Modificado en: Dom 24 Oct 17:21:41 1999 *-----------------------------------------------------------------------*/ /* * Compilar con: * * cc -o gtk-thread gtk-thread.c `gtk-config --cflags --libs gthread` * * Gracias a Sebastian Wilhelmi y Owen Taylor por señalar algunos * bugs. * */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <gtk/gtk.h> #include <glib.h> #include <pthread.h> #define YES_IT_IS (1) #define NO_IT_IS_NOT (0) typedef struct { GtkWidget *label; int what; } yes_or_no_args; G_LOCK_DEFINE_STATIC (yes_or_no); static volatile int yes_or_no = YES_IT_IS; void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void *argument_thread(void *args) { yes_or_no_args *data = (yes_or_no_args *)args; gboolean say_something; for(;;) { /* duerme un rato */ sleep(rand() / (RAND_MAX / 3) + 1); /* trancar la yes_or_no_variable */ G_LOCK(yes_or_no); /* ¿tenemos que decir algo? */ say_something = (yes_or_no != data->what); if(say_something) { /* asignar la variable */ yes_or_no = data->what; } /* Soltar la variable yes_or_no */ G_UNLOCK(yes_or_no); if(say_something) { /* conseguir la cerradura GTK */ gdk_threads_enter(); /* asignar el texto de la etiqueta */ if(data->what == YES_IT_IS) gtk_label_set_text(GTK_LABEL(data->label), "O yes, it is!"); else gtk_label_set_text(GTK_LABEL(data->label), "O no, it isn't!"); /* soltar la cerradura del hilo GTK */ gdk_threads_leave(); } } return(NULL); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *label; yes_or_no_args yes_args, no_args; pthread_t no_tid, yes_tid; /* inicializar hilos */ g_thread_init(NULL); /* inicializar gtk */ gtk_init(&argc, &argv); /* inicializar generador de números aleatorios */ srand((unsigned int)time(NULL)); /* crear una ventana */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); gtk_container_set_border_width(GTK_CONTAINER (window), 10); /* crear una etiqueta */ label = gtk_label_new("And now for something completely different ..."); gtk_container_add(GTK_CONTAINER(window), label); /* mostrar todo */ gtk_widget_show(label); gtk_widget_show (window); /* crear los hilos */ yes_args.label = label; yes_args.what = YES_IT_IS; pthread_create(&yes_tid, NULL, argument_thread, &yes_args); no_args.label = label; no_args.what = NO_IT_IS_NOT; pthread_create(&no_tid, NULL, argument_thread, &no_args); /* entrar al ciclo principal de GTK */ gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return(0); } |
Este no es en realidad un problema de GTK+, y el problema no está relacionado tampoco con fork(). Si ocurre 'x io error', entonces probablemente estás usando la función exit() para salir de un proceso hijo.
Cuando GDK abre una pantalla X, crea un descriptor de archivo de socket. Cuando utilizas la función exit(), implícitamente cierras todos los descriptores de archivos abiertos, y esto no le agrada a la librería X subyacente.
La función correcta a utilizar en este caso es _exit().
Erik Mouw contribuyó el siguiente código de ejemplo para ilustrar el manejo de fork() y exit().
/*------------------------------------------------------------------------- * Nombre de archivo: gtk-fork.c * Versión: 0.99.1 * Copyright: Copyright (C) 1999, Erik Mouw * Autor: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Descripción: Ejemplo de fork en GTK+ * Creado en: Jue 23 Sep 21:37:55 1999 * Modificado por: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Modificado en: Jue 23 Sep 22:39:39 1999 *-----------------------------------------------------------------------*/ /* * Compilar con: * * cc -o gtk-fork gtk-fork.c `gtk-config --cflags --libs` * */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <gtk/gtk.h> void sigchld_handler(int num) { sigset_t set, oldset; pid_t pid; int status, exitstatus; /* blockear otras señales SIGCHLD que vengan */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, &oldset); /* esperar por el hijo */ while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { if(WIFEXITED(status)) { exitstatus = WEXITSTATUS(status); fprintf(stderr, "Parent: child exited, pid = %d, exit status = %d\n", (int)pid, exitstatus); } else if(WIFSIGNALED(status)) { exitstatus = WTERMSIG(status); fprintf(stderr, "Parent: child terminated by signal %d, pid = %d\n", exitstatus, (int)pid); } else if(WIFSTOPPED(status)) { exitstatus = WSTOPSIG(status); fprintf(stderr, "Parent: child stopped by signal %d, pid = %d\n", exitstatus, (int)pid); } else { fprintf(stderr, "Parent: child exited magically, pid = %d\n", (int)pid); } } /* re-instalar el manejador de señal (algunos sistemas necesitan esto) */ signal(SIGCHLD, sigchld_handler); /* y desbloquearlo */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_UNBLOCK, &set, &oldset); } gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { return(FALSE); } void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void fork_me(GtkWidget *widget, gpointer data) { pid_t pid; pid = fork(); if(pid == -1) { /* ouch, falló el fork() */ perror("fork"); exit(-1); } else if(pid == 0) { /* hijo */ fprintf(stderr, "Child: pid = %d\n", (int)getpid()); execlp("ls", "ls", "-CF", "/", NULL); /* si exec() retorna, hay algo malo */ perror("execlp"); /* salir del hijo. nota el uso de _exit() en lugar de exit() */ _exit(-1); } else { /* padre */ fprintf(stderr, "Parent: forked a child with pid = %d\n", (int)pid); } } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; gtk_init(&argc, &argv); /* las cosas básicas: hacer una ventana y colocar callbacks para destruír y * borrar eventos */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); gtk_signal_connect(GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); #if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0) gtk_container_border_width(GTK_CONTAINER (window), 10); #else gtk_container_set_border_width(GTK_CONTAINER (window), 10); #endif /* agregar un botón para hacer algo útil */ button = gtk_button_new_with_label("Fork me!"); gtk_signal_connect(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC(fork_me), NULL); gtk_container_add(GTK_CONTAINER(window), button); /* mostrar todo */ gtk_widget_show (button); gtk_widget_show (window); /* instalar un manejador de señal para señales SIGCHLD */ signal(SIGCHLD, sigchld_handler); /* ciclo principal */ gtk_main (); exit(0); } |
De: Peter Mattis
"La razón por la cual los botones no mueven su hijo hacia abajo y la derecha cuando son presionados es porque no me parece que eso es lo que está ocurriendo visualmente. Mi visión de los botones es que los estás mirando en línea recta. Osea que, la interface del usuario está en un plano y tú estás encima de este mirándolo recto. Cuando un botón es oprimido se mueve directamente lejos de ti. Para ser absolutamente correcto yo supongo que el hijo debería encogerse un poquito. Pero no veo por que el hijo se debería mover hacia abajo y a la izquierda. Recuerda, se supone que el hijo está pegado a la superficie del botón. No es bueno que aparezca como si el hijo resbalara en la superficie del botón."
"En una nota más práctica, yo sí implemente esto en algún momento y decidí que no se veía bien y lo eliminé."
Hay un par de maneras de encontrar el padre superior de un widget. La más fácil es llamar la función gtk_widget_top_level() que devuelve un puntero a GtkWidget que es la ventana superior.
Una manera más complicada de hacer esto (pero menos limitada, ya que permite al usuario obtener el ancestro más cercano de un tipo conocido) es utilizar gtk_widget_get_ancestor() como en:
GtkWidget *widget; widget = gtk_widget_get_ancestor(w, GTK_TYPE_WINDOW); |
Ya que virtualmente todos los GTK_TYPEs pueden ser usados como el segundo parámetro de esta función, puedes obtener cualquier widget padre de un widget en particular. Supón que tienes un hbox que contiene un vbox, que en su lugar contiene algún otro widget atómico (entry, label, etc. Para encontrar el hbox maestro utilizando el widget entry simplemente usa:
GtkWidget *hbox; hbox = gtk_widget_get_ancestor(w, GTK_TYPE_HBOX); |
La ventana Gdk/X se creará cuando el widget se realiza. Puedes conseguir el ID de Ventana con:
#include <gdk/gdkx.h> Window xwin = GDK_WINDOW_XWINDOW (GTK_WIDGET (my_window)->window); |
Tim Janik envió a gtk-list (un poco modificado):
Definición de un manejador de señal:
gint signal_handler_event(GtkWiget *widget, GdkEvenButton *event, gpointer func_data) { if (GTK_IS_LIST_ITEM(widget) && (event->type==GDK_2BUTTON_PRESS || event->type==GDK_3BUTTON_PRESS) ) { printf("I feel %s clicked on button %d\", event->type==GDK_2BUTTON_PRESS ? "double" : "triple", event->button); } return FALSE; } |
Y conecta el manejador a tu objeto:
{ /* lista, asuntos de inicialización de lista*/ gtk_signal_connect(GTK_OBJECT(list_item), "button_press_event", GTK_SIGNAL_FUNC(signal_handler_event), NULL); /* y/o */ gtk_signal_connect(GTK_OBJECT(list_item), "button_release_event", GTK_SIGNAL_FUNC(signal_handler_event), NULL); /* algo más */ } |
y, Owen Taylor escribió:
"Ten en cuenta que presionar un botón será recibido de antemano, y si estás haciendo esto para un botón, por lo tanto obtendrás una señal "clicked" para el botón. (Esto es cierto para cualquier juego de herramientas, ya que las computadoras no son buenas leyendo la mente.)"
Antes que nada, Havoc Pennington da una descripción bastante completa de las diferencias entre eventos y señales en su libro libre (puede encontrar dos capítulos en http://www106.pair.com/rhp/sample_chapters.html).
Además, Havoc envió esto a la gtk-list "Los eventos son un flujo de mensajes recibido del servidor X. Ellos manejan el ciclo main de Gtk; que más o menos es "esperar por eventos, procesarlos" (no exactamente, es en realidad más general que eso y puede esperar en varios flujos de entrada distintos al mismo tiempo). Los eventos son un concepto de Gdk/Xlib"
"Las señales son una característica de GtkObject y su subclases. No tienen nada que ver con algún flujo de entrada; en realidad, una señal es solo una manera de mantener una lista de callbacks a mano e invocarlos ("emitir" la señal). Hay una gran cantidad de detalles y características extras. Las señales son emitidas por instancias de objetoc, y no tienen relación con el ciclo main Gtk. Convencionalmente, las señales son emitidas "cuando algo cambia" en los objetos que emiten la señal."
"Las señales y los eventos solo se juntan porque sucede que GtkWidget emite señales cuando recibe eventos. Esto es puramente una conveniencia, de manera que puedas conectar callbacks para que sean invocadas cuando un widget en particular recibe un evento particular. No hay nada sobre esto que haga los eventos y las señales conceptos inherentemente relacionados, no más que emitir una señal cuando presionas un botón hace el presionar y la señal conceptos relacionados."
Todos los manejadores de eventos toman un argumento adicional que contiene información sobre el evento que disparó al manejador. De manera que, un manejador delete_event debe ser declarado como:
gint delete_event_handler (GtkWidget *widget, GdkEventAny *event, gpointer data); |
Para atrapar algunos eventos en particular se debe hacer una inicialización especial. De hecho, debes colocar la máscara de bit de evento correcta de tu widget antes de obtener algún evento particular.
Por ejemplo,
gtk_widget_add_events(window, GDK_KEY_RELEASE_MASK); |
te permite atrapar eventos de soltar tecla. Si quieres atrapar todos los eventos, simplemente utiliza la máscara de eventos GDK_ALL_EVENTS_MASK.
Todas las máscaras de eventos se definen en el archivo gdktypes.h.
Si la señal que quieres agregar puede ser beneficiosa para otros usuarios GTK+, puede que quieras enviar un parche que presente tus cambios. Chequea el tutorial para más información sobre cómo agregar señales a una clase widget.
Si no crees que ese sea el caso, o si tu parche no es aplicado, tendrás que usar la función gtk_object_class_user_signal_new. gtk_object_class_user_signal_new te permite agregar una nueva señal a un widget GTK+ predefinido sin modificación alguna al código fuente de GTK+. La nueva señal puede ser emitida con gtk_signal_emit y puede ser manejada en la misma forma que otras señales.
Tim Janik puso este pedacito de código:
static guint signal_user_action = 0; signal_user_action = gtk_object_class_user_signal_new (gtk_type_class (GTK_TYPE_WIDGET), "user_action", GTK_RUN_LAST | GTK_RUN_ACTION, gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); void gtk_widget_user_action (GtkWidget *widget, gpointer act_data) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_signal_emit (GTK_OBJECT (widget), signal_user_action, act_data); } |
Si quieres que tu nueva señal tenga algo más que el parámetro clásico gpointer, tendrás que jugar con los marshallers de GTK+.
El comportamiento de GTK (sin recorte) es una consecuencia de sus intentos de conservar recursos X. Los widgets etiqueta (entre otros) no tienen su propia ventana X - simplemente dibujan su contenido en su ventana padre. Aunque es posible el hacer recorte colocando la máscara de recorte antes de dibujar el texto, esto probablemente causaría una penalidad substancial en el desempeño.
Es posible que, a largo plazo, la mejor solución a esos problemas simplemente sea el cambiar gtk para que le de ventanas X a las etiquetas. Una solución a corto plazo es poner el widget de etiqueta dentro de otro widget que si obtiene su propia ventana - una candidato posible puede ser el widget puerto de vista.
viewport = gtk_viewport (NULL, NULL); gtk_widget_set_usize (viewport, 50, 25); gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); gtk_widget_show(viewport); label = gtk_label ("a really long label that won't fit"); gtk_container_add (GTK_CONTAINER(viewport), label); gtk_widget_show (label); |
Si hicieras esto para un montón de widgets, podrías querer copiar gtkviewport.c y arrancar la funcionalidad de ajuste y sobra (tal vez lo podrías llamar GtkClipper).
Después de crear tu ventana, haz un gtk_grab_add(my_window). Y después de cerrar la ventana haz un gtk_grab_remove(my_window).
Probablemente estás haciendo todos los cambios dentro de una función sin devolver el control a gtk_main(). Este puede ser el caso si haces algún calculo largo en tu código. La mayoría de las actualizaciones de dibujo solo se ponen en una cola, la cual es procesada dentro de gtk_main(). Puedes forzar el procesamiento de la cola de dibujo utilizando algo como esto:
while (g_main_iteration(FALSE)); |
dentro de la función que cambia el widget.
Lo que el pedacito anterior hace es correr todos los eventos pendientes y funciones ociosas de alta prioridad, para luego regresar inmediatamente (el dibujado se hace en una función ociosa de alta prioridad).
Antes que nada, los datos pegados se almacenan en el campo object_data de un GtkObject. El tipo de este campo es GData, el cual se define en glib.h. Por lo tanto deberías leer muy cuidadosamente el archivo gdataset.c en tu directorio de los fuentes de glib.
Hay dos maneras (fáciles) de pegar algún dato a un objeto gtk. Utilizando gtk_object_set_data() y gtk_object_get_data() parece ser la manera más común de hacer esto, ya que provee una interface poderosa para conectar objetos y datos.
void gtk_object_set_data(GtkObject *object, const gchar *key, gpointer data); gpointer gtk_object_get_data(GtkObject *object, const gchar *key); |
Ya que un ejemplo corto es mejor que un discurso largo:
struct my_struct p1,p2,*result; GtkWidget *w; gtk_object_set_data(GTK_OBJECT(w),"p1 data",(gpointer)&p1); gtk_object_set_data(GTK_OBJECT(w),"p2 data",(gpointer)&p2); result = gtk_object_get_data(GTK_OBJECT(w),"p1 data"); |
Las funciones gtk_object_set_user_data() y gtk_object_get_user_data() hacen los mismo que las funciones anteriores, pero no te permiten especificar el parámetro "key". En su lugar, utiliza una llave estándar llamada "user_data". Ten en cuenta que el uso de estas funciones se desaprueba en la versión 1.2. Simplemente proveen un modo de compatibilidad con algunos paquetes gtk viejos.
Cuando pegas los datos al objeto, puedes usar la función gtk_object_set_data_full(). Los primeros tres argumentos de la función son los mismos que en gtk_object_set_data(). El cuarto es un puntero a una función callback que es llamada cuando los datos son destruidos. Los datos son destruidos cuando:
destruyes el objeto
reemplazas los datos con nuevos datos (con la misma llave)
reemplazas los datos con NULL (con la misma llave)
La manera normal de dar un nuevo padre (eje. cambiar el dueño) a un widget debería ser utilizando la función:
void gtk_widget_reparent (GtkWidget *widget, GtkWidget *new_parent) |
Pero esto es solo un "debería" ya que esta función no hace su trabajo correctamente en algunos widgets. El objetivo principal de gtk_widget_reparent() es evitar de-realizar un widget si tanto widget como new_parent están realizados (en este caso, widget->window tiene un cambio exitoso de padre). El problema aquí es que algunos widgets en la jerarquía GTK+ pegadas múltiples subventanas X y este es notablemente el caso para el widget GtkSpinButtton. Para esos, gtk_widget_reparent() fallará dejando un hijo sin realizar donde no debería.
Para evitar este problema, simplemente utiliza el siguiente pedacito de código:
gtk_widget_ref(widget); gtk_container_remove(GTK_CONTAINER(old_parent), widget); gtk_container_add(GTK_CONTAINER(new_parent), widget); gtk_widget_unref(widget); |
Tal como Tim Janik señaló, hay casos distintos, y cada caso requiere una solución diferente.
Si quieres la posición de un widget en relación con su padre, debes usar widget->allocation.x y widget->allocation.y.
Si quieres la posición de una ventana en relación con la ventana raíz de X, debes usar gdk_window_get_geometry() gdk_window_get_position() o gdk_window_get_origin().
Si quieres obtener la posición de la ventana (incluyendo las decoraciones del Administrador de Ventana), debes usar gdk_window_get_root_origin().
Por último pero no menos importante, si quieres obtener la posición de un marco del Administrador de Ventana, debes usar gdk_window_get_deskrelative_origin().
Tu elección de Administrador de Ventana tendrá un efecto en los resultados de las funciones anteriores. Debes tener esto en mente cuando escribas tu aplicación. Esto es dependiente de como los Administradores de Ventana manejan las decoraciones que agregan alrededor de las ventanas.
La función gtk_widget_set_uposition() se usa para asignar la posición de cualquier widget.
La función gtk_widget_set_usize() se usa para asignar el tamaño de un widget. Para poder usar todas las características que esta función provee cuando actúa en una ventana, querrás utilizar la función gtk_window_set_policy. Las definiciones de estas funciones son:
void gtk_widget_set_usize (GtkWidget *widget, gint width, gint height); void gtk_window_set_policy (GtkWindow *window, gint allow_shrink, gint allow_grow, gint auto_shrink); |
Auto_shrink encogerá automáticamente la ventana cuando el tamaño solicitado del widget hijo vaya por debajo del tamaño actual de la ventana. Allow_shrink le dará al usuario la autorización de hacer la ventana más pequeña de lo que normalmente debería ser. Allow_grow le dará al usuario la posibilidad de hacer la ventana más grande. Los valores por defecto de estos parámetros son:
allow_shrink = FALSE allow_grow = TRUE auto_shrink = FALSE |
La función gtk_widget_set_usize() no es la manera más fácil de asignar un tamaño de ventana ya que no puedes disminuir el tamaño de esta ventana con otra llamada a esta función, a menos que la llames dos veces, como en:
gtk_widget_set_usize(your_widget, -1, -1); gtk_widget_set_usize(your_widget, new_x_size, new_y_size); |
Otra manera de asignar el tamaño y/o mover una ventana es usando la función gdk_window_move_resize() la cual suele trabajar bien tanto para crecer como para encoger la ventana:
gdk_window_move_resize(window->window, x_pos, y_pos, x_size, y_size); |
El ejemplo de menú en el directorio examples/menu de la distribución GTK+, implementa un menú desplegable con esta técnica:
static gint button_press (GtkWidget *widget, GdkEvent *event) { if (event->type == GDK_BUTTON_PRESS) { GdkEventButton *bevent = (GdkEventButton *) event; gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL, bevent->button, bevent->time); /* Decirle al código que llama que hemos manejado este evento; el * pase de carga termina aquí. */ return TRUE; } /* Decirle al código que llama que no hemos manejado este evento; * pasarlo */ return FALSE; } |
Para deshabilitar (o habilitar) un widget, utiliza la función gtk_widget_set_sensitive(). El primer parámetro es tu puntero a widget. El segundo parámetro es un valor buleano: cuando es TRUE, el widget se habilita.
Por ejemplo:
gint gtk_clist_prepend (GtkCList *clist, gchar *text[]); |
Repuesta: No, aunque un tipo "gchar*" (puntero a char) puede ser moldeado a un "const gchar*" (puntero a const char), esto no se aplica para "gchar *[]" (cadena de un número no especificado de punteros a char) a un "const gchar *[]" (cadena de un número no especificado de punteros a const char).
El calificador de tipo "const" puede estar sujeto a moldeado automático, pero en el caso de la cadena, no es la cadena en sí la que necesita el molde calificado (const), si no sus miembros, por lo tanto cambiando todo el tipo.
Hay varias maneras de atacar esto. La manera más simple es utilizar GdkRGB, mira en gdk/gdkrgb.h. Puedes crear un buffer RGB, dibujar en buffer RGB, y entonces usar rutinas GdkRGB para copiar el buffer RGB a un widget área de dibujo o a un widget hecho por uno mismo. El libro "GTK+/Gome Application Development" da algunos detalles; GdkRBG también está documentado en la documentación de referencia de GTK+.
Si estás escribiendo un juego u otro aplicación intensiva con las gráficas, puedes considerar una solución más elaborada. OpenGL es el estándar gráfico que te permitirá acceder a la aceleración hardware en versiones futuras de XFree86; así que, para máxima velocidad, probablemente te interese usar OpenGL. Un widget GtkGLArea está disponible para usar OpenGL en GTK+ (pero GtkGLArea no viene con GTK+). También hay varias librerías para juegos con código fuente abierto, tal como ClanLib y la librería SimpleDirectMedia Layer (SDL) de Loki's.
NO deberías utilizar gdk_draw_point(), porque sería extremadamente lento.
Funciones tal como gdk_pixmap_create_from_xpm() requieren una ventana válida como parámetro. Al transcurrir la fase de inicialización de una aplicación, una ventana válida puede no estar disponible sin mostrar una ventana, lo que puede no ser apropiado. Para evitar esto, se puede usar una función como gdk_pixmap_colormap_create_from_xpm(), como en:
char *pixfile = "foo.xpm"; GtkWidget *top, *box, *pixw; GdkPixmap *pixmap, *pixmap_mask; top = gtk_window_new (GKT_WINDOW_TOPLEVEL); box = gtk_hbox_new (FALSE, 4); gtk_conainer_add (GTK_CONTAINER(top), box); pixmap = gdk_pixmap_colormap_create_from_xpm ( NULL, gtk_widget_get_colormap(top), &pixmap_mask, NULL, pixfile); pixw = gtk_pixmap_new (pixmap, pixmap_mask); gdk_pixmap_unref (pixmap); gdk_pixmap_unref (pixmap_mask); |