Nous continuons aujourd'hui notre exploration de la bibliothèque GTK+. L'article de ce mois-ci présente de manière approfondie le père des widgets de GTK+.
Les GtkWidgets
Tous les widgets de GTK+ sont répartis dans une hiérarchie d'objets dont la racine (la base) est le widget GtkWidget. Cela implique que tous les widgets GTK+, comme les GtkButtons, les GtkLabels... peuvent à tout moment être considérés comme des GtkWidgets et l'on peut donc utiliser les fonctions gtk_widget_* sur tous ceux-là. Il en va de même pour les signaux. En effet, les signaux définis pour des widgets sont aussi reçus par tous les widgets qui en dérivent (et qui sont capables de recevoir des signaux...).
Les signaux de GtkWidgets
Les GtkWidgets étant la base de tous les widgets, ils introduisent énormément de signaux différents. Les signaux pouvant être traités par les GtkWidgets sont :
"show", "hide", "map", "unmap", "realize",
"unrealize", "draw", "draw_focus", "draw_default", "size_request", "size_allocate", "state_changed", "parent_set", "style_set", "grab_focus", "event", "button_press_event", "button_release_event", "motion_notify_event", "delete_event", "destroy_event", "expose_event", "key_press_event", "key_release_event", "enter_notify_event", "leave_notify_event", "configure_event", "focus_in_event", "focus_out_event", "map_event", "unmap_event", "property_notify_event", "selection_clear_event", "selection_request_event", "selection_notify_event", "selection_received", "selection_get", "proximity_in_event", "proximity_out_event", "drag_leave", "drag_begin", "drag_end", "drag_data_delete", "drag_motion", "drag_drop", "drag_data_get", "drag_data_received", "visibility_notify_event", "client_event",
"no_expose_event", "debug_msg", et "destroy".
Ce qui fait beaucoup, j'en conviens. Mais rappelez-vous, ces signaux sont utilisables par tous les widgets et la plupart ne sont que très rarement employés, mais ils doivent être présents « au cas où ».
Bien sûr, tous ces signaux peuvent être connectés grâce à la fonction
guint gtk_signal_connect(GtkObject *Widget, const gchar *NomDuSignal,
GtkSignalFunc FonctionDeRappel,
gpointer Donnees);
et les autres fonctions du même genre présentées lors de l'article précédent de cette initiation.
Quelques Macros bien utiles
Chaque widget GTK+ est fourni avec son lot de macros très utiles qui permettent de connaître leur état. Là encore, on en trouve une véritable foison pour le père de tous les widgets GTK+. Ces macros s'appliquent bien entendu à n'importe quel widget. Toutes ces macros renvoient TRUE ou FALSE.
4 GTK_WIDGET_TOPLEVEL(widget) : permet de savoir si un widget est une fenêtre gérée par le window manager.
4 GTK_WIDGET_NO_WINDOW(widget) : est vrai si le widget ne possède pas de fenêtre propre. C'est notamment le cas des GtkLabels qui utilisent la fenêtre parente. Les widgets ne possédant pas de fenêtre propre ne peuvent pas non plus recevoir de signaux.
4 GTK_WIDGET_REALIZED(widget) : est vrai si le widget est actuellement entièrement géré par GTK+
4 GTK_WIDGET_MAPPED(widget) : est vrai si le widget a été affiché par le serveur X.
4 GTK_WIDGET_VISIBLE(widget) : est vrai si le widget est actuellement visible.
4 GTK_WIDGET_CAN_FOCUS(widget) : est vrai si le widget peut recevoir le focus du clavier.
4 GTK_WIDGET_HAS_FOCUS(widget) : est vrai si le widget a actuellement le focus du clavier.
4 GTK_WIDGET_HAS_DEFAULT(widget) : est vrai si le widget peut être activé en pressant la touche 'entrée', même s'il n'a pas le focus du clavier.
4 GTK_WIDGET_CAN_DEFAULT(widget) : est vrai si le widget peut obtenir l'état GTK_WIDGET_HAS_DEFAULT, par exemple par l'utilisation de la touche 'Tab' dans une boîte de dialogue.
4 GTK_WIDGET_HAS_GRAB(widget) : est vrai si le widget a actuellement fait main basse (to grab) sur le serveur X. C'est-à-dire que le widget récupère actuellement tous les événements souris ou clavier même si le pointeur de la souris n'est pas au-dessus de lui.
4 GTK_WIDGET_SENSITIVE(widget) : est vrai si le widget peut recevoir des événements et n'est pas grisé.
La plupart de ces états peuvent être changés grâce aux macros :
GTK_WIDGET_SET_FLAGS(Widget, NomDuFlag); et GTK_WIDGET_UNSET_FLAGS(Widget, NomDuFlag);
où NomDuFlag est un des états suivants :
GTK_TOPLEVEL, GTK_NO_WINDOW, GTK_REALIZED, GTK_MAPPED, GTK_VISIBLE,
GTK_SENSITIVE, GTK_PARENT_SENSITIVE, GTK_CAN_FOCUS, GTK_HAS_FOCUS,
GTK_CAN_DEFAULT, GTK_HAS_DEFAULT, GTK_HAS_GRAB, GTK_RC_STYLE,
GTK_COMPOSITE_CHILD, GTK_NO_REPARENT, GTK_APP_PAINTABLE,
GTK_RECEIVES_DEFAULT
A tout cela, on peut rajouter que l'on peut connaître (mais pas changer) la taille et la position d'un widget par rapport à son parent grâce à :
widget->allocation.x,
widget->allocation.y,
widget->allocation.width et
widget->allocation.height.
Les fonctions importantes communes à tous les widgets de GTK+
Le nombre des fonctions s'appliquant au GtkWidget étant vraiment très important, je ne présenterai ici que celles que je juge (et cela n'engage vraiment que moi) les plus importantes. Ce sont en tout cas celles que j'emploie le plus souvent dans mes programmes GTK+ et notamment dans Giram. Les lecteurs désirant en savoir plus pourront inspecter les fichiers sources gtkwidget.h et gtkwidget.c.
Il existe une fonction pour créer un GtkWidget :
GtkWidget* gtk_widget_new(GtkType type, const gchar *first_arg_name, ...);
Mais comme les GtkWidgets sont des choses plus ou moins abstraites, cette fonction n'est pratiquement jamais utilisée.
On peut détruire un widget (et sa descendance) avec la fonction
void gtk_widget_destroy(GtkWidget *widget);
le rendre visible avec
void gtk_widget_show(GtkWidget *widget);
le cacher avec
void gtk_widget_hide(GtkWidget *widget);
le rendre visible lui et toute sa descendance avec
void gtk_widget_show_all(GtkWidget *widget);
le cacher lui et toute sa descendance avec
void gtk_widget_hide_all(GtkWidget *widget);
le rendre disponible pour GTK+
void gtk_widget_realize(GtkWidget *widget);
Cet appel crée notamment la fenêtre associée au widget (s'il en a une !).
On peut aussi forcer un widget à se redessiner :
void gtk_widget_queue_draw(GtkWidget *widget);
ou juste une partie de celui-ci :
void gtk_widget_queue_draw_area(GtkWidget *widget, gint x, gint y,
gint width, gint height);
Si un widget est contenu dans un autre, on peut décider de le placer dans un autre grâce à :
void gtk_widget_reparent(GtkWidget *widget, GtkWidget *nouveau_parent);
On peut aussi demander à un widget de surgir à un endroit précis en appelant la fonction :
void gtk_widget_popup(GtkWidget *widget, gint x, gint y);
Le widget est alors affiché sans décoration du window manager. Cette fonction est surtout utile pour les 'popup-menu' que l'on déclenche habituellement d'un clic du bouton droit de la souris.
Si, à l'intérieur d'une boîte de dialogue, l'on désire qu'un widget en particulier récupère tous les événements en provenance du clavier, la fonction void gtk_widget_grab_focus(GtkWidget *widget);
est faite pour cela. Tant que le window manager laisse le focus à la boîte de dialogue, ce sera le widget sélectionné par cette fonction qui aura le focus clavier.
Dans une boîte de dialogue, il y a souvent un widget qui est actionné par l'appui sur la touche 'Entrée'. Ce widget doit avoir l'état GTK_CAN_DEFAULT et l'on peut le choisir en utilisant la fonction :
void gtk_widget_grab_default(GtkWidget *widget);
Le nom d'un widget peut être positionné et récupéré par les fonctions :
void gtk_widget_set_name(GtkWidget *widget, const gchar *nom);
gchar *gtk_widget_get_name(GtkWidget *widget);
Ceci est surtout utile à des fins de débogage.
void gtk_widget_set_sensitive(GtkWidget *widget, gboolean sensitive);
permet de choisir si le widget doit être grisé (inactif) ou dégrisé (actif).
void gtk_widget_set_uposition(GtkWidget *widget, gint x, gint y);
permet de choisir la position du widget à l'intérieur de son parent.
void gtk_widget_set_usize(GtkWidget *widget, gint width, gint height);
permet de choisir la taille minimale que l'on souhaite voir allouée à notre widget. Attention, GTK+ peut tout à fait choisir d'allouer une taille plus grande que celle qu'on lui demande, par exemple, lorsque l'utilisateur redimensionne la fenêtre.
On peut bien évidemment choisir les événements que notre widget peut gérer avec :
void gtk_widget_set_events(GtkWidget *widget, gint evenements);
où evenements est la liste des masques des événements que l'on veut gérer réunis par un 'ou logique': |.
Par exemple,
gtk_widget_set_events(widget, GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK);
permet de signifier que notre widget doit pouvoir se redessiner (GDK_EXPOSURE_MASK), de recevoir les signaux se rapportant à l'appui sur un bouton de la souris (GDK_BUTTON_PRESS_MASK) et au relâchement d'un bouton de la souris (GDK_BUTTON_RELEASE_MASK).
void gtk_widget_add_events(GtkWidget *widget, gint events);
Il arrive parfois que l'on souhaite connaître quel est le widget 'top level' (celui géré par le window manager) contenant un widget donné. La fonction GtkWidget *gtk_widget_get_toplevel(GtkWidget *widget); fournit cette indication.
Enfin, la fonction void gtk_widget_get_pointer(GtkWidget *widget, gint *x, gint *y); fournit les coordonnées du pointeur de la souris.
L'exemple du mois
Malheureusement, je ne peux donner un exemple d'utilisation pour chacune des fonctions et macros présentées aujourd'hui, faute de place. Seules quelques fonctions sont illustrées ici :
/* widget.c */
#include <gtk/gtk.h>
/*****************************************************
* Change l'aspect (grisé ou non) et l'activité du widget
***************************************************/
static gint ToggleSensitive(GtkWidget *widget)
{
gtk_widget_set_sensitive(widget, !(GTK_WIDGET_SENSITIVE(widget)));
}
void main(int argc, char *argv[])
{
GtkWidget *Dialogue;
GtkWidget *Cobaye;
GtkWidget *Bouton;
gtk_init(&argc, &argv);
/* Création et affichage de la fenêtre principale de l'application */
Dialogue = gtk_dialog_new();
gtk_widget_show(Dialogue);
/* Création du bouton qui va subir tous les changements d'états */
Cobaye = gtk_button_new_with_label("Coucou");
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(Dialogue)->vbox), Cobaye);
gtk_widget_set_usize(Cobaye, 300, 300);
gtk_widget_show(Cobaye);
/* Ce bouton permet de montrer notre bouton cobaye à l'aide de la fonction
* gtk_widget_show
*/
Bouton = gtk_button_new_with_label("Montre");
gtk_signal_connect_object(GTK_OBJECT(Bouton), "clicked",
(GtkSignalFunc)gtk_widget_show,
GTK_OBJECT(Cobaye));
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(Dialogue)->action_area), Bouton);
GTK_WIDGET_SET_FLAGS(Bouton, GTK_CAN_DEFAULT);
gtk_widget_show(Bouton);
/* Ce bouton permet de cacher notre bouton cobaye à l'aide de la fonction
* gtk_widget_hide
*/
Bouton = gtk_button_new_with_label("Cache");
gtk_signal_connect_object(GTK_OBJECT(Bouton), "clicked",
(GtkSignalFunc)gtk_widget_hide,
GTK_OBJECT(Cobaye));
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(Dialogue)->action_area), Bouton);
GTK_WIDGET_SET_FLAGS(Bouton, GTK_CAN_DEFAULT);
gtk_widget_grab_default(Bouton); /* Ce bouton est le bouton par défaut */
gtk_widget_show(Bouton);
/* Ce bouton permet de passer de l'état grisé à l'état actif et vice versa */
Bouton = gtk_button_new_with_label("Change Grisé");
gtk_signal_connect_object(GTK_OBJECT(Bouton), "clicked",
(GtkSignalFunc)ToggleSensitive,
GTK_OBJECT(Cobaye));
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(Dialogue)->action_area), Bouton);
GTK_WIDGET_SET_FLAGS(Bouton, GTK_CAN_DEFAULT);
gtk_widget_grab_default(Bouton);
gtk_widget_show(Bouton);
/* Ce bouton nous permet de quitter l'application */
Bouton = gtk_button_new_with_label("Quitter");
gtk_signal_connect(GTK_OBJECT(Bouton), "clicked", (GtkSignalFunc)gtk_exit,
NULL);
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(Dialogue)->action_area), Bouton);
GTK_WIDGET_SET_FLAGS(Bouton, GTK_CAN_DEFAULT);
gtk_widget_grab_default(Bouton);
gtk_widget_show(Bouton);
/* On passe la main a GTK+ */
gtk_main();
}
à compiler comme toujours avec :
gcc widget.c -o widget `gtk-config --cflags --libs`
ou
gcc widget.c -o widget $(gtk-config --cflags --libs)
Essayez de bien comprendre tout ce qui se passe pendant le déroulement de ce programme. Vous pouvez aussi changer la nature de Cobaye avec un Cobaye = gtk_label_new("coucou"); par exemple, afin de voir comment chacun des widgets se comporte.
La prochaine fois
La prochaine fois, nous continuerons notre tour d'horizon des widgets de GTK+, avec notamment une présentation des GtkContainers et de leurs dérivés.
David.Odin@bigfoot.com