GtkFr - Cours Gtk+-2

LibGlade

PageAccueil :: LesNews :: Telechargement :: Liens :: Forum :: LeChat :: Contact
DerniersChangements :: DerniersCommentaires :: ParametresUtilisateur :: Vous êtes 38.107.191.94
La libglade est une bibliothèque associée au programme Glade et Gtk+. Son but est, entre autres, de pouvoir lire le fichier d'un projet Glade. Lorsque l'on design une interface graphique avec Glade, celui-ci sauvegarde l'ensemble du projet (widgets et signaux) dans un fichier xml .glade .

La libglade permet donc de lire ce fichier depuis son programme, et affiche immédiatement l'interface. On n'a plus besoin du code généré par Glade. En effet, lorsque l'on programme en Gtk+ plusieurs solutions s'offrent à nous :

* On décide de coder l'interface soi-même. Pour cela, on imaginera l'I.H.M et on codera directement celle-ci, façon guerrier ninja de la nuit :)
* On utilise Glade, on génère le code et on intègre celui-ci dans notre programme.

Entre ces deux extrêmes la libglade apporte une solution élégante. On obtient l'avantage de pouvoir construire l'interface graphiquement avec Glade, ce qui est très utile pour les programmes possédant plus de deux boutons et une fenêtre (vous remarquerez que la plupart des tutoriels s'arrêtent comme par hasard à ce genre de démo :). Et l'on ne subit pas la gestion d'un code généré (Note). De plus le code généré ne peut être qu'en C, C++ ou Ada.

L'avantage de la libglade prend encore une nouvelle dimension lorsque l'on sait que celle-ci est utilisable depuis du C, C++, python, ruby, perl, java, ... L'interface n'est donc plus dépendante d'un langage de programmation, on peut à loisir en changer ou mixer les langages pour en tirer le meilleur (par exemple C et python).

Autre avantage, l'interface est lue dynamiquement au lancement de l'application. Il est donc possible de modifier celle-ci sans avoir besoin de recompiler, tant qu'on ne touche pas aux widgets utilisés par l'application bien sûr. Un utilisateur avancé pourra ainsi modifier le contenu d'un label ou changer une image.

Enfin, même les signaux sont gérés. Dans l'onglet signaux d'un objet, il suffit de fournir le signal et la fonction appelée. Dans son code, il ne faut créer que la fonction de callback, la libglade s'occupe du g_signal_connect(). Par contre, j'ai dû moi-même gérer le g_signal_connect() lorsque j'avais besoin de passer un pointeur de data.

site :
http://www.daa.com.au/~james/software/libglade/
doc :
http://developer.gnome.org/doc/API/2.0/libglade/index.html
Glade :
http://glade.gnome.org/

SOURCES :
Voici quelques codes source que j'utilise pour charger via la libglade les différents widgets dont j'ai besoin.
Rappel :

Tout d'abord, il est essentiel de structurer son code pour que l'on puisse s'y retrouver. J'utilise donc une structure globale dédiée à l'interface qui contiendra les widgets auxquels j'ai besoin d'accéder comme les boutons. Il n'est donc pas nécessaire d'avoir dans cette structure tous les widgets de l'interface (cela peut paraitre évident mais pas forcément pour tout le monde :).

Le fichier interface.h qui contient la définition de cette structure est inclus dans tous les .c, ce qui permet de manipuler les widgets depuis n'importe où dans le programme. Ce fichier contient aussi la déclaration extern de l'initialisation : extern Interface GismWidget?;

interface.h :
#include 

typedef struct
{
  /* window */
  GtkWidget *main;
  GtkWidget *monitoring;
  GtkWidget *about;
  GtkWidget *menu_park;
  /**********/

  GtkWidget *hbox_gism;
  GtkWidget *vpaned_shell;
  GtkWidget *hpaned_customer;
  GtkWidget *hpaned_user;
  GtkWidget *hpaned_park;
  GtkWidget *hpaned_doc;
  GtkWidget *hpaned_main;
  GtkWidget *dbgrid_customer;
  GtkWidget *notebook_shell;
  GtkWidget *notebook_park;
  GtkWidget *togglebutton_customer;
  GtkWidget *togglebutton_park;
  GtkWidget *togglebutton_user;
  GtkWidget *togglebutton_doc;

  GtkWidget *treeview_park;
  GtkWidget *scrolledwindow_park;
  GtkWidget *canvas_park;
  GtkWidget *textview_gismlog;

  gint iPanedPosition;
  gint iPanedShellPosition;
  GtkWidget *gism_term;
}
Interface;

extern Interface GismWidget;

void gism_gui_load (void);


On y ajoutera, petit à petit, chaque widget que l'on a besoin de manipuler au fur et à mesure du développement.
Au départ, je n'avais que la fenêtre et quelques boutons. Ensuite, on va créer un fichier interface.c qui va
initialiser cette structure, initialiser la libglade et charger, via les fonctions de la libglade, les widgets dans
la structure.

interface.c :
#include 
#include 
#include "interface.h"

// gruik !
#define DATADIR "/home/fred/dev/gism/src/"

// le pointeur vers l'interface
GladeXML *gui_glade_xml = NULL;

// on définit la variable globale de type de notre structure.
Interface GismWidget;

int gism_gui_error (gchar *error) {
	g_printf("%s\n", error);
	exit(1);
}


// fonction de chargement d'un widget avec test 
void gism_widget_load (GladeXML *glade_xml, gchar *glade_widget, GtkWidget **gism_widget) {

	gchar *msg_error;
	*gism_widget =  glade_xml_get_widget (glade_xml, glade_widget);

	if (!*gism_widget) {
		msg_error = g_strconcat ("libglade error : can't get *", glade_widget, "* widget", NULL);
		gism_gui_error(msg_error);
	}
}



void gism_gui_load (void) {

	gui_glade_xml = glade_xml_new(DATADIR "gism.glade", "main", NULL);

	/* chargement de l'interface
	   le premier paramètre est le nom du fichier glade. Si le deuxième est à NULL,
           la fonction chargera TOUT le projet et affichera d'un coup TOUTES les fenètres qu'il contient.
	   Si l'on souhaite éviter cela on précise en deuxième paramètre le nom du widget racine.
	   ici "main" est le nom d'une fenêtre du projet. On accèdera donc via le pointeur gui_glade_xml
	   à cette fenètre et à TOUS les widgets qu'elle contient.
	   Pour accéder à d'autres fenêtres on répètera l'opération glade_xml_new sur un autre
	   pointeur.
	   Une autre technique consiste à charger toute l'interface (2ème paramètre à NULL) et à positionner 
	   les autres fenêtres en propriété caché par défaut (je trouve cela un peu goret mais cela peut servir).
	*/

	// test de réussite de chargement
	if( !gui_glade_xml ) {
		gism_gui_error("gism.glade loading error\n");
	}

	/* ici on charge les widgets dans la structure globale
	    pour cela il faut fournir le nom du widget nommé dans Glade.
	    On ne nommera donc dans Glade que les widgets chargés par la libglade en laissant le nommage par défaut
	    pour les autres (pourquoi se fatiguer à renommer tous les widgets. De plus cela permet de voir tout de suite
	    depuis Glade quels sont les widgets impactés par le code. Un utilisateur ou un développeur y fera plus attention 
	    s'il veut bricoler l'interface).
	*/
	gism_widget_load (gui_glade_xml, "dbgrid_customer", &GismWidget.dbgrid_customer);
	gism_widget_load (gui_glade_xml, "hbox_gism", &GismWidget.hbox_gism);
	gism_widget_load (gui_glade_xml, "vpaned_shell", &GismWidget.vpaned_shell);
	gism_widget_load (gui_glade_xml, "hpaned_customer", &GismWidget.hpaned_customer);
	gism_widget_load (gui_glade_xml, "hpaned_user", &GismWidget.hpaned_user);
	gism_widget_load (gui_glade_xml, "hpaned_park", &GismWidget.hpaned_park);
	gism_widget_load (gui_glade_xml, "hpaned_doc", &GismWidget.hpaned_doc);
	gism_widget_load (gui_glade_xml, "notebook_shell", &GismWidget.notebook_shell);
	gism_widget_load (gui_glade_xml, "notebook_park", &GismWidget.notebook_park);
	gism_widget_load (gui_glade_xml, "togglebutton_customer", &GismWidget.togglebutton_customer);
	gism_widget_load (gui_glade_xml, "togglebutton_user", &GismWidget.togglebutton_user);
	gism_widget_load (gui_glade_xml, "togglebutton_doc", &GismWidget.togglebutton_doc);
	gism_widget_load (gui_glade_xml, "treeview_park", &GismWidget.treeview_park);


	/* On lie les signaux définis dans Glade à leur fonction de callback */
	glade_xml_signal_autoconnect(gui_glade_xml);
} 


Voilà, l'essentiel est fait. En incluant interface.h dans ses .c on accède aux widgets tout simplement.
Par exemple, un extrait de mon fichier callback.c (nommé comme le fait Glade lors de la génération de code, mais c'est juste un choix personnel :)

callback.c :
#include 
#include "interface.h"

void on_togglebutton_toggled (GtkWidget *toggle_button, gpointer paned) {

  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(toggle_button))) {

    GismWidget.iPanedPosition = gtk_paned_get_position (GTK_PANED(paned));
    GismWidget.iPanedShellPosition = gtk_paned_get_position (GTK_PANED(GismWidget.vpaned_shell));

    gtk_paned_set_position (GTK_PANED (paned), 10000);
    gtk_paned_set_position (GTK_PANED (GismWidget.vpaned_shell), 10000);

  }
  else {

    if (!GismWidget.iPanedPosition) GismWidget.iPanedPosition = 200;

    gtk_paned_set_position (GTK_PANED (paned), GismWidget.iPanedPosition);
    gtk_paned_set_position (GTK_PANED (GismWidget.vpaned_shell), GismWidget.iPanedShellPosition);
  }

}

Un main.c:

#include  
#include "interface.h"

int main(int argc, char *argv[]) 
{
	gtk_init(&argc, &argv);
	gism_gui_load();

	gtk_main();    
	return 0;
}

et la ligne de compilation :
gcc -o test main.c interface.c callback.c `pkg-config --cflags --libs libglade-2.0 gtk+-2.0`

Signaux : champ Objet
Dans les propriétés d'un widget à l'onglet signaux vous trouverez un champ Objet. On peut y mettre un widget que l'on souhaite passer à la fonction de callback.




En effet, par défaut la fonction signal utilisé par la libglade est le g_signal_connect(). Le widget transmis est celui sur lequel est associé le signal. Il peut arriver que l'on souhaite passer à la fonction de callback un autre widget sur lequel la fonction agira. En fournissant un widget dans le champs Objet, la libglade utilisera la fonction g_signal_connect_object () qui permet cette manipulation.
Voici la fonction de callback qui recoit en paramètre le widget hpaned_customer :

void on_button100_clicked (GtkWidget *paned, gpointer user) {

  gtk_paned_set_position (GTK_PANED (paned), -1);
  gtk_paned_set_position (GTK_PANED (GismWidget.vpaned_shell), -1);
  
}


L'intérêt de cette méthode est de n'avoir qu'une seule fonction de callback quel que soit le bouton cliqué. Factorisez ! :)
Evidement dans le cas où le traitement est identique pour chacun des boutons cliqués.
ATTENTION
Lorsque la libglade charge un widget, elle l'initialise. Il est possible que vous souhaitiez agir d'une certaine manière sur ce widget lors de son initialisation. Ou bien pendant l'utilisation de votre programme une fonction génère un widget. Dans ces cas, il ne faut pas créer ces widgets dans Glade. On va les créer manuellement et on les attachera ensuite à un contenaire de notre interface.

gtk_container_add (GTK_CONTAINER (GismWidget.scrolledwindow_park), GismWidget.treeview_park);
gtk_widget_show (GismWidget.treeview_park);


Ici GismWidget?.scrolledwindow_park est un widget chargé par la libglade, que j'ai stocké dans ma structure globale. Le treeview GismWidget?.treeview_park est construit dynamiquement (il est également stocké dans la structure globale pour faciliter son accès). Une fois construit je l'attache au contenaire GtkScrolledWindow et je l'affiche pour qu'il intégre l'I.H.M.

Note :
D'après le site de Glade le code généré n'est plus maintenu et il y a des chances qu'il ne soit plus en phase avec les prochaines versions de Gtk+ :
"With regard to the last item, note that code generation has been deprecated for a long time: the preferred solution is using libglade."
Ensuite on ne peut pas modifier le code de l'interface généré car à la moindre modification via Glade et regénération du code, les modifications seront écrasés.
Enfin Glade génère pas mal de code inutile permettant notament d'utiliser la fonction lookup_widget() créée par Glade. Elle permet d'accéder à n'importe quel widget de l'interface, ce que fait plus simplement la fonction glade_xml_get_widget de la libglade.
Commentaires [Cacher commentaires/formulaire]
Ajouter un commentaire à cette page:

Apinc