Introduction à la programmation en GTK

GIMP est désormais une application inévitable de Linux, mais savez-vous qu'il est développé avec une librairie dont sa simplicité n'a d'égale que sa puissance ? Je parle en effet de GTK, une librairie permettant le développement d'interfaces graphiques dans notre bon vieux langage qu'est le C, et ceci très simplement.
Avant de commencer, il est toutefois nécessaire de décrire brièvement quelques principes fondamentaux à la programmation en GTK.

Les Widgets

Chaque élément (bouton, menu, barre de progression, ...) d'une interface graphique est appelé Widget. Pour déclarer un widget, rien de plus simple :

GtkWidget *Nom_du_widget;

Ensuite, il est prêt à être initialisé en fonction de sa nature :
Main_Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
dans le cas d'une fenêtre. (L'argument correspond au type de fenêtre voulu). Pour une barre de progression, on aura :
Barre_1 = gtk_progress_bar_new();
Se reporter à la documentation de GTK pour plus de détails.

Les boîtes

Pour GTK, une fenêtre est par définition un container qui ne peut contenir qu'une seule boîte. Cependant cette boîte peut contenir elle-même un nombre illimité d'autres boîtes. C'est pourquoi, pour chaque programme, il est nécessaire d'utiliser au moins une boîte :

GtkWidget *Main_Box;
Main_Box = gtk_vbox_new(FALSE, 5);

Ces deux lignes créent une boîte qui va contenir des Widgets emilés en hauteur (verticale). Pour introduire cette boîte dans une fenêtre, rien de plus simple :

GtkWidget *Main_Window;
GtkWidget *Main_Box;

Main_Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Main_Box = gtk_vbox_new(FALSE, 5);    gtk_container_add(GTK_CONTAINER(Main_Window), Main_Box);
gtk_widget_show(Main_Box);

Les deux premières lignes correspondent à la déclaration de deux variables de type GtkWidget. Ensuite, la fonction gtk_window_new crée une fenêtre et la fonction gtk_vbox_new une boîte verticale. La fonction gtk_container_add ajoute la boîte Main_Box au container qu'est la fenêtre Main_Window. Ensuite on affiche la boîte, celle-ci recevant par la suite des éléments, avec :

gtk_box_pack_start(GTK_BOX(Box), Label_M_, TRUE, TRUE, 5);
gtk_widget_show(Label_M_);

La fonction gtk_box_pack_start introduit le widget Label_M_ par le haut (correspondant au début pour une boîte verticale). On affiche le widget aussitôt après. Mais une boîte peut très bien recevoir elle-même d'autres boîtes. Par exemple, pour les deux labels (voir figure 1),  j'ai créé une boîte horizontale dans laquelle j'ai introduit les deux labels, avant d'inclure cette boîte dans la boîte principale par :

gtk_container_add(GTK_CONTAINER(Main_Box), Box);
gtk_widget_show(Box);

La programmation événementielle

Une interface graphique, c'est (pour le non programmeur) d'abord une jolie fenêtre, avec tout plein de boutons partout, des entrées ou sortie de textes, etc.Mais c'est aussi et surtout (pour le programmeur) des fonctions associées à des événements comme le clic sur un bouton, le clic sur la petite croix pour fermer le programme, etc. Pour cela, il est nécessaire d'associer à des événements particuliers, des fonctions comme
pour la fermeture du programme :
 

gtk_signal_connect(GTK_OBJECT(Window), "delete_event", GTK_SIGNAL_FUNC(Quit), NULL);
La fonction gtk_signal_connect, associe à l'événement "delete_event" de l'objet Window (renvoyé par le Windows_Manager lors du clic sur la croix), la fonction Quit qui est appelé sans argument. (NULL).

Un mot concernant la fonction Scan

Si vous débutez dans la programmation en C, cette fonction vous paraîtra peut être floue. Elle fait, en effet appel,  à des fonctions du type : fscanf, atoi et sprintf. Si vous ne connaissez pas ces fonctions, consultez les pages man. Remarquez que ces fonctions permettent juste une lecture du fichier /proc/meminfo, et ne sont d'aucune manière que ce soit, nécessaires à la création d'une application en GTK.

X-Mem-Stat

Nous voilà prêts à rentrer dans le vif du sujet, à savoir un petit utilitaire permettant de connaître la mémoire disponible (mémoire vive et swap) grâce à deux barres de progression.

/* Ce programme permet de se renseigner sur la mémoire
libre (réelle et swap). Il a été écrit en GTK 1.0.1

Auteur : Bruno BONFILS (asyd@minitel.net)
Contactez moi si vous trouvez des bugs... :-(
Date : 30 Aout 1998

L'avertissement : warning: passing arg 2 of 'gtk_timeout_add' from ...n'est pas important das le sens où l'application fonctionne très bien.

*/
/* X-Mem Stat v0.2.3 */

#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <string.h>

// Variables globales :

unsigned int MEM_FREE; // Mémoire libre
unsigned int MEM_;  // Mémoire totale
unsigned int SWAP_FREE; // Swap libre
unsigned int SWAP_;  // Swap totale

GtkWidget *Mem_Bar; // Barre pour la mémoire libre
GtkWidget *Swap_Bar; // Barre pour la  SWAP libre
GtkWidget *Label_M_Free;   // Label pour la mémoire libre
GtkWidget *Label_S_Free; // Label pour la SWAP libre
GtkWidget *Label_M_; // Label pour : "Mémoire libre :"
GtkWidget *Label_S_; // Label pour : "SWAP libre :"

// Lit la mémoire disponible et rafraîchit les progress bars ainsi que les labels void Scan(void)

{
FILE *File_Info;
char *Fichier;
char *Temp;
int index;
float Value;
 
Fichier = (char *) malloc(1024);
Temp = (char *) malloc(16);
if((File_Info = fopen("/proc/meminfo","rb"))==NULL)
{
g_error("Le fichier /proc/meminfo ne peut être ouvert.");
exit(-1);
}
for(index=0;index<19;index++)
fscanf(File_Info,"%s", Fichier);
MEM_ = atoi(Fichier);
fscanf(File_Info, "%s", Fichier);
fscanf(File_Info, "%s", Fichier);
fscanf(File_Info, "%s", Fichier);
MEM_FREE = atoi(Fichier);
for(index=0;index<12;index++)
fscanf(File_Info, "%s", Fichier);
SWAP_ = atoi(Fichier);
fscanf(File_Info, "%s", Fichier);
fscanf(File_Info, "%s", Fichier);
fscanf(File_Info, "%s", Fichier);
SWAP_FREE = atoi(Fichier);
fclose(File_Info);

free(Fichier);
Value = ((float) MEM_FREE / MEM_);

// Fonction permettant de changer la valeur d'une barre de progression
gtk_progress_bar_update(GTK_PROGRESS_BAR(Mem_Bar),Value);
Value = Value * 100;
sprintf(Temp, "%3.2f %%", Value);

// Permet de remettre à jour le texte d'un label :
gtk_label_set(GTK_LABEL(Label_M_Free), Temp);
Value = ((float) SWAP_FREE / SWAP_);
gtk_progress_bar_update(GTK_PROGRESS_BAR(Swap_Bar),Value);
Value = Value * 100;
sprintf(Temp, "%3.2f %%", Value);
gtk_label_set(GTK_LABEL(Label_S_Free), Temp);
free(Temp);
}

// Fonction appelée lors du clic sur le bouton fermeture de la barre de titre

gint Quit(GtkWidget *Widget, gpointer *Data)
{
g_print("Mem Status v0.2.3: Bruno BONFILS: 30 Aout 98");

// Fonction appelée pour la fermeture du programme
gtk_main_quit();
return(TRUE);
}
int main(int argc, char * argv[])
{
GtkWidget *Window;  // Fenêtre
GtkWidget *Main_Box;  // Boîte principale verticale
GtkWidget *Box;  // Boîte temporaire (horizontale)
gtk_init(&argc, &argv); // Fonction nécessaire à
l'initialisation de GTK

// On créé une fenêtre
Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

// On modifie le titre de cette fenêtre
gtk_window_set_title(GTK_WINDOW(Window), "X Mem Stat");

// Création de la boîte principale verticale
Main_Box = gtk_vbox_new(FALSE, 5);

// qu'on ajoute aussitôt à la fenêtre
gtk_container_add(GTK_CONTAINER(Window), Main_Box);

// On ajoute une bordure de taille 5 au container Window
gtk_container_border_width(GTK_CONTAINER(Window), 5);

// Associe la fonction Quit à l'événement "delete_event" (renvoyé par le Window_Manager)

//  de l'objet Window
gtk_signal_connect(GTK_OBJECT(Window), "delete_event", GTK_SIGNAL_FUNC(Quit), NULL);

// Création des deux progress_bar
Mem_Bar = gtk_progress_bar_new();
Swap_Bar = gtk_progress_bar_new();

// Création des labels :
Label_M_Free = gtk_label_new("0");
Label_M_ = gtk_label_new("Mémoire libre :");
Label_S_Free = gtk_label_new("0");
Label_S_ = gtk_label_new("SWAP libre :");

// Création d'une boîte horizontale pour faire tenir deux labels sur une même ligne
Box = gtk_hbox_new(TRUE, 5);

// On ajoute le premier label à cette boîte
gtk_box_pack_start(GTK_BOX(Box), Label_M_, TRUE, TRUE, 5);
gtk_widget_show(Label_M_);

// On ajoute le second label
gtk_box_pack_start(GTK_BOX(Box), Label_M_Free, TRUE, TRUE, 5);
gtk_widget_show(Label_M_Free);

// On ajoute cette boîte à la boîte principale
gtk_container_add(GTK_CONTAINER(Main_Box), Box);
gtk_widget_show(Box);

// On ajoute ensuite la première barre de progression à la boîte principale
gtk_box_pack_start(GTK_BOX(Main_Box), Mem_Bar, TRUE, TRUE, 5);
gtk_widget_show(Mem_Bar);
 
// On réinitialise la boîte temporaire pour introduire les deux seconds labels
Box = gtk_hbox_new(TRUE, 5);
gtk_box_pack_start(GTK_BOX(Box), Label_S_, TRUE, TRUE, 5);
gtk_widget_show(Label_S_);
gtk_box_pack_start(GTK_BOX(Box), Label_S_Free, TRUE, TRUE, 5);
gtk_widget_show(Label_S_Free);
gtk_container_add(GTK_CONTAINER(Main_Box), Box);
gtk_widget_show(Box);

// Puis on ajoute enfin la seconde barre de progression
gtk_box_pack_start(GTK_BOX(Main_Box), Swap_Bar, TRUE, TRUE, 5);
gtk_widget_show(Swap_Bar);

// Appelle la fonction Scan sans paramètre (NULL) toutes les 500 milli-secondes.
gtk_timeout_add(500, Scan, NULL);

// On affiche enfin la boîte principale, puis la fenêtre complète
gtk_widget_show(Main_Box);
gtk_widget_show(Window);

// Cette fonction permet l'initialisation de GTK et est obligatoire.
gtk_main();
return(0);
}
Voilà, j'espère que cet exemple vous a permis d'entrevoir la facilité de programmation en GTK, et qu'il vous permettra de développer de nombreuses interfaces graphiques.

    Bruno BONFILS
    asyd@minitel.net  

 

 


© Copyright 2000 Diamond Editions/Linux magazine France - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".