GTK+ est une bibliothèque de fonctions permettant de créer facilement et rapidement une interface utilisateur dans l'environnement X-Window. Les initiales G, T et K signifient Gimp ToolKit (la boîte à outils de Gimp) car cette bibliothèque a été développée à l'origine pour servir d'interface utilisateur à ce superbe logiciel de retouche d'images. GTK+ est distribué sous la licence LGPL, ce qui permet à tous de l'utiliser, de la faire évoluer, sans avoir à payer le moindre droit d'auteur et ce, même dans un contexte professionnel.
Les versions de GTK+
Comme beaucoup de logiciels libres, GTK+ est disponible en plusieurs versions. Une version dite "stable" (actuellement la 1.0.6) et une version dite "de développement" (actuellement la 1.2.1). En général, on reconna"t qu'une version est stable à la parité du deuxième chiffre de la version (le '0' dans 1.0.6). Ici, la version 1.2.1 n'est pas reconnue comme stable (mais cela ne devrait pas tarder) car elle est encore très jeune et de nombreux programmes utilisent encore GTK+-1.0.x. Malheu reusement les versions 1.0.x et 1.2.x ne sont pas totalement compatibles. Un programme prévu pour GTK+-1.0.x ne se recompile pas toujours avec GTK+-1.2.x et réciproquement. Pour nos exemples, lorsque la version GTK+-1.2.x sera absolument nécessaire, ce sera clairement indiqué. En règle générale, nous nous restreindrons aux fonctions communes. Bien entendu, même si vous choisissez de développer vos propres programmes avec GTK+-1.2.x, vous pourrez continuer d'utiliser les programmes prévus GTK+-1.0.x (tant que vous n'essayez pas de les recompiler...)
En fait, parler de LA bibliothèque GTK+ est légèrement incorrect puisqu'il s'agit en réalité de trois bibliothèques l'une sur l'autre. La première, Glib, est destinée à simplifier la vie du programmeur en proposant des fonctions de gestion de mémoire, de listes chaînées, de chaînes de caractères... La bibliothèque Glib est distribuée séparément de GTK+ depuis la version 1.1.x.
La deuxième, GDK (Gimp Drawing Kit), constitue une surcouche à la bibliothèque Xlib (la bibliothèque de base de X-Window). Elle représente une sorte d'abstraction entre GTK+ et X-Window; ce qui a permis, entre autres, de porter GTK+ sous d'autres environnements graphiques comme les Microsoft Windows.
La troisième est bien sûr GTK+ elle-même. Elle utilise activement les deux précédentes, et fournit les fonctions de gestion des Widgets.
Installation
Pour créer vos propres programmes en utilisant la bibliothèque GTK+, vous devez installer une version complète de GTK+, c'est-à-dire l'ensemble des fichiers .so et .h. Cela peut être fait en installant les fichiers .rpm gtk+-1.0.6-x.x.rpm et gtk+-devel-1.0.6-x.x.rpm, si vous avez une distribution Linux qui les supporte ; ou les fichiers libgtk1_1.0.6-x.deb et libgtk-dev_1.0.6-x.deb, si vous possédez une distribution Debian;.
ou en compilant vous même ces packages.
Rassurez-vous, c'est très facile à compiler soi-même. Il suffit de récupérer le fichier gtk+-1.0.6.tar.gz, de le décompacter avec la commande
tar xvfz gtk+-1.0.6.tar.gz,
puis
cd gtk+-1.0.6
./configure
make
(devenez root si nécessaire)
make install
ldconfig
et voilà !
Lisez le fichier INSTALL pour plus d'informations.
Si vous choisissez d'installer la version 1.1.x ou 1.2.x, la procédure est la même si ce n'est que vous devez auparavant installer la bibliothèque glib correspondante.
Le programme le plus simple possible
/* simple.c */
#include <gtk/gtk.h>
void main(int argc, char *argv[])
{
GtkWidget *Fenetre;
gtk_init(&argc, &argv);
Fenetre = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(Fenetre);
gtk_main();
}
constitue l'un des programmes les plus simples que l'on puisse écrire en utilisant la bibliothèque GTK+. Il affiche simplement une fenêtre carrée et attend simplement de se faire supprimer (en 'killant' l'application).
Décortiquons un peu ce petit programme :
La ligne
#include <gtk/gtk.h>
est obligatoire dans tout programme GTK+. Par cette ligne, on inclut tous les symboles, types, variables, macros et fonctions de GTK+.
Le prototype de la fonction main
void main(int argc, char *argv[])
doit comporter ces deux arguments car GTK+ en a besoin pour conna"tre d'éventuels arguments d'invocation que l'utilisateur pourrait passer à GTK+.
L'appel de la fonction
gtk_init(&argc, &argv);
est également obligatoire. Elle initialise la bibliothèque GTK+ avec les arguments de la ligne de commande. Elle retire les arguments qu'elle a utilisés. Elle doit bien sûr être la première des fonctions GTK+ à être appelée.
La fonction
gtk_window_new(GTK_WINDOW_TOPLEVEL);
crée une fenêtre. Le paramètre GTK_WINDOW_TOPLEVEL signifie qu'il s'agit d'une fenêtre qui doit être prise en compte par le window manager avec toutes les décorations habituelles. Les autres valeurs possibles pour ce paramètre sont GTK_WINDOW_DIALOG (un peu moins de décoration) pour les bo"tes de dialogue et GTK_WINDOW_POPUP (aucune décoration) pour les fenêtres destinées à être fermées rapidement. Les dimensions par défaut de la fenêtre créée sont 200x200 pixels. La fenêtre n'est pas encore affichée ni même prise en compte par GTK+. En fait, elle n'a même pas encore d'existence au sens X-Window du terme. C'est le rôle de la fonction
gtk_widget_show(Fenetre);
La fonction gtk_main(); donne le contrôle de notre programme à GTK+ qui n'a pas grand chose à faire ici, si ce n'est gérer l'affichage de notre fenêtre et son éventuel redimensionnement.
Saisissez ce programme et compilez le par la commande :
gcc simple.c -o simple `gtk-config --cflags --libs`
Le script gtk-config est un programme fourni avec la bibliothèque GTK+ et qui conna"t les bons paramètres à passer à gcc pour compiler un programme GTK+.
Si vous lancez le programme ./simple, vous devez avoir sur votre bureau X-Window une fenêtre de 200x200 que vous pouver redimensionner. Ce n'est pas très passionnant, mais c'est un début.
HelloWorld
Voici le classique programme 'hello world' écrit en GTK+.
/* helloworld.c */
#include <gtk/gtk.h>
void main(int argc, char *argv[])
{
GtkWidget *Dialogue, *Label, *Bouton;
gtk_init(&argc, &argv);
/* La fenêtre principale */
Dialogue = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(Dialogue), "Bonjour, monde");
gtk_widget_show(Dialogue);
/* Le label "bonjour monde" */
Label = gtk_label_new("Bonjour, monde");
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(Dialogue)- >vbox), Label, TRUE, TRUE, 0);
gtk_widget_show(Label);
/* Le bouton fermer */
Bouton = gtk_button_new_with_label("Fermer");
gtk_signal_connect_object(GTK_OBJECT(Bouton), "clicked",
(GtkSignalFunc)gtk_exit, NULL);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(Dialogue)->action_area), Bouton,
TRUE, TRUE, 0);
gtk_widget_show(Bouton);
gtk_main();
}
Ici, à la place de gtk_window_new(), nous utilisons gtk_dialog_new() qui crée une fenêtre un peu plus évoluée avec une zone appelée 'vbox' en haut pour mettre toutes les informations que l'on souhaite (ici un label 'Bonjour monde') et une zone appelée 'action_area' en dessous plutôt destinée à accueillir des boutons. Bien sûr vous pouvez tout à fait utiliser ces zones à d'autres fins. Mais n'oubliez pas que les utilisateurs aiment bien que l'on respecte leurs habitudes !
La fonction gtk_label_new("Bonjour, monde"); crée un label (un texte). On peut remarquer ici la façon dont sont nommées les fonctions GTK+. Elles commencent toutes par "gtk_" suivi du nom du widget sur lequel porte la fonction.
La fonction
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(Dialogue)- >vbox), Label, TRUE, TRUE, 0);
nous permet de placer le Label dans la zone 'vbox' de la bo"te de dialogue. Ensuite, on crée un bouton avec un label par la fonction
gtk_button_new_with_label("Fermer");
En règle générale, un bouton peut contenir à peu près n'importe quoi, par exemple une image. Il faut alors créer séparement le widget que l'on veut et le placer nous-même dans le bouton. Mais comme, le plus souvent, on place un texte dans un bouton, GTK+ fournit une fonction toute prête pour cela.
La fonction
gtk_signal_connect_object(GTK_OBJECT(Bouton), "clicked",
(GtkSignalFunc)gtk_exit, NULL);
relie le signal "clicked" de l'objet Bouton à la fonction gtk_exit(). En gros, on indique à GTK+ que lorsque l'utilisateur clique sur le bouton 'Bouton', il faut appeler la fonction gtk_exit() qui, comme son nom l'indique, termine la boucle principale de GTK+ et termine donc souvent le programme. La fonction suivante place le bouton "Fermer" dans la zone 'action_area' de la bo"te de dialogue. Et voilà notre programme Hello world !
Vous pouvez le compiler comme précédemment par :
gcc helloworld.c -o helloworld `gtk-config --cflags --libs`
Les widgets
Depuis le début de cet article, je vous parle sans arrêt de 'widget' sans vraiment avoir précisé de quoi il s'agissait. "Widget" est la contraction de "window gadget". En fait tout ce qui peut constituer un élément d'interface utilisateur est un widget. On trouve par exemple, les fenêtres, les boutons, les ascenseurs, les labels, les zones de textes, les bo"tes invisibles qui permettent de ranger tout cela... La bibliothèque GTK+ étant organisée de façon 'objet', chaque widget hérite des caractéristiques de ses ancêtres. Ainsi, dans notre exemple, le widget 'Dialogue' peut utiliser la fonction
gtk_window_set_title(GTK_WINDOW(Dialogue), "Bonjour, monde");
car les widgets de type 'dialog' descendent directement des widgets de type 'window'. Cette hiérarchisation en arbre d'objets est très pratique car elle permet d'utiliser les mêmes fonctions sur des widgets différents. Le problème est d'arriver à se souvenir de la hiérarchie de ces objets. Rassurez-vous, cela vient vite.
Les signaux et les fonctions de rappel
Une fois que notre interface utilisateur est définie, il faut définir comment chaque widget doit réagir aux interventions de l'utilisateur. En fait, à chaque fois qu'il arrive quelque chose à un widget, celui-ci envoie un signal à GTK+ qui appelle alors la fonction définie par défaut pour ce widget et ce signal. C'est comme cela que, par exemple, GTK+ sait qu'il doit redessiner un widget qui redevient visible car l'utilisateur a bougé une fenêtre. On peut bien sûr ajouter des fonctions à celles qui sont définies par défaut. C'est le rôle de la fonction
int gtk_signal_connect(GtkObject *object,
const char *signal_name,
GtkSignalFunc func,
void *func_data);
qui connecte le signal 'signal_name' de l'objet 'object' (qui sera souvent mais pas toujours un widget) à la fonction 'func' (appelée fonction de rappel ou 'CallBack' en anglais) qui recevra le paramètre 'func_data' en troisième paramètre. En clair, dès que l'utilisateur fait une action qui incite l'objet 'object' à émettre le signal 'signal_name', la fonction 'func' est appelée avec comme paramètre : l'objet 'object', l'événement (GdkEvent *) qui a généré le signal, et en troisième position func_data. Il faut donc faire attention à ce que la fonction 'func' ait le prototype suivant :
int func(GtkObject *object, GdkEvent *event, void *func_data);
La valeur de retour de cette fonction doit être TRUE, si le signal a été complètement traité par notre fonction et FALSE si GTK+ doit continuer de traiter le signal par les fonctions définies par défaut. En général, il vaut mieux retourner FALSE, sauf si l'on sait vraiment ce que l'on fait.
Dans le programme helloworld.c, on utilise la fonction:
int gtk_signal_connect_object(GtkObject *object,
const char *signal_name,
GtkSignalFunc func,
GtkObject *object_data);
qui est équivalente à la fonction gtk_signal_connect, si ce n'est que la fonction de rappel n'a qu'un seul paramètre (object_data). En général, on utilise plutôt cette forme pour attacher une fonction interne de GTK+ à un signal (comme dans notre exemple).
La prochaine fois
Et oui ! C'est tout pour cette fois !
Dans le prochain article, nous verrons qu'il existe vraiment beaucoup de widgets différents. Nous étudierons plus particulièrement les façons de ranger ces widgets dans nos fenêtres avec les GtkBox et GtkTable. D'ici là, n'hésitez pas à me demander des conseils par E-Mail, ou mieux encore pour les anglophones, sur la mailing-list gtk-app-devel.
David.Odin@bigfoot.com
Créateur du logiciel de modélisation pour POV : Giram