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