#include <gtk/gtk.h> main(int argc, char * argv[]){ gtk_init(&argc,&argv); gtk_main(); }La fonction
gtk_init
se contente de regarder les options
passées au programme, de prendre celles qui concernent gtk (par
exemple, -display
) et de contacter le serveur X.
La fonction gtk_main
est la boucle principale, dans
laquelle on attend les évènements.
Pour compiler ce programme, on peut utiliser la commande
gtk-config
qui connait les bonnes options à donner à
gcc
.
gcc -o toto toto.c `gtk-config --cflags --libs`
GtkWidget * w; w=gtk_window_new(GTK_WINDOW_TOPLEVEL); ... gtk_widget_show(w);La commande
gtk_widget_show
demmande l'affichage d'un
widget, quel qu'il soit. Si on l'oublie, rien n'apparaît.
On remarque que tous les widgets ont le même type,
GtkWidget *
. Néanmoins certaines commandes attendent un
widget d'un type particulier. En C++, on ne se poserait pas de
question, les mécanismes d'héritage s'occuperaient de tout. Mais nous
sommes en C : il va falloir mettre des casts dans tous les sens. Gtk
foirnit des macros à cet effet ; elles sont toutes semblables à
l'exemple suivant.
GTK_CONTAINER(w)Il est maintenant temps de peupler notre fenêtre, disons, avec un bouton.
GtkWidget * b; b=gtk_button_new_with_label("bouton 1"); gtk_container_add(GTK_CONTAINER(w), b); gtk_widget_show(b);
#include <gtk/gtk.h> main(int argc, char * argv[]){ GtkWidget * w; GtkWidget * b; gtk_init(&argc, &argv); w=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(w), "Coucou !"); gtk_container_set_border_width(GTK_CONTAINER(w), 10); b=gtk_label_new("Essai"); gtk_misc_set_alignment(GTK_MISC(b),0,0); gtk_container_add(GTK_CONTAINER(w), b); gtk_widget_show(b); b=gtk_hseparator_new(); gtk_widget_set_usize(b,400,5); gtk_container_add(GTK_CONTAINER(w), b); gtk_widget_show(b); b=gtk_button_with_label("OK"); gtk_container_add(GTK_CONTAINER(w), b); gtk_widget_show(b); gtk_widget_show(w); gtk_main(); }
GtkWidget * w; GtkWidget * box; GtkWidget * b; w=gtk_window_new(GTK_WINDOW_TOPLEVEL); box=gtk_hbox_new(FALSE,0); b=gtk_button_new_with_label("bouton 1"); gtk_box_pack_start(GTK_BOX(box), b, TRUE, TRUE, 0); gtk_widget_show(b); b=gtk_button_new_with_label("bouton 2"); gtk_box_pack_start(GTK_BOX(box), b, TRUE, TRUE, 0); gtk_widget_show(b); gtk_widget_show(box); gtk_widget_show(w); gtk_main();Le premier argument de la commande
gtk_hbox_new
dit si tous les widgets doivent avoir la même taille.
Le second est l'espace entre deux widgets.
La commande
gtk_box_pack_start
met les widgets dans une boite (à gauche ou en haut, selon la nature
de la boite).
Son troisième argument (expand
) dit s'il faut prendre
toute la place disponible.
Le quatrième argument (fill
) dit s'il faut la prendre en
augmentant la taille du widget. ou en rajoutant de l'espace sur ses
côtés.
Le dernier argument est l'espace à rajouter sur les côtés.
Il existe d'autres manières de positionner les widgets, en particulier
les table
s ; nous n'en parlerons pas ici.
gtk_signal_connect( GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(affiche), (gpointer) "Coucou !" );La fonction, quant à elle, sera défiunie ainsi.
void affiche( GtkWidget * w, gpointer data ){ printf("%s\n", (char *) data); }Le second argument de la commande
gtk_signal_connect
est un signal envoyé par un widget. Le troisème argument est une
fonction (convenablement castée) et le dernier argument sera passé en
paramètre à la fonction (si on n'en a pas besoin, on peut mettre
NULL
).
Il est possible de desassocier une fonction du signal auquel elle
a été préalablement associée en récupérant l'entier renvoyé par la
commande
gtk_signal_connect
et en le donnat à la commande
gtk_signal_disconnect
.
gint id; id = gtk_signal_connect(...); ... gtk_signal_disconnect( GTK_OBJECT(w), id );Il est même possible de tout enlever.
gtk_signal_handlers_destroy( GTK_OBJECT(w), id );
(Je déconseille de lire ce qui suit.)
Il y a une différence entre signal (envoyé par Gtk,
spécifique à un widget, par exemple "cliquer" sur un bouton) et
évènement (envoyé par le serveur X, d'un niveau plus
bas). Les signaux correspondent à des évènements particuliers. Il est
possible de remplacer le signal
"clicked"
dans l'exemple ci-dessus, mais la définition de
la fonction affiche
devra être changée.
void affiche( GtkWidget * w, GdkEvent * e, gpointer data )
il y a une autre commande, qui fait à peu près la même chose que
gtk_signal_connect
, mais qui appelle des fonctions avec
un seul argument, le widget. [Je ne sais pas pourquoi il faut préciser
le widget parent, w
.]
gtk_signal_connect_object( GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(affiche), GTK_OBJECT(w) );
void affiche( GtkWidget * w ){ printf("Coucou !\n"); }
Contrairement à d'autres bibliothèques (Tk), on ne gère pas séparément ce qui se passe horizontalement et verticalement (expand, fill, padding).
Il faut dire explicitement ce qu'il faut faire quand on reçoit le
signel destry
du gestionnaire de fenêtres.
C'est de la programmation orientée objets en C, l'héritage de fait avec des casts.
Il est (comme toujours) très pénible de mettre les widgets là où il faut. Y a-t-il un utilitaire graphique pour faire cela ?
Je n'ai pas parlé des fonctions
gtk_main_quit()
(pour quitter) et
gtk_exit()
(pour quitter violemment).
Je n'ai pas parlé des sélections.
Je n'ai pas parlé du fichier de configuration .gtkrc
.
Pour les dessins : le widget drawing area
est de
beaucoup plus bas niveau que le canvas
de Tk : par
exemple, il faut s'occuper soi-même du double-buffering. De surcroit,
il n'y a pas de documentation. J'ai l'impression qu'il n'y a pas de «
tags » comme avec Tk.
Je n'ai pas regardé les interfaces en C++ et en Perl.
J'aurais pu parler des horloges. Pour appeler une fonction dans 300 ms :
id=gtk_timeout_add(300, (GtkFunction)toto, NULL); gboolean toto(gpointer data){ ... return FALSE; }pour l'appeler toutes les 300 ms :
id=gtk_timeout_add(300, (GtkFunction)toto, NULL); gboolean toto(gpointer data){ ... return TRUE; }pour desactiver une telle fonction :
gtk_timeout_remove(id);