Vous êtes sur la page 1sur 99

Créer des interfaces graphiques en C++ avec

gtkmm
Introduction
Vous ne connaissez pas du tout gtkmm ?
Vous ne savez même pas ce qu'est un widget ?
Pas de problème, car c'est ici que ça commence !
Dans ce chapitre, vous découvrirez GTK+ et gtkmm, vous apprendrez ce que sont les widgets et,
par-dessus tout, vous compilerez votre première application utilisant gtkmm !

Qu'est-ce que GTK+ ? gtkmm ?

Présentation de GTK+
Présentation générale
GTK+ est un ensemble de bibliothèques permettant de créer des interfaces graphiques, donc des
applications fenêtrées, en C.

Au début, GTK+ avait été créé pour le logiciel GIMP, d'où son nom d'ailleurs .
GTK+ est libre, ce qui vous permet de créer des logiciels propriétaires sans contrainte.
De plus, GTK+ est portable ; il fonctionne sur la plupart des systèmes d'exploitation : GNU/Linux,
Mac OS X et Windows.

Présentation technique
GTK+ utilise la programmation orientée objet même si elle est programmée en C.
Elle est utilisable dans de nombreux autres langages tels C++, PHP, Java et Python pour ne citer que
ceux-ci.
Pour vous convaincre d'utiliser cette bibliothèque, sachez que GTK+ est utilisée par de nombreux
projets de qualité comme GIMP (bien sûr ) et le fameux gestionnaire de bureau GNOME.
Étant donné que ces projets sérieux utilisent GTK+ et qu'elle existe depuis plus d'une décennie,
vous pouvez être sûr que cette bibliothèque est de qualité.

Possibilités de GTK+
Cette bibliothèque vous permettra de créer des fenêtres avec des boutons, des images, des menus,
des barres d'outils, des barres d'état, des zones de texte, etc.
Elle gère également les événements, les threads et bien d'autres choses que nous verrons plus tard.
Les différentes bibliothèques de GTK+
GTK+ est basée sur plusieurs bibliothèques :
• GLib : la base de GTK+ (boucle d'événements, threads, système orienté objet, etc.) ;
• Pango : pour le texte (mise en forme, internationalisation) ;
• Cairo : pour le dessin 2D ;
• Glade : pour la création d'interfaces graphiques avec un logiciel ;
• ATK : pour l'accessibilité (lecteurs d'écran, loupes, ...) ;
• et quelques autres.
Il est certain que je vais vous parler des 4 premières.
Cependant, n'ayant jamais utilisé ATK, je ne pourrai pas vous en parler.
ATTENTION !
Bien qu'il existe un logiciel pour créer ses propres fenêtres, il est préférable de savoir en créer en C+
+ avant.

Présentation de gtkmm
Gtkmm est une surcouche de GTK+ pour le langage C++.
Elle permet de créer des interfaces graphiques en utilisant les mécanismes du C++ plutôt que ceux
du C.
Elle utilise, bien sûr, la programmation orientée objet (héritage, polymorphisme, etc.).
Il existe également les surcouches des autres bibliothèques présentées plus haut :
• glibmm ;
• libglademm ;
• etc.
Pourquoi utiliser gtkmm à la place de GTK+ ?
Premièrement, cela vous permet de programmer en C++ avec la programmation orientée objet.
Autre point important : le compilateur C++ détectera des erreurs non détectées en C.
De plus, vous pouvez utiliser l'héritage pour créer de nouveaux widgets (nous verrons dans la
prochaine sous-partie ce que c'est) à partir de ceux existants.
Enfin, le code à écrire en C++ est plus court qu'en C.
Pourquoi utiliser gtkmm à la place de Qt (la bibliothèque graphique expliquée dans le cours de
M@teo21) ?
Gtkmm est écrit dans un style plus proche du C++ que Qt.
Par exemple, Qt utilise la classe QVectorau lieustd::vector, classe présente dans la
bibliothèque standard.
Donc, si vous connaissez cette dernière, inutile d'en réapprendre une nouvelle.
De plus, gtkmm ne modifie pas le langage C++ comme le fait Qt avec ses signaux et ses slots.
Finalement, vous pouvez avoir une bonne intégration avec le bureau GNOME.
À quoi ressemble un programme utilisant gtkmm ?
Je vais vous montrer un programme qui affiche une fenêtre à l'écran :
#include <gtkmm.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Main::run(fenetre);

return 0;

Court, n'est-ce pas ?


Je vous ai montré ce code pour vous montrer à quoi ressemble gtkmm.
Ne vous en faites pas si vous n'y comprenez rien (c'est tout à fait normal ) ; je vais vous expliquer
tout cela lors du prochain chapitre.

Les widgets

Que sont les widgets ?


Un widget est un composant dans une interface graphique.
Sans widget, il n'y aurait rien dans les fenêtres de vos applications...
il n'y aurait même pas de fenêtre...
Il y a des widgets avec lesquels l'utilisateur peut interagir comme les boutons et les zones de texte.
D'autres widgets servent à afficher des informations ou à arranger des widgets selon un ordre
précis.

Widgets fournis par gtkmm


Il y a une pléthore de widgets utilisable avec gtkmm.
Eh oui, tant que ça .
Je vais vous présenter les principaux.
Le bouton
Je pense que vous savez ce que c'est, je me trompe ?
Voici tout de même une image d'un bouton :

La case à cocher
Une case à cocher est un bouton à deux états : activé (coché) et désactivé (décoché) :

Cette case est cochée.

La fenêtre
Je pense que vous savez très bien ce que c'est :

Les fenêtres que vous programmerez prendront l'apparence du système.

Le menu
Un menu est un widget présentant plusieurs éléments que l'utilisateur peut choisir :
La barre d'outils
La barre d'outils sert à rendre plus accessibles certains des éléments les plus utilisés du menu :

La barre d'état
La barre d'état affiche des informations que nous savons déjà :
Les zones de texte uniligne et multiligne
Les zones de texte permettent à l'utilisateur d'entrer du texte.
Voici une zone de texte uniligne :

Et voici une zone de texte multiligne :

La barre de progression
La barre de progression indique à l'utilisateur l'avancement d'une opération plus ou moins longue :

La barre d'onglets
La barre d'onglets permet d'afficher un seul widget à la fois, dans une fenêtre ayant une barre
d'onglets :
L'étiquette
Une étiquette affiche simplement du texte pour dire, par exemple, ce qu'il faut écrire dans une zone
de texte.
Elle sert donc à donner des informations utiles à l'utilisateur.
En voici une :

Cela fait le tour de tous les widgets quelques widgets de gtkmm.


Je vous ai montré cela pour que vous sachiez à quoi vous attendre.
Passons dès maintenant à l'installation de gtkmm !

Installation de gtkmm

Sous Linux...
Je vais détailler l'installation sous Ubuntu ; pour les autres distributions, ce devrait être semblable.
Pour ceux qui veulent installer gtkmm depuis les sources, cliquez ici.
Donc, sous Ubuntu, si vous n'avez pas de compilateur C++ (ça ne devrait pas être le cas), lancez la
commande suivante :
sudo apt-get install g++

Ceci installera le compilateur g++.


Ensuite, vous devez installer gtkmm avec la commande :
sudo apt-get install libgtkmm-3.0-dev
L'installation prend quelques minutes.
Ce devrait être bon.
Rendez-vous à la prochaine sous-partie pour la compilation de votre premier programme.

Sous Windows...
Gtkmm 3 (dernière version de gtkmm expliquée dans ce cours) n’est toujours pas sortie sous
Windows. Par conséquent, il se peut que certains codes ne compilent pas chez vous.
Il vous faudra donc patienter en attendant sa sortie.

Téléchargement
Veuillez télécharger gtkmm depuis le site officiel de GNOME.
Cliquez ici, puis choisissez la dernière version (2.22 à l'heure où j'écris ces lignes) et téléchargez le
fichier gtkmm-win32-devel-x.y.z.exe.

Installation
Double-cliquez sur le fichier téléchargé et installez le programme.
Pour ce faire, choisissez la langue et vous devriez arriver sur une fenêtre semblable à celle-ci :
Ensuite, cliquez sur Suivant, J'accepte, Suivant et vous devriez arriver sur une fenêtre vous
demandant dans quel dossier doit se trouver gtkmm.
Vous pouvez garder celui par défaut ou le modifier à votre guise.
Cliquez sur Suivant et vous devriez arriver sur une fenêtre ayant plusieurs cases à cocher :

Sauf si vous savez ce que vous faites, gardez toutes les cases cochées.
Cliquez sur Suivant et enfin sur Installer.
L'installation devrait durer quelques minutes :
À la fin de l'installation, cliquez sur Fermer :
Voilà, tout est installé, rendez-vous à la prochaine sous-partie pour tester votre installation toute
fraîche, toute belle.

Compiler votre première application !

Le premier programme que vous allez compiler affichera une fenêtre de 200px par 200px.

Super !
Commençons tout de suite.
Veuillez copier-coller le code source suivant (il sera détaillé au prochain chapitre) :
#include <gtkmm.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Main::run(fenetre);
return 0;

Sous Linux…
Sous linux, j'utilise la ligne de commande ; si ce n'est pas votre cas, vous devrez configurer votre
EDI.
Si vous utilisez Code::Blocks, allez voir la compilation sous Window et suivez ces instructions, car
elles devraient être semblables.
Rendez-vous au répertoire de votre fichier .cpp :
cd vers/dossier/de/mon/projet

Ensuite, il vous suffit d'entrer la commande suivante pour compiler votre projet (si votre fichier ne
s'appelle pas main.cpp, veuillez adapter la commande en conséquence, je vous fait confiance ):
g++ main.cpp -o Fenetre `pkg-config gtkmm-3.0 --cflags --libs`

ATTENTION !
Il faut utiliser les accents graves (`) et non de simples apostrophes (') autour de pkg-
config gtkmm-3.0 --cflags --libs pour que ce soit exécuté comme une commande.

De plus, si ça ne fonctionne pas, veuillez recopier cette ligne à la main, car il peut y avoir des
problèmes avec les tirets.
Pour ceux qui ne savent pas ce que fait cette commande, je vais la détailler un peu.
Après g++, il faut mettre les fichiers sources (.cpp) ; ici, il n'y en a qu'un : main.cpp.
Ensuite, après -o, il faut donner un nom à notre exécutable ; ici, je l'ai appelé Fenetre.
À la fin, c'est ce qu'il y a de nouveau :
`pkg-config gtkmm-3.0 --cflags --libs`

Cela permet de dire au compilateur que nous allons utiliser gtkmm.


Si vous voulez, vous pouvez utiliser la nouvelle norme C++ 2011 en ajoutant l’argument -std=c+
+0x :
g++ main.cpp -o Fenetre -std=c++0x `pkg-config gtkmm-3.0 --cflags --libs`

Après la compilation, exécutez votre premier programme (./Fenetre) et vous devriez obtenir, à
quelques détails près, ceci :
Votre première fenêtre !

Utilisation de gtkmm avec CMake


Si vous utilisez CMake, vous pouvez utilisez un fichier semblable à celui-ci :
cmake_minimum_required(VERSION 2.6)

project(Fenetre)

find_package(PkgConfig)

pkg_check_modules(GTKMM gtkmm-3.0)

if(NOT GTKMM_FOUND)

message(FATAL_ERROR "gtkmm-3.0 not found!")

endif()

link_directories(

${GTKMM_LIBRARY_DIRS}

include_directories(

${GTKMM_INCLUDE_DIRS}
)

file(

GLOB_RECURSE

source_files

src/*

add_executable(

Fenetre

${source_files}

target_link_libraries(

Fenetre

${GTKMM_LIBRARIES}

Vos fichiers sources devront se trouver dans le répertoire src/.


Vous pouvez par exemple utiliser ce fichier comme suit :
cd vers/le/dossier/contenant/CMakeLists.txt
mkdir build && cd build
cmake ..
make
./Fenetre

Sous Windows…

Compilation
Je vais détailler la compilation pour Code::Blocks ; si vous utilisez un autre EDI, vous devrez
adapter les prochaines manipulations en conséquence.
Créez un nouveau projet C++ en console.
Copiez le code source que je vous ai donné plus haut et coller-le dans le fichier main.cpp.
Une fois que c'est fait, enregistrez et aller dans le menu Project->Properties…
Ensuite, allez dans l'onglet Build target.
À droite de Type:, choisissez GUI application dans la liste déroulante.
Validez.
Ensuite, allez dans le menu Project->Build options… et allez dans l'onglet Other options de la
deuxième barre d'onglets.
Dans la grande zone de texte, ajoutez la ligne suivante :
`pkg-config gtkmm-2.4 --cflags`

ATTENTION !
Il faut utiliser les accents graves (`) et non de simples apostrophes (') autour de pkg-
config gtkmm-2.4 --cflags pour que ce soit exécuté comme une commande.

De plus, si ça ne fonctionne pas, veuillez recopier cette ligne à la main, car il peut y avoir des
problèmes avec les tirets.
Et un dernier point :
si vous utilisez un Windows 64 bits, vous devez ajouter -m32 devant le premier accent grave :
-m32 `pkg-config gtkmm-2.4 --cflags`
(Merci à paul161 pour l'astuce.)
Enfin, allez dans l'onglet Linker settings et collez cette ligne dans la zone de texte intitulée Other
linker options :
`pkg-config gtkmm-2.4 --libs`

Validez et vous pouvez enfin compiler votre application et la lancer :

Pour ne pas avoir à refaire tout le temps les mêmes manipulations, vous pouvez enregistrer le projet
en tant que template.
Pour ce faire, cliquez sur le menu File->Save project as template…, entrez un nom (par exemple,
gtkmm) et validez.
Lorsque vous créerez un nouveau projet utilisant gtkmm, vous n'aurez qu'à cliquer sur l'icône New
file, choisir From template…, choisir votre nouveau template (gtkmm) et valider.
Distribution de votre application
Pour distribuer votre application à d'autres personnes, vous devrez leur fournir des dll.
Pour cela, votre application devra avoir une structure particulière (la même que GIMP).
Si votre programme est dans un dossier Fenetre, votre structure sera celle-ci :
• Fenetre\bin\
• Fenetre\etc\
• Fenetre\lib\
• Fenetre\share\
Votre exécutable devra se trouver dans le dossier bin\.
De plus, dans tous ces dossiers, il devra y avoir des dll et quelques autres fichiers.
Voici toutes les fichiers que vous devez ajouter (vous trouverez ces fichiers dans votre dossier
d'installation de gtkmm - ici, c'est C:\gtkmm\) :
• C:\gtkmm\redist\*.dll ;
• C:\gtkmm\etc\* ;
• C:\gtkmm\lib\gtk-2.0\2.10.0\* ;
• C:\gtkmm\share\themes\* ;
• C:\gtkmm\share\locale\*.
Lorsque j'écris *.dll, ça signifie que vous devez copier tous les fichiers ayant l'extension .dll.
Lorsque j'écris *, vous copier tout, y compris les dossiers et sous-dossiers.
Les fichiers du dossier redist\ devront se trouver dans le dossier Fenetre\bin\ de votre programme.
Les autres vont dans leur répertoire respectif (etc\ dans Fenetre\etc\, lib\ dans Fenetre\lib\, …).
Vous devez faire une telle structure pour chaque projet que vous voulez distribuer, car vos clients
n'ont probablement pas installé gtkmm.
Vous êtes maintenant prêtes et prêts pour commencer votre apprentissage sur la création
d'interfaces graphiques avec gtkmm !
Dans le prochain chapitre, vous créerez une fenêtre et apprendrez à modifier son titre, sa taille, etc.

Votre première fenêtre


Après une brève introduction, c'est maintenant le temps de commencer.
Vous n'avez encore rien fait et vous commencerez lentement, mais sûrement, en modifiant la fenêtre
que vous avez créée lors du chapitre précédent.
Au programme, modification du titre de la fenêtre, de son icône et autres et ajout d'un widget à la
fenêtre.
Cela vous donnera des bases pour pouvoir modifier tous les widgets, car je ne peux vous dire tout ce
qu'il est possible de faire (la documentation est là pour cela).
Quelques mots sur les #include...
Dans le chapitre précédent, je vous ai fait compiler un programme qui avait l'include suivant :
#include <gtkmm.h>

Cependant, ce n'est pas du tout adapté, car cela inclut toutes les classes de gtkmm.
Il est préférable d'inclure seulement ce dont nous avons besoin pour nos projets.
Par exemple, à la place de ce code :
#include <gtkmm.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Main::run(fenetre);

return 0;

nous devrions écrire celui-ci :


#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Main::run(fenetre);

return 0;

Vous voyez ?
Nous avons remplacé #include <gtkmm.h> par #include <gtkmm/main.h> et
#include <gtkmm/window.h>.
De cette manière, nous n'incluons pas toutes les classes de gtkmm, mais seulement celles
nécessaires.
En quoi est-ce plus adapté ?
Je trouve que cela va faire un code beaucoup plus gros si nous devons ajouter une ligne par widget
utilisé.
Si nous incluons seulement le nécessaire, l'exécutable sera plus petit et la compilation, moins
longue.
Par exemple, pour le programme que vous avez fait au chapitre précédent, si j'inclus seulement le
nécessaire, l'exécutable est près de 2 fois plus petit et la compilation, près d'une demi-fois moins
longue.

Vous n'aimez pas attendre pendant la compilation, n'est-ce pas ?


Alors, faites ce que je dit.
En outre, un exécutable moins gros dit un téléchargement moins long pour les utilisateurs de vos
logiciels.
C'est merveilleux.
D'accord, je comprends maintenant.
Mais, comment vais-je faire pour savoir quelles classes inclure ?
Dans ce tutoriel, je vous dirai quelles classes inclure.
Mais, je vais également vous donner un petit truc.
Dans le code donné plus haut, il y a cette ligne :
Gtk::Main app(argc, argv);

Cela signifie que nous créons un objet Main de l'espace de noms Gtk.

Pour utiliser cette classe, il faut l'inclure :


#include <gtkmm/main.h>

C'est la même chose pour #include <gtkmm/window.h> qui inclut le nécessaire pour cette
ligne :
Gtk::Window fenetre;

La classe Window de l'espace de noms Gtk.

Pour conclure, il est préférable d'inclure seulement les classes qui sont nécessaires au projet.

Explication du code de base


Je vais vous expliquer ce que fait le code que vous avez vu dans la sous-partie précédente, à savoir,
ce code :
#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Main::run(fenetre);

return 0;

Premièrement, nous incluons ce qui est nécessaire au bon fonctionnement de ce programme


(explications de la dernière sous-partie).
Ensuite, nous devons prendre les arguments donnés au programme, c'est pourquoi la fonction main
est déclarée comme suit :
int main(int argc, char* argv[]) {

Ces arguments peuvent être utiles à gtkmm, car certains arguments peuvent lui être destinés.
Ces arguments seront enlevés et ceux qu'il ne connaît pas resteront dans le tableau de telle sorte que
vous pourrez les traiter par la suite.
Ensuite, il faut initialiser gtkmm avec la ligne suivante :
Gtk::Main app(argc, argv);

De surcroît, on lui passe les arguments par cette ligne.


Si vous ne lui donnez pas les arguments, le programme compilera, mais plantera.
La ligne suivante crée une fenêtre :
Gtk::Window fenetre;

Et la dernière instruction avant le return affiche la fenêtre et entre dans le boucle principale :
Gtk::Main::run(fenetre);
Qu'est-ce que la boucle principale ?
Pour que l'application puisse tourner en continu, c'est-à-dire pour qu'elle ne se ferme pas lorsque
toutes les instructions sont effectuées, il faut qu'il y ait une boucle infinie.
Cette boucle s'arrêtera lorsque l'utilisateur fermera le programme en cliquant sur la petite croix se
trouvant sur la barre de titre de votre fenêtre.
Elle peut également s'arrêter lors d'une autre action que vous aurez prédéfinie (par exemple : lors du
clic sur ce bouton, le programme doit se fermer - vous apprendrez à gérer les événements dans le
prochain chapitre).
C'est tout pour les explications du code de base.
Passons maintenant à la modification de votre fenêtre.

Modifier un widget

Pour modifier un widget, il suffit d'appeler des méthodes qui font tout ce qu'on veut (ou presque
).
Pour modifier notre fenêtre, il faudra appeler des méthodes de la classe Gtk::Window.

Modifier le titre de votre fenêtre


Pour modifier le titre de votre fenêtre, celui dans la barre de titre située en haut de votre fenêtre, il
faut appeler la méthode set_title().

Après avoir créer un objet fenetre de type Gtk::Window, nous pouvons donc l'appeler :
fenetre.set_title("Ma belle fenêtre !");

Cela modifiera le titre pour "Ma belle fenêtre !".


Pour obtenir le titre de votre fenêtre, il faut appeler la méthode get_title() (je suis sûr que
vous l'aviez deviné ).
Exemple :
std::string titre = fenetre.get_title();

Et vous pouvez ensuite l'afficher dans la console comme n'importe quel objet string :
std::cout << titre << std::endl;
Modifier l'icône de votre fenêtre
Pour modifier l'icône de votre fenêtre, il faudra... appeler une autre méthode.
En l'occurrence, il s'agit de set_icon_from_file() avec comme paramètre le chemin vers
votre icône.
Exemple :
fenetre.set_icon_from_file("icone.png");

C'est simple, non ?


L'icône apparaîtra, la plupart du temps, en haut à gauche de la fenêtre et dans la barre des tâches :

Minimiser, maximiser, etc.

Minimiser
Pour minimiser la fenêtre (réduire dans la barre des tâches), c'est-à-dire la rendre invisible jusqu'à
temps que l'utilisateur appuie sur son nom dans la barre des tâches, il faut appeler la méthode
iconify().

Pour faire l'opération inversion, donc pour la rendre de nouveau visible, appelez la méthode
deiconify().

Maximiser
Je suis sûr que vous pouvez deviner le nom de la méthode pour maximiser la fenêtre :
fenetre.maximize();

Cela donne la taille maximale à la fenêtre de telle sorte qu'elle prend toute la taille de l'écran.
L'opération inverse lui rend sa taille d'avant :
fenetre.unmaximize();

Mode plein écran


Pour que la fenêtre soit en mode plein écran, vous devez appeler la méthode fullscreen() et
pour qu'elle redevienne comme avant, unfullscreen().
fenetre.fullscreen();

fenetre.unfullscreen();

Ce code ne fait rien tellement les actions sont rapides.


ATTENTION !
En mode plein écran, votre fenêtre n'a pas de bordures de telle sorte qu'il est impossible de la fermer
en cliquant sur le X.
Pour la fermer, vous pouvez faire CTRL-ALT-DELETE ou ALT-TAB.
Dans vos applications, il faudra prévoir cela et afficher un bouton pour quitter le mode plein écran
ou quitter l'application.
Nous verrons comment faire cela dans le prochain chapitre.

Redimensionner la fenêtre et la déplacer


Pour redimensionner la fenêtre, appelez la méthode resize(int largeur, int
hauteur).

Pour la déplacer à une position précise, il faudra appeler la méthode move(int x, int y).
Cette position est relative au coin en haut à gauche de l'écran.
Exemple pour les deux méthodes précédente :
fenetre.resize(640, 480); //Redimensionner la fenêtre : nouvelle largeur =
640px, nouvelle hauteur = 480px.

fenetre.move(100, 50); //Déplacer la fenêtre à la position (100, 50).

Comment faire pour déplacer la fenêtre au centre de l'écran ?

Il faudra utiliser une autre méthode.


Cette méthode s'appelle set_position() et elle prend une constante en paramètre.

Pour centrer la fenêtre, appelez-la comme ceci :


fenetre.set_position(Gtk::WIN_POS_CENTER);

Pour connaître les autres constantes, cliquez ici.


Évitez d'appeler les méthodes move() et set_position() l'une à la suite de l'autre parce qu'un
seul des deux appels fonctionnera.

D'ailleurs, il serait illogique de vouloir placer la fenêtre en même temps au centre et à (100, 50).
Il existe beaucoup, beaucoup d'autres méthodes, mais je ne peux pas vous les apprendre, car ce
serait long et... inutile .

Ajout d'un widget à la fenêtre

Pour ajouter un widget à notre fenêtre, nous devrons... appeler une méthode (je pense que vous
commencez à comprendre le principe ).
Mais avant, nous devrons créer un widget.
Pour cet exemple, nous allons afficher un bouton (Gtk::Button).
Cela veut-il dire que nous devrons ajouter un #include <gtkmm/button.h> ?

Oui !
Vous comprenez bien le principe
Donc, ajoutez cette ligne en haut de votre fichier main.cpp.
Ensuite, il faudra créer un objet de type Gtk::Button.
Nous allons lui passer un paramètre : son texte.
Voici comment faire :
Gtk::Button bouton("Clique-moi !");

Cela créera un bouton sur lequel est affiché le texte "Clique-moi !".
Hé ! J'ai compilé mon programme en ajoutant cette ligne et ma fenêtre est toujours vide...
Quel est le problème ?

Le problème, c'est que vous êtes impatients qu'il faut l'ajouter à la fenêtre.
Pour ce faire, appelez la fonction add() sur la fenêtre avec comme paramètre le widget à ajouter :
fenetre.add(bouton);

Tu me fais marcher, non ?


Je ne vois toujours rien dans cette fenêtre...
Il reste une dernière étape pour voir le widget :
l'afficher !
Pour afficher chacun des widgets de votre fenêtre, il faut, pour chacun, appeler leur méthode
show().

Donc, pour résumer, voici un code complet :


#include <gtkmm/button.h>

#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main kit(argc, argv); //Initialise gtkmm et regarde dans les arguments


du programme.
Gtk::Window fenetre; //Création de la fenêtre.

fenetre.set_title("Ma belle fenêtre !"); //Modifier le titre de la fenêtre.

fenetre.set_border_width(10); //Ajouter une bordure (invisible) de 10px à la


fenêtre.

Gtk::Button bouton("Clique-moi !"); //Création d'un bouton.

fenetre.add(bouton); //Ajout du bouton à la fenêtre.

bouton.show(); //Afficher le bouton.

Gtk::Main::run(fenetre); //Affiche la fenêtre et démarre la boucle, qui se


terminera lorsqu'on fermera le programme.

return 0;

Je me suis permis d'ajouter une bordure intérieure (invisible) à la fenêtre pour qu'il y ait un peu
d'espace autour du bouton.
Enlevez cette instruction pour voir la différence.
Le code précédent donne ceci :

Mais, c'est quoi ce truc affreux sur le bouton ?


Cela indique que le bouton a le focus.
Lorsqu'un widget a le focus, cela veut dire que l'utilisateur peut l'utiliser.
Par exemple, appuyez sur la touche Entrée lorsqu'un bouton a le focus et cela fera la même chose
que si vous aviez cliqué dessus avec la souris.
Si une zone de texte a le focus, cela signifie que vous pouvez écrire dedans.
Pour qu'un widget ne puisse pas avoir le focus, appelez la méthode suivante :
bouton.set_can_focus(false);
Voilà le résultat :

C'est mieux ainsi, non ?


Empêcher une zone de texte d'avoir le focus empêche l'utilisateur d'écrire dedans !
Faites attention à ce que vous faites.
J'ai essayé d'ajouter un deuxième bouton à la fenêtre et j'ai obtenu une erreur à l'exécution me disant
que je ne peux mettre qu'un seul widget à la fois dans une fenêtre.
C'est nul, si c'est vrai !
Comment faire pour en mettre plusieurs ?
En effet, nous ne pouvons mettre qu'un seul widget dans une fenêtre ; un deuxième appel à la
méthode add() provoquera une erreur.

Pour afficher plusieurs widgets, il faudra utiliser des conteneurs ; je vous présenterai ceci dans trois
chapitres.

Les Stock Items


Les Stock Items sont très utiles pour développer des applications sans aller chercher des icônes un
peu partout à travers le Net.
En effet, grâce à eux, nous pouvons créer des boutons, mais aussi des éléments dans un menu et
dans une barre d'outils, avec une étiquette (son texte) et une icôneprédéfinies.
Cela signifie que vous codez d'une certaine façon et vous n'avez pas besoin de fournir les icônes
avec l'exécutable.
Super, non ?
De plus, les Stock Items gèrent les raccourcis des éléments dans un menu.
Vous n'avez donc pas à vous en faire avec cela.
Pour créer un bouton avec une étiquette et une icône prédéfinie, il faudra utiliser une surcharge de
son constructeur.
À la place de lui donner un objet de type std::string, nous allons lui donner une constante de
type Gtk::Stock.
Par exemple, pour créer un bouton ayant pour texte "Annuler" et l'icône qui va avec, faites comme
ceci :
#include <gtkmm/button.h>

#include <gtkmm/main.h>
#include <gtkmm/stock.h> //Ne pas oublier d'inclure ce fichier pour pouvoir
utiliser les Stock Items.

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre; //Création de la fenêtre.

Gtk::Button bouton(Gtk::Stock::CANCEL); //Création d'un bouton à partir d'un


Stock Item.

fenetre.add(bouton); //Ajout du bouton à la fenêtre.

bouton.set_can_focus(false); //Empêcher le bouton d'avoir le focus.

bouton.show(); //Afficher le bouton.

Gtk::Main::run(fenetre);

return 0;

Il ne faut pas oublier d'ajouter la ligne #include <gtkmm/stock.h> !

Voici le résultat :

Comment faire pour savoir quelles sont les constantes prédéfinies ?


Regardez la documentation !
C'est ici, plus précisément.
Elles se trouvent sous le titre Variables.
Cliquez sur l'une d'elle, par exemple CANCEL, pour voir à quoi l'icône ressemble.
Elle peut être différente selon les systèmes d'exploitation et les thèmes.
Il ne se passe rien lorsque je clique sur le bouton.
Comment ajouter un événement lors du clic ?
Pour cela, il faudra utiliser les signaux et les fonctions de rappel.
Je vous présente cela au prochain chapitre (je sens que vous avez hâte ).

Utiliser l'héritage !
Pour terminer ce second chapitre, parlons de l'héritage dans gtkmm.
Comme je l'ai mentionné plus tôt, nous pouvons utiliser l'héritage pour créer de nouveaux widgets.
Ces widgets dériveront des widgets existants.

Une classe pour la fenêtre principale


Pour un vrai projet, nous allons créer une classe pour la fenêtre principale.
C'est dans celle-ci que nous modifierons son titre, son icône et que nous y ajouterons des widgets.
Cela permet de mieux organiser le code.
Voyons à quoi cela ressemble.

Le fichier main.cpp
Ce fichier restera toujours identique :
#include <gtkmm/main.h>

#include "Fenetre.hpp"

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Comme vous pouvez le constater, nous utilisons une classe Fenetre que nous allons créer à
l'instant.

Le fichier Fenetre.hpp
C'est dans ce fichier que nous allons créer la classe Fenetre :
#ifndef DEF_FENETRE

#define DEF_FENETRE
#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

};

#endif

Je crois que quelques explications s'imposent.


Premièrement, nous écrivons toujours la protection avec #ifndef, #define et #endif.

Ensuite, nous incluons seulement le nécessaire (si nous aurions utilisé un bouton, il aurait fallu
ajouter un #include <gtkmm/button.h>).

Ensuite, nous créons notre classe Fenetre, qui hérite de Gtk::Window, de façon public.

Nous pouvons donc dire que Fenetre est une Gtk::Window (c'est vrai, je vous le jure ).

Ensuite, il y a le constructeur ; vous devriez en avoir l'habitude.

Le fichier Fenetre.cpp
Enfin, voici le fichier Fenetre.cpp.
C'est dans ce fichier que nous allons modifier notre fenêtre et ajouter des fonctions de rappel (nous
verrons plus tard ce que c'est - leur prototype étant, bien sûr, dans le fichier Fenetre.hpp).
Le voici :
#include "Fenetre.hpp"

Fenetre::Fenetre() {

//Modification de la fenêtre.

set_title("Ma fenêtre héritée !");

//Autres modifications de la fenêtre.

Nous ne sommes plus obligé d'écrire fenetre. devant un appel de méthode, car ce sont des
méthodes de la classe même.
Sous Linux, si vous utilisez la ligne de commande, n'oubliez pas d'ajouter le fichier Fenetre.cpp
après main.cpp, sinon cela ne compilera pas !
Mais, c'est la même fenêtre qu'avant.
Quelle est la différence ?
La différence, c'est que c'est plus lisible pour vous (et ça, je peux vous dire que c'est une grande
différence lors d'un grand projet ).
J'ai une autre question.
J'ai ajouté un bouton dans le constructeur, mais rien ne s'affiche.
J'ai pourtant appelé les méthodes add(bouton) et bouton.show().
Quel est le problème ?
Lorsque nous utilisons l'héritage avec gtkmm, nous ne pouvons plus créer des widgets comme
avant.
La bibliothèque gtkmm permet aux programmeurs de contrôler la durée de vie des widgets de la
même manière qu'en C++.
Il y a plusieurs façons de créer des widgets dans une classe et je vais vous en montrer deux.

Ajout d'un widget à la fenêtre

Déclarer les widgets en tant que attribut


La méthode la plus simple est de déclarer les widgets en tant que attribut.
Et lorsque nous déclarons un objet en tant que attribut, il faut l'initialiser dans la liste d'initialisation
du constructeur.
De cette façon, les widgets seront détruits en même temps que la fenêtre.
Voyons le nouveau fichier Fenetre.hpp :
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

private :
Gtk::Button bouton; //Déclaration du bouton.

};

#endif

Et le nouveau fichier Fenetre.cpp :


#include "Fenetre.hpp"

Fenetre::Fenetre() : bouton("Mon bouton !") { //Initialisation du bouton dans la


liste d'initialisation.

//Modification de la fenêtre.

set_title("Ma fenêtre héritée !");

//Autres modifications de la fenêtre.

bouton.show(); //Ne pas oublier l'affichage du bouton.

bouton.set_can_focus(false); //Nous pouvons aussi modifier le bouton.

add(bouton); //Ajouter le bouton à la fenêtre.

Le fichier main.cpp reste toujours le même.


Pourquoi ne pas créer une classe pour le bouton ?
Il aurait été possible de le faire.
Mais comme nous ne l'utilisons qu'une seule fois, cela n'est pas vraiment nécessaire et n'améliore
pas vraiment la lisibilité du code.
Créez seulement une classe lorsque cela améliore la lisibilité de votre code ou que vous voulez créer
un widget différent de ceux qui existent.
Par exemple, si vous créez une boîte de dialogue personnalisée (nous verrons plus tard ce que c'est
et comment en créer une), il est beaucoup plus lisible de faire une classe pour ce widget.
Nous voyons là toute la puissance de l'héritage.

Créer des pointeurs sur des widgets


La deuxième méthode consiste à créer des pointeurs sur des widgets.
De cette façon, il faudra détruire nous-même les widgets.
Voici le nouveau fichier Fenetre.hpp :
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

~Fenetre(); //Le destructeur pour pouvoir détruire le bouton.

private :

Gtk::Button* bouton; //Création du pointeur sur bouton.

};

#endif

C'est le même fichier que précédemment, sauf que nous créons un pointeur sur Gtk::Button à la
place d'un objet Gtk::Button.

En outre, nous ajoutons un destructeur pour pouvoir détruire le bouton à la destruction de la fenêtre.
Voici le nouveau fichier Fenetre.cpp :
#include "Fenetre.hpp"

Fenetre::Fenetre() {

bouton = new Gtk::Button("Hello World!");

bouton->show();

add(*bouton);

}
Fenetre::~Fenetre() {

delete bouton;

Puisque nous utilisons un pointeur, nous devons utiliser new dans le constructeur et delete dans
le destructeur (pour le détruire en même temps que la fenêtre).
La méthode add() prend un Gtk::Widget et non un pointeur ; c'est pourquoi nous ajoutons une
étoile (*) devant le pointeur.
Cependant, il y a un moyen de faire plus rapide que l'exemple précédent.
Dans l'exemple précédent, nous détruisons le widget en même que la fenêtre.
Il y a un moyen d'automatiser cela.
Le fichier Fenetre.hpp devient très simple :
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

};

#endif

Je n'ai rien à dire sur ce code.


Et le fichier Fenetre.cpp est également plus court :
#include "Fenetre.hpp"

Fenetre::Fenetre() {
Gtk::Button* bouton = Gtk::manage(new Gtk::Button("Hello World!"));

add(*bouton);

bouton->show();

Nous créons le pointeur en utilisant la fonction Gtk::manage().


Nous ajoutons le bouton, encore une fois, en ajoutant une étoile (*) pour obtenir l'objet et non le
pointeur.
Puis, nous l'affichons.
Nous n'avons plus besoin de détruire le widget ; gtkmm le détruira en même temps que son
conteneur, à savoir la fenêtre.
De plus, cela nous permet de créer facilement un nombre indéterminé (à la compilation) de widgets.
Dans quel cas ne connaissons-nous pas le nombre de widgets à la compilation ?
Présentement, vous utilisez un navigateur Web.
Ceux qui ont conçu ce logiciel ne savaient pas combien d’onglets vous allez ouvrir. Par conséquent,
ils ne pouvaient pas utiliser un attribut par onglet.
Donc, quand vous utiliserez des widgets dont le nombre peut varier, vous devrez bien souvent vous
servir de Gtk::manage().

Dans ce tutoriel, pour que le code soit plus clair, j'utiliserai la première façon de faire (les widgets
déclarés en tant que attribut) sauf lorsque ce sera impossible.
Vous savez maintenant créer et modifier votre fenêtre à votre guise (ou presque ). Il suffit d'utiliser
des méthodes déjà écrites par d'autres. C'est simple, non ?
Mais... il y a pleins d'autres choses que je voudrais faire avec ma fenêtre... par exemple, empêcher
l'utilisateur de la redimensionner, ou bien enlever les bordures du système. Y a-t-il un moyen de
faire cela ?
Mais bien sûr.
Cependant, je ne peux guère tout vous apprendre sur la création d'interfaces graphiques avec
gtkmm.
C'est tellement immense... et je ne connais pas tout sur cette bibliothèque.
Alors, comment vais-je faire pour le savoir ?
Vous devrez lire la documentation !
(Dites donc, pourquoi tout le monde est parti. )
N'ayez pas peur, je vais vous apprendre à la lire.
Elle vous dira tout ce dont vous avez besoin de savoir.
Pour ceux qui veulent savoir tout de suite à quoi elle ressemble, cliquez ici (en anglais, comme
toujours ).
Pour les autres, je vous apprendrai, dans un chapitre ultérieur, à lire la documentation.

Les signaux et les fonctions de rappel (Partie 1/2)


Dans le chapitre précédent, vous avez ajouté un bouton dans votre fenêtre.
Cependant, il ne se passait rien lorsqu'on cliquait dessus.
Il va falloir modifier un peu cela pour que, par exemple, la fenêtre se ferme lorsque l'utilisateur
clique sur le bouton.
Avec l'utilisation des signaux, vous verrez que c'est facile de rendre votre application interactive.
Les signaux sont le cœur d'une interface graphique. Vous ne pouvez pas vous en passer.
Mettons un peu d'interactivité à votre fenêtre !
Dans le but de l'alléger, notre étude des signaux et des fonctions de rappel se fera en deux chapitres.

Fonctionnement des signaux

Définition
Un signal est un message envoyé par un widget lorsqu'un événement s'est produit sur ce dernier
(par exemple, lorsque l'utilisateur clique sur un bouton).
Pour traiter les événements, nous devons connecter un signal à une fonction de rappel.
Ce peut être n'importe quelle fonction, qui prend des paramètres ou non.
Ce peut également être une méthode d'une classe.
Il est à noter que chaque signal a un prototype de fonction de rappel.
En connectant un signal à une telle fonction, nous devons vérifier qu'il envoie autant de paramètres
que son prototype en contient.
Toutefois, il existe une méthode pour ajouter des paramètres supplémentaires à un signal.
Je vais vous montrer cette technique dans ce chapitre.
Pour gérer les événements, nous utiliserons la bibliothèque sigc++ que vous avez installée (sans
vous en rendre compte ) en même temps que gtkmm.
Cette bibliothèque détecte des erreurs de type à la compilation, ce qui permet d'éviter des bogues
qu'il est possible d'avoir avec Gtk+.
Elle vérifie aussi le nombre de paramètres envoyés par le signal et reçus par la fonction de rappel à
la compilation.
Nous pouvons donc détecter des erreurs à la compilation.

Connexion d'un signal à une fonction de rappel


Connexion à une fonction de rappel sans paramètre
Notre premier exemple sera de connecter le clic sur un bouton à la fermeture du programme.
Créez-moi donc une fenêtre qui ressemble à ceci :

Vous devriez être capable.


Cependant, lorsque nous cliquons dessus, le programme ne se ferme pas.
C'est pourtant ce que nous voulions faire en utilisant ce Stock Item .
Avant de vous dire comment connecter un signal à une fonction de rappel, je vais vous donner la
méthode qui fait arrêter le programme (cette fonction termine la boucle du programme) :
Gtk::Main::quit();

Mais, où devons-nous placer ce code pour que le programme se ferme lorsque l'utilisateur clique sur
le bouton ?
Voici la ligne qu'il faut ajouter :
boutonQuitter.signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit));

Je vous l'explique à l'instant.


Après boutonQuitter.signal_, il faut mettre le signal que nous souhaitons connecter à une
fonction de rappel.
Ici, c'est clicked() (ce qui signifie que ce signal est émis lorsque l'utilisateur clique sur le
bouton).
Ensuite, nous connectons (connect()) le signal à la fonction de rappel.

Puis, nous indiquons sigc::ptr_fun() pour dire que nous connectons le signal à une fonction
et non à une méthode.
Pour info, sigc::ptr_fun() retourne un foncteur.
À la fin, il y a le nom de la fonction, sans parenthèses, précédé d'une esperluette (&).
Dans le but d'alléger le code, je vous montre un projet qui ne contient qu'un seul fichier —
main.cpp.
Mais, vous, dans vos projets, vous créerez une classe pour votre fenêtre (comme je vous ai montré
au chapitre précédent).
Voici un code complet :
#include <gtkmm/button.h>
#include <gtkmm/main.h>

#include <gtkmm/stock.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Button bouton(Gtk::Stock::QUIT); //Création d'un bouton.

fenetre.add(bouton); //Ajout du bouton à la fenêtre.

bouton.show(); //Ne pas oublier d'afficher le bouton.

bouton.set_can_focus(false); //Empêcher le bouton d'avoir le focus.

bouton.signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit));
//Connexion du signal clicked() à la fonction Gtk::Main::quit() qui ferme le
programme.

Gtk::Main::run(fenetre);

return 0;

Dans ce code, nous construisons la fenêtre en y ajoutant un bouton Quitter.


Ensuite, nous connectons le signal clicked() du bouton à la méthode Gtk::Main::quit(),
ce qui a pour effet de fermer le programme.
Votre bouton fait enfin ce pour quoi il a été conçu.
Ce code avec sigc::ptr_fun() peut rapidement devenir compliqué.
Il est possible de le simplifier si vous utilisez la nouvelle norme C++ 2011, grâce aux fonctions
anonymes et aux fermetures.
La ligne de connexion du signal devient :
bouton.signal_clicked().connect([]() { Gtk::Main::quit(); });
Connexion à une méthode sans paramètre
Mettez votre fenêtre en plein écran en appelant fullscreen() dans le constructeur de la classe
Fenetre.

Changez le Stock Item de votre bouton pour Gtk::Stock::LEAVE_FULLSCREEN.

Voici ce que nous voulons faire maintenant :


quitter le mode plein écran en cliquant sur le bouton.
Cependant, la méthode unfullscreen() appartient à la classe Gtk::Window.

Je le sais !
Et si nous essayions ceci :
boutonQuitter.signal_clicked().connect(sigc::ptr_fun(&unfullscreen
));
La classe Fenetre hérite de Gtk::Window, il ne devrait pas y avoir de problème, non ?
Malheureusement, le compilateur n'aime pas trop...
Et comment saurait-il quel objet doit appeler cette méthode ?
En effet, nous pouvons connecter un signal à une méthode de n'importe quel objet.
Au lieu d'utiliser ptr_fun(), nous utiliserons mem_fun().
Le premier paramètre est l'objet et le deuxième, la méthode.
Voici ce qu'il faut écrire pour qu'un clic sur le bouton quitte le mode plein écran :
bouton.signal_clicked().connect(sigc::mem_fun(*this, &Fenetre::unfullscreen));

Ceci appelle la méthode unfullscreen() de l'objet this qui est de type Fenetre.

Pour ceux qui utilisent C++ 2011, ce code fait la même chose :
bouton.signal_clicked().connect([this]() { unfullscreen(); });

Il ne faut pas oublier de capturer this pour que la fonction puisse appeler unfullscreen() sur
la fenêtre courante.
Est-il possible de connecter un même signal à deux fonctions de rappel ?
Oui, ceci est tout à fait possible :
bouton.signal_clicked().connect(sigc::mem_fun(*this, &Fenetre::fullscreen));

bouton.signal_clicked().connect(sigc::mem_fun(*this, &Fenetre::unfullscreen));

Bien que ce code ne fasse rien d'exceptionnel, ça fonctionne.


Si vous avez une erreur incompréhensible à la compilation, c'est que vous avez fait une erreur lors
d'une connexion d'un signal à une fonction de rappel.
L'erreur la plus courante est l'irrespect des paramètres transmis par le signal et ceux reçus par la
fonction de rappel.
Vous aurez une erreur semble à celle-ci :
In file included from /usr/include/sigc++-2.0/sigc++/functors/slot.h:7,
                 from /usr/include/sigc++-2.0/sigc++/signal_base.h:28,
                 from /usr/include/sigc++-2.0/sigc++/signal.h:8,
                 from /usr/include/sigc++-2.0/sigc++/sigc++.h:23,
                 from /usr/include/glibmm-2.4/glibmm/signalproxy.h:13,
                 from /usr/include/glibmm-2.4/glibmm/objectbase.h:23,
                 from /usr/include/glibmm-2.4/glibmm/wrap.h:26,
                 from /usr/include/glibmm-
2.4/glibmm/containerhandle_shared.h:25,
                 from /usr/include/glibmm-2.4/glibmm/arrayhandle.h:23,
                 from /usr/include/glibmm-2.4/glibmm.h:82,
                 from /usr/include/gtkmm-2.4/gtkmm/main.h:7,
                 from Fenetre.hpp:4,
                 from Fenetre.cpp:1:
/usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h: In member function ‘typ
ename sigc::adaptor_functor<T_functor>::result_type sigc::adaptor_functor<T_func
tor>::operator()() const [with T_functor = sigc::bound_mem_functor1<void, Fenetr
e, const Glib::ustring&>]’:
/usr/include/sigc++-2.0/sigc++/functors/slot.h:103:   instantiated from ‘static 
T_return sigc::internal::slot_call0<T_functor, T_return>::call_it(sigc::internal
::slot_rep*) [with T_functor = sigc::bound_mem_functor1<void, Fenetre, const Gli
b::ustring&>, T_return = void]’
/usr/include/sigc++-2.0/sigc++/functors/slot.h:110:   instantiated from ‘static 
void* (* sigc::internal::slot_call0<T_functor, T_return>::address())(void*) [wit
h T_functor = sigc::bound_mem_functor1<void, Fenetre, const Glib::ustring&>, T_r
eturn = void]’
/usr/include/sigc++-2.0/sigc++/functors/slot.h:454:   instantiated from ‘sigc::s
lot0<T_return>::slot0(const T_functor&) [with T_functor = sigc::bound_mem_functo
r1<void, Fenetre, const Glib::ustring&>, T_return = void]’
/usr/include/sigc++-2.0/sigc++/functors/slot.h:1130:   instantiated from ‘sigc::
slot<T_return, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, 
sigc::nil>::slot(const T_functor&) [with T_functor = sigc::bound_mem_functor1<vo
id, Fenetre, const Glib::ustring&>, T_return = void]’
Fenetre.cpp:11:   instantiated from here
/usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h:251: error: no match for 
call to ‘(sigc::bound_mem_functor1<void, Fenetre, const Glib::ustring&>) ()’
/usr/include/sigc++-2.0/sigc++/functors/mem_fun.h:1850: note: candidates are: T_
return sigc::bound_mem_functor1<T_return, T_obj, T_arg1>::operator()(typename si
gc::type_trait<T_arg3>::take) const [with T_return = void, T_obj = Fenetre, T_ar
g1 = const Glib::ustring&]
/usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h:251: error: return-
statement with a value, in function returning 'void'

Pour corriger l'erreur, il faudra aller voir dans la documentation pour vérifier le prototype de la
fonction de rappel du signal en question.
Je vous apprendrai comment vérifier cela lorsque je vous apprendrai à lire la documentation.

Connexion à une méthode avec paramètre(s)


Maintenant, nous voulons que le clic sur le bouton modifie le titre de la fenêtre.
Pour modifier ceci, il faut appeler set_title(), mais avec un argument.
Comment allons nous faire ?
Le problème c'est que le signal clicked() n'envoie aucun paramètre à la fonction de rappel.
Il faut donc en ajouter un.
Pour cela, il faudra lier le paramètre avec sigc::bind().

Donc, nous allons faire ceci :


bouton.signal_clicked().connect(sigc::bind<std::string>(sigc::mem_fun(*this,
&Fenetre::set_title), "Titre de la fenêtre"));

Entre les chevrons se trouve le type du paramètre que nous envoyons.


"Titre de la fenêtre" est bien de type std::string.

Dans un code complet, ça donne ceci :


#include <string>

#include <gtkmm/button.h>

#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

fenetre.set_title(""); //Cacher le titre de la fenêtre.

Gtk::Button bouton("Afficher le titre !");

fenetre.add(bouton); //Ajout du bouton à la fenêtre.

bouton.show(); //Ne pas oublier d'afficher le bouton.

bouton.set_can_focus(false); //Empêcher le bouton d'avoir le focus.

bouton.signal_clicked().connect(sigc::bind<std::string>(sigc::mem_fun(fenetre,
&Gtk::Window::set_title), "Titre de la fenêtre")); //Lorsque l'utilisateur
cliquera sur le bouton, le titre de la fenêtre changera.

Gtk::Main::run(fenetre);
return 0;

Encore une fois, nous créons une fenêtre avec un bouton.


Dans la connexion du signal à la méthode set_title(), notez que nous indiquons
&Gtk::Window::set_title au lieu de &Fenetre::set_title, car nous n'avons pas
créer de classe Fenetre dans ce code.

Encore une fois, c’est bien plus simple avec la nouvelle norme :
bouton.signal_clicked().connect([&fenetre]() { fenetre.set_title("Titre de la
fenêtre"); });

Il ne faut pas oublier de capturer la fenêtre par référence.


Si nous voulons envoyer plusieurs paramètres, il suffit de les séparer par des virgules aux deux
endroits (entre les chevrons et après le nom de la fonction de rappel).
Vous verrez un exemple de transmission de plusieurs paramètres dans le prochain chapitre.
Essayez !
Maintenant, si nous cliquons sur le bouton, le titre de la fenêtre s'affiche !
Transmettre un paramètre est également utile lorsque nous utilisons un même signal pour plusieurs
widgets.
Nous pouvons envoyer le widget en paramètre pour savoir que c'est lui qui a émis le signal.

Exemple

Pour terminer ce chapitre, nous allons concevoir un petit programme qui utilise les signaux.
Ce programme sera très simple et, bien que inutile, vous fera pratiquer ce que vous avez vu dans ce
chapitre.
Le programme affichera un bouton :

Et lorsque l'utilisateur cliquera dessus, nous allons modifier le texte sur le bouton et le titre sur la
fenêtre :
Si vous vous sentez capable, essayez de faire ce programme par vous-même.
Je vous donne la correction.

Le fichier main.cpp
#include <gtkmm/main.h>

#include "Fenetre.hpp"

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Ce fichier est toujours le même.

Le fichier Fenetre.hpp
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <string>

#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :
Fenetre();

private :

Gtk::Button bouton;

};

#endif

Nous créons la fenêtre et le bouton ; il n'y a rien de bien compliqué.

Le fichier Fenetre.cpp
#include "Fenetre.hpp"

using namespace std;

Fenetre::Fenetre() : bouton("Hello World!") {

set_title("Hello World!");

set_border_width(10);

add(bouton);

bouton.show();

bouton.set_can_focus(false);

bouton.signal_clicked().connect(sigc::bind<string>(sigc::mem_fun(*this,
&Fenetre::set_title), "Bonjour le monde !")); //Lorsque l'utilisateur clique sur
le bouton, modifier le titre et...

bouton.signal_clicked().connect(sigc::bind<string>(sigc::mem_fun(bouton,
&Gtk::Button::set_label), "Bonjour le monde !")); //... le texte sur le bouton.

Lui aussi est très simple.


Nous commençons par modifier un peu la fenêtre (titre, bordure).
Puis, nous ajoutons le bouton à la fenêtre.
Enfin, nous connectons le signal clicked() du bouton à deux fonctions de rappel.

La première connexion se charge de modifier le titre de la fenêtre avec set_title().


La deuxième modifie le texte sur le bouton avec set_label().

Dans les deux cas, nous ajoutons un paramètre avec sigc::bind.

Voilà, c'est très simple, n'est-ce pas ?


Et la version C++ 2011 est encore plus simple :
bouton.signal_clicked().connect([this, &bouton]() {

set_title("Bonjour le monde !");

bouton.set_label("Bonjour le monde !");

});

Que de nouveautés dans ce chapitre.


C'est un gros morceau que vous venez de voir, là.
Vous savez maintenant comment connecter un signal à une fonction de telle sorte que celle-ci soit
appelée lorsque l'utilisateur effectue une action pré-déterminée.
Il existe d'autres signaux que clicked() ; il en existe beaucoup d'autres.
Vous n'avez donc pas vu tous les signaux existants (vous n'en avez vu qu'un seul, en fait ), mais,
au fur et à mesure que vous avancerez dans ce tutoriel, vous en apprendrez d'autres.
Si vous voulez en connaître qui ne sont pas expliqués dans ce tutoriel, il faudra aller dans la doc'.
Dans le prochain chapitre, nous terminerons notre étude des signaux et des fonctions de rappel en
voyant comment en créer.

Les signaux et les fonctions de rappel (Partie 2/2)

Après avoir vu comment connecter un signal à une fonction de rappel, nous allons voir comment en
créer.
Dans vos projets, vous créerez plus souvent des fonctions de rappel que des signaux, mais il est bon
de savoir comment créer les deux.

Créez vos propres fonctions de rappel


Maintenant que nous sommes capables de connecter des signaux à des fonctions de rappel, nous
allons apprendre à en créer.
Commençons par les fonctions de rappel et nous finirons avec la création de signaux (gardons le
meilleur pour la fin ).

La fonction de rappel
Une fonction de rappel est une fonction comme les autres.
Elle retourne une valeur et a des paramètres.
Pour cette sous-partie, nous allons utiliser une fonction de rappel qui agrandit la taille de la fenêtre.
Voici son prototype (que vous pouvez ajouter dans le fichier Fenetre.hpp) :
void augmenterTaille();

Créez également un bouton dans ce fichier.


Nous allons maintenant écrire son contenu dans le fichier Fenetre.cpp :
void Fenetre::augmenterTaille() {

int largeur(0);

int hauteur(0);

get_size(largeur, hauteur);

resize(largeur + 10, hauteur + 10);

Je vous explique ce qui se passe.


Premièrement, nous créons des variables pour conteneur la largeur et la hauteur actuelles de la
fenêtre.
Nous obtenons ces données en envoyant ces variables en paramètre à la méthode get_size().

Ensuite, nous redimensionnons la fenêtre grâce à la méthode resize() (nous ajoutons 10px à la
taille actuelle).
C'est très simple, n'est-ce pas ?
Il ne vous reste plus qu'à connecter un signal à cette fonction et le tour est joué :
bouton.signal_clicked().connect(sigc::mem_fun(*this,
&Fenetre::augmenterTaille));

À chaque clic sur un bouton, la fenêtre s'agrandit de 10px.

La fonction de rappel avec des paramètres


Notez qu'il est impossible de surcharger une fonction de rappel, car il y aurait ambiguïté.
Modifions le prototype de notre fonction de rappel pour celui-ci :
void augmenterTaille(int augmentationLargeur, int augmentationHauteur);

Cette fois, nous allons envoyer à cette fonction la largeur et la hauteur que nous voulons ajouter à la
fenêtre.
Vous devriez être capable de modifier la méthode, mais je vous la donne tout de même (que je suis
gentil ):
void Fenetre::augmenterTaille(int augmentationLargeur, int augmentationHauteur)
{

int largeur(0);

int hauteur(0);

get_size(largeur, hauteur);

resize(largeur + augmentationLargeur, hauteur + augmentationHauteur);

Et comme vous avez appris plus haut comment appeler une fonction de rappel qui ne comprend pas
les mêmes paramètres que le signal, vous devriez être capable d'écrire la connexion.
Je vous la donne également :
bouton.signal_clicked().connect(sigc::bind<int, int>(sigc::mem_fun(*this,
&Fenetre::augmenterTaille), 130, 140));

Comme il y a plusieurs paramètres, nous devons les séparer par des virgules.
À chaque clic sur le bouton, la fenêtre s'agrandira de 130 pixels horizontalement et 140
verticalement.
Cette fois, je vous donne un code complet avec 3 fichiers (main.cpp, Fenetre.cpp et Fenetre.hpp).

main.cpp
#include <gtkmm/main.h>

#include "Fenetre.hpp"

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);


Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Ce code est toujours le même.

Fenetre.hpp
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public:

Fenetre();

private:

Gtk::Button bouton;

void augmenterTaille(int augmentationLargeur, int augmentationHauteur);

};

#endif

C'est la classe de la fenêtre.


Nous créons le prototype de notre fonction de rappel (qui prend des paramètres) et notre bouton.

Fenetre.cpp
#include "Fenetre.hpp"
Fenetre::Fenetre() : bouton("Agrandir") {

set_default_size(200, 200); //Déterminer la taille par défaut de la fenêtre.

set_border_width(10); //Ajouter une bordure de 10px autour du bouton.

add(bouton);

//Connexion du signal clicked() du bouton à la méthode augmenterTaille() de


la présente fenêtre.

//Nous lui passons deux paramètres.

bouton.signal_clicked().connect(sigc::bind<int, int>(sigc::mem_fun(*this,
&Fenetre::augmenterTaille), 130, 140));

show_all();

void Fenetre::augmenterTaille(int augmentationLargeur, int augmentationHauteur)


{

int largeur(0);

int hauteur(0);

get_size(largeur, hauteur);

resize(largeur + augmentationLargeur, hauteur + augmentationHauteur);

À la ligne 10, nous connectons le signal clicked() (encore lui :colere2: ) du bouton à notre fonction
de rappel augmenterTaille() ; nous lui transmettons les deux paramètres demandés par cette
dernière.
À la ligne 14, il y a notre fonction de rappel.
Je viens de vous expliquer son principe ; je ne me répèterai pas.
Passons à la création de signaux.

Créez vos propres signaux

Créer un signal
Un signal est un objet de type sigc::signal.
Lorsque nous utilisons sigc::signal, nous ajouterons toujours des chevrons après pour
indiquer le type de retour et celui des paramètres.
Par exemple, si nous voulons créer un signal qui ne renvoie rien et n'envoie aucun paramètre, nous
le créerons comme ceci :
sigc::signal<void> signal_nom;

Cependant, cela ne crée pas le signal comme ceux dans gtkmm.


Pour l'utiliser, nous ne devrons pas ajouter de parenthèses.
Par exemple, pour le signal clicked() d'un bouton, nous faisons comme ceci :
bouton.signal_clicked().connect()

Pour le signal que nous venons de créer nous devons faire cela :
objet.signal_nom.connect()

Un signal avec des parenthèses


Pour ajouter des parenthèses, nous allons devoir créer un attribut (variable membre) qui sera
retournée par une méthode.
Pourquoi devons-nous faire cela ?
C'est un peu une perte de temps, non ?

C'est ce que je me disais aussi.


En utilisant un accesseur, ne saurons que c'est un signal lorsque nous l'émettrons et lorsque nous le
connecterons à une fonction de rappel.
En outre, si d'autres personnes lisent votre code, ils sauront immédiatement que c'est un signal.
Voyons comment faire cela.
Ce qui suit se situe dans la partie public de votre classe Fenetre.

Premièrement, comme le type de retour et celui des paramètres peut parfois être long à écrire, nous
commencerons par créer un typedef :
typedef sigc::signal<void> type_signal_nom;

type_signal_nom

est maintenant l'équivalent de sigc::signal<void>.

Dans ce cas-ci, ce n'est pas vraiment un gain niveau longueur, mais, parfois, ce sera le cas.
C'est au moins un gain niveau lisibilité.
Ensuite, nous pouvons créer notre accesseur :
type_signal_nom signal_nom();

Vous voyez, l'accesseur retourne un objet de type type_signal_nom, donc notre signal.
Dans la partie protected, nous allons créer notre signal :
type_signal_nom m_signal_nom;

Vous voyez, nous l'utilisons souvent, ce typedef (il n'est donc pas si inutile que ça ).

Enfin, nous devons créer l'accesseur dans le fichier Fenetre.cpp :


Fenetre::type_signal_nom Fenetre::signal_nom() { //Cette méthode retourne le
signal.

return m_signal_nom;

Maintenant, nous pouvons utiliser notre signal avec des parenthèses, comme ceci :
signal_nom().connect(...);

Comme c'est un signal de notre fenêtre, nous n'avons pas besoin d'écrire le nom d'un objet devant
signal_nom().
Il est tout à fait possible de créer des signaux pour d'autres widgets ; il faut juste créer une classe
héritant de ce widget et faire ce je viens de vous montrer.

Émettre un signal
Pour émettre un signal, il suffit d'appeler la méthode emit() de ce signal :
signal_nom().emit();

Un signal qui transmet des paramètres


Si nous voulons que notre signal transmette des paramètres à la fonction de rappel, nous devons
modifier deux choses :
• Ajouter un type lors de la création du typedef :

• Envoyer un argument à la fonction emit().


La création du typedef
Pour la création, il faut ajouter le type de l'argument qui sera transmis.
Si c'est un std::string qui est transmis, nous devons faire ceci :
typedef sigc::signal<void, std::string> type_signal_nom;

L'émission du signal
Il suffit d'ajouter un argument de type std::string à la méthode emit() :
signal_nom().emit("Texte");

Exemple
Pour l'exemple, nous allons reprendre notre fonction de rappel augmenterTaille() (celle sans
paramètre) et allons émettre un signal lorsque la taille de la fenêtre est supérieure ou égale à 200px
(car nous trouvons que c'est assez gros, un bouton de 200px ).
Voici les fichiers :

main.cpp
#include <gtkmm/main.h>

#include "Fenetre.hpp"

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Rien de spécial, comme à l'accoutumée.

Fenetre.hpp
#ifndef DEF_FENETRE

#define DEF_FENETRE
#include <gtkmm/button.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public:

Fenetre();

void augmenterTaille();

typedef sigc::signal<void> type_signal_taille_max;

type_signal_taille_max signal_taille_max();

protected:

type_signal_taille_max signalTailleMax;

private:

Gtk::Button bouton;

};

#endif

Dans ce fichier, nous créons notre signal (typedef, attribut et accesseur).

Fenetre.cpp
#include "Fenetre.hpp"

Fenetre::Fenetre() : bouton("Agrandir la fenêtre") {

set_default_size(200, 200);

set_border_width(10);

bouton.set_can_focus(false);
add(bouton);

//Connexion du signal clicked() du bouton à la fonction de rappel


augmenterTaille().

bouton.signal_clicked().connect(sigc::mem_fun(*this,
&Fenetre::augmenterTaille));

//Connexion de notre signal perso à la fonction resize().

signal_taille_max().connect(sigc::bind<int, int>(sigc::mem_fun(*this,
&Fenetre::resize), 100, 100));

show_all();

void Fenetre::augmenterTaille() {

int largeur(0);

int hauteur(0);

get_size(largeur, hauteur);

int nouvelleLargeur(largeur + 10);

int nouvelleHauteur(hauteur + 10);

resize(nouvelleLargeur, nouvelleHauteur);

if(nouvelleLargeur >= 200 or nouvelleHauteur >= 200) {

signal_taille_max().emit();

Fenetre::type_signal_taille_max Fenetre::signal_taille_max() {

return signalTailleMax;

}
Dans le constructeur, nous configurons notre fenêtre et nous connectons le signal clicked() et
notre signal perso à des fonctions de rappel (nous ajoutons des paramètres à l'une d'elle avec
sigc::bind).

Puis, nous avons notre fonction de rappel augmenterFenetre() que j'ai modifié un peu.
Après avoir redimensionné la fenêtre — pas avant, car le code n'aurait aucun effet à cause que la
méthode resize() connectée au signal taille_max() serait appelé avant la méthode resize()
de la présente fonction de rappel —, nous émettons le signal perso.
Enfin, nous avons notre accesseur.
Après deux chapitres très riche en nouveautés, je pense qu'un exemple qui regroupe la plupart de
celles-ci vous ferait le plus grand bien.

Exemple
Nous allons faire un programme qui utilise une barre de progression.
Lorsque cette barre de progression atteindra 50%, nous émettrons un signal qui ne transmet pas
d'argument.
Lorsque cette barre atteindra 100%, nous émettrons un signal qui transmet un argument.
Avec cet exemple, nous pratiquerons tout ce que nous avons vu lors de ce chapitre et du précédent.
Voici le résultat :

Le fichier main.cpp
Il est toujours identique :
#include <gtkmm/main.h>

#include "Fenetre.hpp"

int main(int argc, char* argv[]) {


Gtk::Main app(argc, argv);

Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Le fichier Fenetre.hpp
Nous allons voir ce fichier petit à petit.
Commençons par le début :
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <string>

#include <gtkmm/button.h>

#include <gtkmm/buttonbox.h>

#include <gtkmm/label.h>

#include <gtkmm/progressbar.h>

#include <gtkmm/stock.h>

#include <gtkmm/window.h>

L'habituelle protection est présente et nous ajoutons ensuite les entêtes nécessaires au programme.
Dans cet exemple, nous utiliserons un conteneur.
Nous verrons comment utiliser les conteneurs dans le prochain chapitre.
Nous vous attardez donc pas à cette partie du code.
Ensuite, nous créons la classe :
class Fenetre : public Gtk::Window {

public :

Fenetre();
Rien de nouveau...
Puis, nous créons nos deux signaux et nous créons une fonction de rappel :
//

typedef sigc::signal<void> type_signal_pourcentage_moitie; //Création


d'un typedef pour le type du signal.

type_signal_pourcentage_moitie signal_pourcentage_moitie(); //Création


de la méthode qui retourne le signal.

typedef sigc::signal<void, std::string> type_signal_pourcentage_max;

type_signal_pourcentage_max signal_pourcentage_max();

void ajouterPourcentage(); //Création d'une fonction de rappel.

Nous créons, comme à l'accoutumée, un typedef pour le type de chacun des signaux.
En dessous de ces typedefs, nous créons les prototypes des accesseurs pour les signaux.

Plus bas, dans protected, nous créons les-dits signaux :


//

protected :

type_signal_pourcentage_moitie signalPourcentageMoitie; //Création du


signal.

type_signal_pourcentage_max signalPourcentageMax;

Le signal pourcentage_moitie() sera émis lorsque la barre de progression atteint 50% et


pourcentage_max(), lorsqu'elle atteindra 100%.

Finalement, nous créons les widgets nécessaires au projet et terminons la classe :


//

private :

Gtk::ProgressBar barreProgression; //Création d'une barre de


progression.

Gtk::VButtonBox boiteV;

Gtk::Button bouton;

Gtk::Label etiquette;
};

#endif

Voici le fichier Fenetre.hpp au complet :


#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <string>

#include <gtkmm/button.h>

#include <gtkmm/buttonbox.h>

#include <gtkmm/label.h>

#include <gtkmm/progressbar.h>

#include <gtkmm/stock.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

typedef sigc::signal<void> type_signal_pourcentage_moitie; //Création


d'un typedef pour le type du signal.

type_signal_pourcentage_moitie signal_pourcentage_moitie(); //Création


de la méthode qui retourne le signal.

typedef sigc::signal<void, std::string> type_signal_pourcentage_max;

type_signal_pourcentage_max signal_pourcentage_max();

void ajouterPourcentage(); //Création d'une fonction de rappel.


protected :

type_signal_pourcentage_moitie signalPourcentageMoitie; //Création du


signal.

type_signal_pourcentage_max signalPourcentageMax;

private :

Gtk::ProgressBar barreProgression; //Création d'une barre de


progression.

Gtk::VButtonBox boiteV;

Gtk::Button bouton;

Gtk::Label etiquette;

};

#endif

Le fichier Fenetre.cpp
Voici le début de ce fichier :
#include "Fenetre.hpp"

Fenetre::Fenetre() : boiteV(Gtk::BUTTONBOX_SPREAD), bouton(Gtk::Stock::ADD) {

add(boiteV);

boiteV.pack_start(bouton);

bouton.set_can_focus(false);

boiteV.pack_start(barreProgression);

boiteV.pack_start(etiquette);

//Connexions des signaux aux fonction de rappel.

//...
//...

//Affichage des widgets.

show_all();

J'ai volontairement passé la partie de connexion des signaux pour vous la montrer plus tard.
Ne vous attardez pas au code ci-dessus, car il utilise des notions que vous n'avez pas encore vues
(les conteneurs).
Passons à notre fonction de rappel :
void Fenetre::ajouterPourcentage() {

double nouveauPourcentage(barreProgression.get_fraction() + 0.1);

if(std::abs(nouveauPourcentage - 0.5) < 0.00001) {

signal_pourcentage_moitie().emit(); //Émettre notre signal perso.

else if(std::abs(nouveauPourcentage - 1) < 0.00001) {

signal_pourcentage_max().emit("Le pourcentage est 100%."); //Émettre


notre signal perso avec un paramètre.

if(nouveauPourcentage <= 1.00001) {

barreProgression.set_fraction(nouveauPourcentage);

else {

barreProgression.set_fraction(0);

}
Cette fonction sera appelée lorsque l'utilisateur (donc vous ) cliquera sur le bouton Ajouter.
Premièrement, nous récupérons le pourcentage actuel avec get_fraction() et nous lui
ajoutons 0.1 (10%).
Ensuite, nous émettons nos signaux aux pourcentages voulus.
N'oubliez pas que le signal pourcentage_max() prend un paramètre !

Puis, si le pourcentage actuel est inférieur à 100%, nous pouvons mettre à jour la barre de
progression avec set_fraction().

Si le pourcentage est égal à 100%, nous remettons la barre de progression à 0% (pour tester
plusieurs fois ce merveilleux programme ).
Voyons les accesseurs :
Fenetre::type_signal_pourcentage_moitie Fenetre::signal_pourcentage_moitie()
{ //Cette méthode retourne le signal.

return signalPourcentageMoitie;

Fenetre::type_signal_pourcentage_max Fenetre::signal_pourcentage_max() {

return signalPourcentageMax;

Je n'ai rien à vous dire sur cette partie du code.


Ces accesseurs retournent les signaux appropriés.
Voyons maintenant les connexions des signaux aux fonctions de rappel (dans le constructeur) :
//

//Connexion du signal clicked() à une fonction de rappel.

bouton.signal_clicked().connect(sigc::mem_fun(*this,
&Fenetre::ajouterPourcentage));

//Connexion du signal pourcentage_moitie() à une fonction de rappel en


ajoutant un paramètre.

signal_pourcentage_moitie().connect(sigc::bind<std::string>(sigc::mem_fun(etique
tte, &Gtk::Label::set_text), "Le pourcentage est 50%."));

//Cette fois, pas besoin d'ajouter un paramètre, car le signal le transmet


automatiquement.

signal_pourcentage_max().connect(sigc::mem_fun(etiquette,
&Gtk::Label::set_text));
Nous connectons le signal clicked() du bouton à notre fonction de rappel
ajouterPourcentage() (à chaque fois que l'utilisateur clique sur le bouton, le pourcentage de
la barre de progression augmente de 10%).
Ensuite, nous connectons notre signal pourcentage_moitie() à la fonction de rappel
set_text().
Comme notre signal pourcentage_moitie() ne transmet aucun paramètre à la fonction de
rappel, nous devons en ajouter un avec sigc::bind.

Finalement, nous connectons notre autre signal (pourcentage_max()) à la fonction de rappel


set_text().
Cette fois, pas besoin d'ajouter un paramètre, car le signal le transmet (au moment de l'émission).
Lorsque la barre de progression atteint 50% et 100%, un message s'affiche dans le bas de la fenêtre.
La version C++ 2011 se fait comme ceci :
bouton.signal_clicked().connect([this]() { this->ajouterPourcentage(); });

signal_pourcentage_moitie().connect([&etiquette]() { etiquette.set_text("Le
pourcentage est 50%."); });

signal_pourcentage_max().connect([&etiquette](std::string texte)
{ etiquette.set_text(texte); });

Seule la dernière ligne pouvait vous poser des problèmes.


En effet, étant donné que le signal pourcentage_max() transmet un paramètre, il fallait que notre
fermeture en reçoive un pour pouvoir l’utiliser.
Voici le fichier Fenetre.cpp au complet :
#include "Fenetre.hpp"

Fenetre::Fenetre() : boiteV(Gtk::BUTTONBOX_SPREAD), bouton(Gtk::Stock::ADD) {

add(boiteV);

boiteV.pack_start(bouton);

bouton.set_can_focus(false);

boiteV.pack_start(barreProgression);

boiteV.pack_start(etiquette);

//Connexion du signal clicked() à une fonction de rappel.


bouton.signal_clicked().connect(sigc::mem_fun(*this,
&Fenetre::ajouterPourcentage));

//Connexion du signal pourcentage_moitie() à une fonction de rappel en


ajoutant un paramètre.

signal_pourcentage_moitie().connect(sigc::bind<std::string>(sigc::mem_fun(etique
tte, &Gtk::Label::set_text), "Le pourcentage est 50%."));

//Cette fois, pas besoin d'ajouter un paramètre, car le signal le transmet


automatiquement.

signal_pourcentage_max().connect(sigc::mem_fun(etiquette,
&Gtk::Label::set_text));

//Affichage des widgets.

show_all();

void Fenetre::ajouterPourcentage() {

double nouveauPourcentage(barreProgression.get_fraction() + 0.1);

if(std::abs(nouveauPourcentage - 0.5) < 0.00001) {

signal_pourcentage_moitie().emit(); //Émettre notre signal perso.

else if(std::abs(nouveauPourcentage - 1) < 0.00001) {

signal_pourcentage_max().emit("Le pourcentage est 100%."); //Émettre


notre signal perso avec un paramètre.

if(nouveauPourcentage <= 1.00001) {

barreProgression.set_fraction(nouveauPourcentage);

else {

barreProgression.set_fraction(0);

}
}

Fenetre::type_signal_pourcentage_moitie Fenetre::signal_pourcentage_moitie()
{ //Cette méthode retourne le signal.

return signalPourcentageMoitie;

Fenetre::type_signal_pourcentage_max Fenetre::signal_pourcentage_max() {

return signalPourcentageMax;

Voilà, vous savez maintenant beaucoup de choses sur la gestion des événements.
Si vous avez de la difficulté à comprendre, relisez ce chapitre et faites des essais par vous-même ;
c'est comme cela que vous progresserez.

En parlant de faire des essais , je vais vous faire travailler :


Comme vous pouvez le constater, les messages indiquant que la barre de progression est rendue à
50% et 100% demeurent présents lorsque la barre de progression est rendue à 60% et 0%.
Je vous demande d'effacer ces messages lorsqu'elle est rendue à ces pourcentages.
C'est un bon exercice pour vous pratiquer.

Passons aux Q.C.M.


Dans ce chapitre, vous avez appris à créer vos propres signaux pour gérer tous les évènements
possibles !
Maintenant, vous avez une bonne base pour pouvoir gérer la plupart les événements des différents
widgets.
Ne vous inquiétez pas, nous verrons d'autres signaux au cours de ce tutoriel pour gérer d'autres
événements.

Organiser vos fenêtres avec les conteneurs

Vous vous êtes rendu compte, il y a deux chapitres, qu'il était impossible d'ajouter plus d'un widget
dans une fenêtre.

Si c'était vraiment le cas, ce serait vraiment difficile de faire de jolies fenêtres. C'est pourquoi les
conteneurs ont été inventés.

Dans ceux-ci, nous pouvons mettre une infinité de widgets (en théorie ).
Voyons, à l'instant, comment ils fonctionnent.

Les différents conteneurs

Dans un premier temps, voyons ce que sont les conteneurs.


Les conteneurs permettent d'ajouter un ou plusieurs widgets dans une fenêtre. Donc, c’est une façon
d’organiser des widgets.
La plupart des conteneurs positionnent leurs widgets de façon relative de telle sorte que nous ne
devons pas préciser une valeur précise en pixel.
Nous devrons dire, par exemple, qu’un widget est le premier d’une ligne.
Cette façon de positionner les widgets a des avantages et des inconvénients. Un avantage est que la
disposition des widgets reste belle même en modifiant la taille de la fenêtre.
Un inconvénient est que nous ne pouvons pas indiquer précisément la position d’un widget.
Certains conteneurs ne peuvent contenir qu'un seul widget.
D'autres, ceux que nous utiliserons la plupart du temps, permettent d'en contenir plusieurs.
Cela nous permet d'organiser la façon dont les widgets sont disposés dans la fenêtre.
Les widgets faisant partie d'un conteneur sont également appelés widgets enfants.
Voyons les conteneurs que nous étudierons lors de ce chapitre.
Ensuite, nous verrons d'autres conteneurs qui ne feront pas l'objet de ce chapitre (vous pourrez
toutefois savoir comment vous en servir grâce à la documentation).

Principaux conteneurs

Le conteneur d'alignement
Il permet d'aligner un widget à un endroit précis (en bas à droite, au centre, etc.) :

La barre d'onglets
Vous l'avez vue lors du premier chapitre :
Les barres de défilement

Les boîtes
Elles permettent d'aligner plusieurs widgets.
En voici une (spécialisée pour les boutons) :
Les tableaux

Autres conteneurs
Le cadre

Le cadre d'apparence
Même chose que le conteneur précédent, mais il garde une proportion entre la largeur et la hauteur :

L'icône d'extension
Ce conteneur cache son widget enfant ; il faut cliquer sur une petite flèche (ou un plus, dans le cas
présent) pour qu'il s'affiche.
Lorsque le widget est caché :
Lorsqu'il est affiché :

Les panneaux
Nous pouvons y mettre deux widgets que nous pouvons redimensionner à l'aide d'une "poignée"
situé entre les deux widget:

Le widget détachable
Ce conteneur peut se séparer de la fenêtre.
Le widget fixé
Ce conteneur contient des widgets dont la position est fixe :

Faites attention en utilisant ce widget.


Si votre fenêtre est belle chez vous, cela ne signifie par qu'elle le sera chez votre voisin, car les
widgets ont une position absolue au lieu de relative.
Cela fait le tour de beaucoup de conteneurs.
Voyons comment les principaux fonctionnent.

Le conteneur d'alignement

Le premier conteneur que nous allons apprendre à utiliser ne peut contenir qu'un seul widget.
Par contre, après avoir vu les autres conteneurs de ce chapitre (en particulier les boîtes), vous saurez
en mettre plusieurs.
Mais, nous verrons que, parfois, il sera inutile d'utiliser le conteneur d'alignement.
Le conteneur d'alignement permet d'aligner un widget à un endroit précis, par exemple en bas à
droite de la fenêtre..
Création d'un conteneur d'alignement
Avant toute utilisation, ajoutez cette ligne à votre projet :
#include <gtkmm/alignment.h>

Voici le prototype de son constructeur :


Gtk::Alignment::Alignment(Align xalign, Align yalign = Gtk::ALIGN_CENTER, float
xscale = 1.0, float yscale = 1.0);

Attention au nom de la classe !


C'est Alignment - sans 'e' - et non Alignement, car alignement, en anglais, c'est alignment.

Détaillons les paramètres.

xalign
Ce paramètre indique l'alignement horizontal du widget.
Il faut donner une des constantes suivantes :
• Gtk::ALIGN_START, pour l'aligner à gauche ou en haut ;
• Gtk::ALIGN_CENTER, pour l'aligner au centre ;
• Gtk::ALIGN_END, pour l'aligner à droite ou en bas.
Est-il possible d'aligner un widget entre la gauche et le centre ?
Oui.
Pour cela, il faudra donner une valeur de 0.0 (gauche) à 1.0 (droite).
Par exemple, si nous indiquons 0.25, le widget sera exactement centré entre la gauche (0.0) et le
centre (0.5).

yalign
C'est comme xalign, mais c'est pour l'alignement vertical.
Les constantes sont les mêmes.
Il est également possible de donner une valeur de 0.0 (haut) à 1.0 (bas).

xscale
Ce paramètre indique l'espace horizontal que prend le widget dans son espacement alloué (de 0.0, le
minimum, à 1.0, le maximum).
Donc, si nous indiquons 0.5, le widget prendra la moitié de l'espace qui lui est alloué.
Si nous voulons que le widget soit aligné, il faut que cette valeur soit inférieure à 1, sinon, comme il
prendra tout l'espace disponible, il ne semblera pas aligné à un endroit précis.

yscale
Identique au précédent, sauf que cela concerne l'espace vertical.
Exemple
Je pense qu'un exemple vous serait très utile .
Pour que j'aie moins de code à vous montrer, je ne vais qu'utiliser un fichier main.cpp.
Mais, vous, dans vos projets, vous créerez une classe pour votre fenêtre.
#include <gtkmm/alignment.h>

#include <gtkmm/button.h>

#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

fenetre.set_border_width(10);

fenetre.set_default_size(200, 150); //Définir la taille par défaut de la


fenêtre.

Gtk::Alignment alignement(Gtk::ALIGN_END, Gtk::ALIGN_END, 0, 0); //Création


d'un conteneur d'alignement.

//L'alignement se fera en bas à droite.

//Il faut mettre 0, 0 comme derniers paramètres pour que le widget prenne le
moins d'espace possible.

fenetre.add(alignement);

Gtk::Button bouton("Clique-moi !");

bouton.set_can_focus(false);

alignement.add(bouton); //Ajouter un bouton au conteneur d'alignement.


fenetre.show_all(); //Afficher tous les widgets de la fenêtre. Cela évite
d'avoir à appeler show() pour chaque widget à afficher.

Gtk::Main::run(fenetre);

return 0;

Donc, nous créons le conteneur d'alignement à la ligne 14.


Nous l'ajoutons à la fenêtre, comme n'importe quel widget, avec la méthode add() (ligne 18).
Le widget contenu dans ce dernier sera aligné en bas à droite et prendra le moins d'espace possible.
Nous ajoutons ce widget (un bouton) à la ligne 23 le plus normalement possible : avec un add().

Dernier point important à remarquer :


j'utilise la méthode show_all() pour afficher tous les widgets de la fenêtre.
Cela m'évite d'avoir à appeler la méthode show() pour chaque widget.

Nous utiliserons toujours cette méthode lorsque c'est possible.


Voici le résultat :

Modification des paramètres du constructeur


Si vous voulez, après avoir appelé le constructeur, modifié les valeurs de xalign, yalign et
compagnie, vous pouvez appeler la méthode set() avec les mêmes paramètres que le constructeur :
void Gtk::Alignment::set(Align xalign, Align yalign = Gtk::ALIGN_CENTER, float
xscale = 1.0, float yscale = 1.0);

C'est tout ; passons maintenant à des conteneurs qui permettent d'ajouter plusieurs widgets.
Les boîtes
Les boîtes permettent d'aligner des widgets soit horizontalement, soit verticalement.
Pour utiliser les boîtes, vous devez inclure le fichier approprié :
#include <gtkmm/box.h>

Les boîtes verticales


Ce sont les Gtk::VBox.

Il est possible d'en créer une comme ceci :


Gtk::VBox boiteV;

Il est également possible d'en créer une avec des paramètres.


Le premier est un booléen qui indique si oui ou non les widgets prendront le même espace.
Donc, si le premier paramètre vaut true, chaque widget occupera un espace identique aux autres
de la même boîte.
Le deuxième paramètre indique la taille minimale (une marge) entre chaque widget (en pixel).
Exemple :
Gtk::VBox boiteV(false, 10);

Cela créera une boîte verticale dont les widgets ne prennent pas le même espace, mais qui ont au
moins 10px entre eux.

Les boîtes horizontales


Ces boîtes se créent et s'utilisent de la même manière que les boîte verticales, donc je ne vais pas
m'étendre sur leur fonctionnement.
Ce sont les Gtk::HBox (ah oui ?).

Ajout de widgets dans une boîte


Il y a deux méthodes pour ajouter un widget dans une boîte.

pack_start() et pack_end()
Ces méthodes prennent au moins un paramètre : le widget à ajouter à la boîte.
L'une d'elle en ajoute un à partir du début (pack_start()) et l'autre en ajoute un à partir de la fin
(pack_end()).

La plupart du temps, nous utiliserons pack_start(), mais nous pouvons utiliser pack_end()
pour être sûr qu'un widget se retrouvera à la fin.
Étant donné que le principe est difficile à comprendre, je vais vous l'expliquer avec un schéma :

À gauche se trouve une partie du code et à droite, le résultat.


Pour commencer (1), nous ajoutons une étiquette à partir de la fin. C'est pourquoi elle se trouve à la
fin dans le résultat final.
Ensuite (2), nous en ajoutons une autre à partir du début ; elle se trouve donc au début.
Jusque là, c'est facile à comprendre.
Puis (3), nous ajoutons une autre étiquette à partir de la fin.
Alors, elle est au-dessus de la première étiquette, car nous l'ajoutons à partir de la fin.
Cela signifie que la première étiquette ajoutée avec pack_end() se trouve à la fin, la deuxième,
au-dessus de cette dernière, la troisième au-dessus de la deuxième et ainsi de suite.
Après (4), nous ajoutons une autre étiquette, mais, cette fois, à partir du début.
Elle se trouve donc après l'étiquette 2, car nous l'ajoutons à partir du début, mais après l'étiquette 2.
Et ainsi de suite.
Il est également possible d'ajouter une boîte dans une boîte de telle sorte que nous pouvons les
imbriquer les unes dans les autres pour organiser nos widgets d'une façon plus complexe.

Avec d'autres paramètres


Il est possible d'utiliser pack_start() et pack_end() avec d'autres paramètres.

Le deuxième paramètre est une constante définissant le comportement du widget lorsque le


conteneur (donc la fenêtre) est redimensionné.
Nous pouvons utiliser l'une des trois constantes suivantes :
• Gtk::PACK_SHRINK : le widget ne prend que l'espace nécessaire ;
• Gtk::PACK_EXPAND_PADDING : le widget ne prend que l'espace nécessaire, mais,
lorsque la fenêtre s'agrandit, la marge autour du widget s'agrandit également ;
• Gtk::PACK_EXPAND_WIDGET : le widget prend tout l'espace qui lui est donné.
Le troisième paramètre est un espace supplémentaire (marge) autour de ce widget (en plus de
l'espace défini à la création de la boîte).

Exemple
#include <gtkmm/box.h>

#include <gtkmm/label.h>

#include <gtkmm/main.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::VBox boiteV(false, 10); //Création d'une boîte verticale. Il y aura un


minimum de 10px entre chaque widget.

Gtk::Label etiquette4("Mot écrit en trois langues différentes."); //Création


d'une étiquette.

boiteV.pack_end(etiquette4); //Ajout d'un widget à la fin de la boîte.

Gtk::HBox boiteH(true); //Création d'une boîte horizontale. Les widgets


prendront le même espace.

boiteV.pack_start(boiteH); //Ajout d'une boîte horizontale au début d'une


boîte verticale (donc avant etiquette4).

Gtk::Label etiquette1("Bonjour");

Gtk::Label etiquette2("Hello");

Gtk::Label etiquette3("Hola");

boiteH.pack_start(etiquette1); //Ajout d'un widget au début de la boîte.

boiteH.pack_start(etiquette2); //Ajout d'un widget après le premier.


boiteH.pack_start(etiquette3, Gtk::PACK_SHRINK); //L'argument
Gtk::PACK_SHRINK est ignoré puisque chaque widget a le même espace.

fenetre.add(boiteV); //Ajout de la boîte à la fenêtre.

fenetre.show_all(); //Afficher tous les widgets.

Gtk::Main::run(fenetre);

return 0;

Voilà ce que ça donne :

À la ligne 11, nous créons une boîte verticale dont les widgets n'ont pas nécessairement la même
taille et dont il y a un espace d'un minimum de 10px entre chaque widget.
À la ligne 14, nous ajoutons un widget à cette boîte à partir de la fin.
À l'instruction suivante, nous créons une boîte horizontale dont tous les widgets ont la même taille.
Ça ne paraît pas vraiment, mais mettez false (par défaut, c'est false) et vous verrez une petite
différence.
Pour vraiment la voir, essayez avec des boutons au lieu d'étiquettes.

Le reste est facile à comprendre (si vous avez bien suivi ).


Sauf peut-être à la ligne 25.
À cette ligne, nous demandons à ce que le widget prenne le moins d'espace possible
(Gtk::PACK_SHRINK), mais, lorsque nous avons créé la boîte, nous avions dit que nous voulions
que tous les widgets aient la même taille.
Donc, ce paramètre (Gtk::PACK_SHRINK) est ignoré.
En effet, le widget ne prend pas l'espace minimum, car il a une bordure invisible autour de lui.
S'il n'y avait pas cette bordure, le mot "Hola" serait collé au bord droit de la fenêtre (essayez pour
mieux comprendre).
Pour bien comprendre le principe de boîtes imbriquées, j'ai coloré l'image précédente :

En rouge, nous voyons la boîte verticale et en bleu, la boîte horizontale.

Exercice
Je vais vous faire travailler un petit peu.
Essayez de reproduire cette fenêtre en utilisant des boîtes imbriquées :

Les boîtes pour les boutons

Le problème des boutons


Si vous utilisez les boîtes que nous avons vues dans la sous-partie précédente pour y mettre des
boutons, cela n'est pas très... esthétique .
Le problème est surtout lorsque nous redimensionnons la boîte.
Voyons ce que cela peut donner :

Les boutons s'agrandissent.


Même en utilisant Gtk::PACK_SHRINK, ce n'est pas super :

Les boutons s'agrandissent horizontalement et laissent un espace vide en bas.

Utilisons les boîtes faites pour les boutons !


Il y a des boîtes qui sont faites exprès pour les boutons : les Gtk::ButtonBox.

Il en existe deux sortes :


• Gtk::HButtonBox ;

• Gtk::VButtonBox.

Ce sont ces deux dernières que nous allons utiliser pour créer des boîtes contenant des boutons.
Il faut inclure la classe :
#include <gtkmm/buttonbox.h>

Vous pouvez utiliser ces boîtes comme celles de la sous-partie précédente.


Voici un résultat possible :

C'est beaucoup mieux, n'est-ce pas ?


Il est possible d'utiliser ces boîtes avec d'autres widgets que les boutons.
Pour créer une boîte horizontale pour les boutons, faites comme ceci :
Gtk::HButtonBox boiteBoutonsH;

Modification de l'arrangement des boutons


set_layout()
Ceci permet de modifier l'arrangement des boutons dans la boîte.
Nous pouvons décider, par exemple, des les centrer ou de les mettre à la fin.
Pour que les boutons soient à la fin de la boîte (à droite ou en bas), appelez-la comme ceci :
boiteBoutonsH.set_layout(Gtk::BUTTONBOX_END);

Il est possible d'appeler le constructeur différemment pour ne pas avoir besoin d'appeler ensuite
set_layout() :
Gtk::HButtonBox boiteBoutonsH(Gtk::BUTTONBOX_END, 10);

Le premier paramètre indique l'arrangement des boutons et le deuxième (lui aussi facultatif),
l'espacement entre ces derniers.
Cela donne (si nous ajoutons des boutons avec pack_start() ou pack_end()) :

Voici les autres constantes :


• Gtk::BUTTONBOX_SPREAD : les boutons prennent tout l’espace, mais ils ne collent pas
les bords de la fenêtre ;
• Gtk::BUTTONBOX_EDGE : style par défaut, les boutons prennent tout l’espace ;
• Gtk::BUTTONBOX_START : les boutons sont au début de la boîte (haut ou gauche) ;
• Gtk::BUTTONBOX_END : les boutons sont à la fin de la boîte (droite ou bas) ;
• Gtk::BUTTONBOX_CENTER : les boutons sont au centre de la boîte.

set_child_secondary()
Cette méthode permet de rendre un widget secondaire.
Par exemple, si nous utilisons Gtk::BUTTONBOX_END, tous les boutons seront à la fin, sauf ceux
secondaires (ceux-ci seront au début).
Cette méthode s'utilise comme ceci :
boiteBoutonsH.set_child_secondary(boutonOptions);

ATTENTION !
Vous devez ajouter le widgetavant de le rendre secondaire, sinon cela ne fonctionnera pas !

Exemple
Voyons un exemple complexe complet :
#include <gtkmm/button.h>

#include <gtkmm/buttonbox.h>

#include <gtkmm/label.h>

#include <gtkmm/main.h>
#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

//Création d'une boîte pour les boutons (situés à la fin de la boîte) dont
l'espacement entre ceux-ci est de 10px.

Gtk::HButtonBox boiteBoutonsH(Gtk::BUTTONBOX_END, 10);

Gtk::Button boutonOptions("Options");

boutonOptions.set_can_focus(false);

Gtk::Button boutonAide("Aide");

boutonAide.set_can_focus(false);

Gtk::Button boutonFermer("Fermer");

boutonFermer.set_can_focus(false);

//Ajout de boutons à la boîte.

boiteBoutonsH.pack_start(boutonOptions);

boiteBoutonsH.pack_start(boutonAide);

boiteBoutonsH.pack_start(boutonFermer);

//Rendre un bouton secondaire, donc à part des autres. ATTENTION, ce widget


doit déjà être ajouté à la boîte pour que cela fonctionne.

boiteBoutonsH.set_child_secondary(boutonOptions);

fenetre.add(boiteBoutonsH); //Ajout de la boîte à la fenêtre.

fenetre.show_all();
Gtk::Main::run(fenetre);

return 0;

Il n'y a rien de bien compliqué ; je vous ai tout expliqué ce qui se passe.

Avec les commentaires, vous devriez comprendre.


Voici ce que ça donne :

Le bouton Options (à gauche) est secondaire.

Les tableaux

Les tableaux permettent d'organiser les widgets à la manière d'un... tableau.

Création d'un tableau


N'oubliez pas d'inclure la classe appropriée :
#include <gtkmm/table.h>

Pour créer un tableau, nous devons lui dire combien de colonnes et de rangées il aura :
Gtk::Table tableau(1, 2);

Ceci crée un tableau d'une rangée et de deux colonnes.

Redimensionnement
Il est possible de redimensionner un tableau après sa création avec la méthode resize() :
tableau.resize(2, 2);
Le tableau possède maintenant deux rangées et deux colonnes.

Ajout de widgets au tableau


Pour bien comprendre comment ajouter des widgets à un tableau, vous devrez comprendre ceci :
À l'instar d'un graphique mathématique, un tableau a des coordonnées.
Son point de départ est (0, 0) et est situé en haut à gauche (comme le quadrant 4 d'un graphique en
valeurs absolues).
Si vous comprenez ceci, vous n'aurez aucun problème .
Voici une image pour illustrer le tout :

Le point (0, 0) se trouve en haut à gauche du premier bouton.


Le point (1, 1) se trouve en bas à droite de ce même bouton.
Les coordonnées d'un widget sont de (x1, y1) à (x2, y2).
Dans le cas du premier bouton, c'est de (0, 0) à (1, 1).
Voyons le prototype de la méthode pour ajouter un bouton :
void Gtk::Table::attach(Widget& child, guint left_attach, guint right_attach,
guint top_attach, guint bottom_attach, AttachOptions xoptions = FILL|EXPAND,
AttachOptions yoptions = FILL|EXPAND, guint xpadding = 0, guint ypadding = 0);

Aïe Caramba !
Je vais vous détailler tout ceci :
• child est le widget que nous voulons ajouter au tableau ;
• left_attach est la coordonnée x1 (le côté gauche du widget) ;
• right_attach est la coordonnée x2 (le côté droit du widget) ;
• top_attach est la coordonnée y1 (le côté haut du widget) ;
• bottom_attach est la coordonnée y2 (le côté bas du widget) ;
• xoptions décrit le comportement du widget lorsqu'il est redimensionné horizontalement ;
• yoptions décrit le comportement du widget lorsqu'il est redimensionné verticalement ;
• xpadding est la marge (en px) à gauche et à droite du widget ;
• ypadding est la marge (en px) en haut et en bas du widget.

Un exemple ne vous ferait pas de mal, je crois.


Mais avant, je vais vous dire ce que nous pouvons donner comme xoptions et yoptions :
• Gtk::FILL : le widget prend l'espace qui lui est alloué ;
• Gtk::EXPAND : le widget prend l'espace non utilisé autour de lui ;
• Gtk::SHRINK : le widget prend le moins d'espace possible.
Nous pouvons combiner les deux premiers avec un |.
Si nous voulons que notre widget ne prenne qu'une seule rangée de large et une seule colonne de
haut et qu'il se trouve en haut à gauche, nous devrons faire comme ceci :
tableau.attach(bouton1, 0, 1, 0, 1);

Le widget est de (0, 0) à (1, 1).


Et si nous voulons qu'il prenne le moins d'espace possible, ce serait comme ceci :
tableau.attach(bouton1, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);

Si nous voulons qu'il soit à la troisième rangée et à la deuxième colonne et qu'il prenne deux
rangées de large, nous ferons :
tableau.attach(bouton1, 2, 4, 1, 2);

Le widget est de (2, 1) à (4, 2).


N'oubliez pas que ça commence à 0.
La première coordonnée est (0, 0), comme dans un graphique.
Et si nous voulons qu'il ait un espace horizontal de 5px et vertical de 10px, ce serait :
tableau.attach(bouton1, 2, 4, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::FILL |
Gtk::EXPAND, 5, 10);

Exemple
Voyons un exemple qui mélange tout ce que nous avons vu pour les tableaux :
#include <gtkmm/button.h>

#include <gtkmm/main.h>

#include <gtkmm/table.h>

#include <gtkmm/window.h>
int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

Gtk::Table tableau(1, 2); //Création d'un tableau de 1 case par 2 cases,


dont les cases ne sont pas nécessairement de même grandeur.

tableau.resize(2, 2); //Redimensionner le tableau (2 par 2).

fenetre.add(tableau);

Gtk::Button bouton1("Bouton 1");

Gtk::Button bouton2("Bouton 2");

Gtk::Button bouton3("Bouton 3");

bouton1.set_can_focus(false);

bouton2.set_can_focus(false);

bouton3.set_can_focus(false);

tableau.attach(bouton1, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK); //Ajout d'un


widget dans le tableau : ce widget sera de (0, 0) à (1, 1). Le widget prend le
moins d'espace possible.

tableau.attach(bouton2, 1, 2, 0, 1); //Le widget prend l'espace qui lui est


alloué et tout autre espace additionnel.

tableau.attach(bouton3, 0, 2, 1, 2, Gtk::FILL, Gtk::FILL, 25, 10); //Le


widget prend l'espace qui lui est alloué. Ajout d'espace (25px horizontal et
10px vertical) autour du widget.

fenetre.show_all(); //L'habituel show_all() pour afficher tous les widgets.

Gtk::Main::run(fenetre);

return 0;
}

Nous redimensionnons le tableau tout de suite après l'avoir créé, ce qui est tout à fait inutile (c'était
pour l'exemple ).
Vous devriez bien comprendre avec les commentaires et ce screenshot :

Faites des tests pour bien comprendre comment les tableaux fonctionnent.
Je vous assure que cela pourrait vous aider.

La barre d'onglets
La barre d'onglets ne permet d'afficher qu'un seul widget à la fois.
Cependant, en cliquant sur un autre onglet, nous pouvons voir un autre widget.

Vous les utilisez probablement avec votre navigateur Web (en tout cas, moi, je les utilise ).
C'est très pratique pour pouvoir visualiser ou éditer plusieurs documents "en même temps".

Création d'une barre d'onglets


Avant tout, vous devez inclure la classe Gtk::Notebook :
#include <gtkmm/notebook.h>

C'est très simple ; il vous suffit de créer un objet de type Gtk::Notebook :


Gtk::Notebook barreOnglets;
Quelques méthodes utiles

set_scrollable()
Lorsqu'il y a trop d'onglets, la fenêtre s'agrandit.
Cela peut devenir problématique.
En appelant cette méthode, des flèches se rajouteront pour naviguer dans les onglets, donc la fenêtre
peut être plus petite.
Exemple d'une barre d'onglets utilisant set_scrollable() :

Vous voyez, il y a des flèches à gauche et à droite pour naviguer.


Cette méthode s'appelle le plus simplement du monde :
barreOnglets.set_scrollable();

popup_enable()
Cette méthode ajoute un menu contextuel.
Lors d'un clic-droit sur la barre d'onglets, un menu contextuel apparaîtra et permettra à l'utilisateur
de se rendre immédiatement à un onglet de la barre.
Cette méthode s'appelle également très simplement :
barreOnglets.popup_enable();

Le résultat :

set_group_name()
Cette méthode permet de définir le groupe d'une barre d'onglets.
Deux barres d'un même groupe peuvent s'échanger des onglets.
Cela peut être pratique.
Cette méthode prend un paramètre :
une chaîne de caractères représentant le nom du groupe.
barreOnglets.set_group("onglets");

barreOnglets2.set_group("onglets");

Les deux barre d'onglets pourront s'échanger des onglets si ceux-ci sont détachables (nous verrons
plus bas comment faire).

append_page()
Le plus important.
Cela permet d'ajouter une page à la barre d'onglets.
Cette méthode prend au minimum un paramètre : le widget à ajouter.
Exemple :
barreOnglets.append_page(texte1);

Le texte écrit sur l'onglet sera "Page 1" si c'est le premier widget à être ajouté, "Page 2" si c'est le
deuxième, etc.
Il existe aussi d'autres méthodes pour ajouter un widget, dont prepend_page() et
insert_page(), respectivement pour ajouter un widget au début et en ajouter un à un position
précise.
Dans le prochain exemple, je vous détaillerai l'utilisation de append_page() avec d'autre
paramètres.

remove_page()
Cette méthode enlève un onglet et son widget de la barre d'onglets.
Elle prend en paramètre soit le numéro de la page de l'onglet, soit le widget lui-même.
ATTENTION !
Comme pour les tableaux, les numéros de page débutent à 0.

set_tab_reorderable()
Cette méthode permet à l'utilisateur de réordonner un widget (modifier sa position) en utilisant le
glisser-déposer.
Elle prend un paramètre : le widget.

set_tab_detachable()
Cette méthode permet à l'utilisateur de détacher un widget, c'est-à-dire de l'enlever de la barre
d'onglets pour le mettre dans une autre.
Elle prend elle aussi un paramètre : le widget.
Ne pas oublier que les deux barres d'onglets doivent avoir le même groupe pour pouvoir s'échanger
des pages !

next_page() et prev_page()
next_page() avance à la prochaine page de l'onglet et prev_page() va à la page précédente.

set_action_widget()
Voici la dernière méthode que nous verrons dans cette sous-partie.
Elle permet d'ajouter un widget à gauche ou à droite des onglets.
Rien ne nous empêche d'appeler deux fois cette méthode pour en avoir un à gauche et un à droite.
Elle prend un pointeur sur le widget et l'une des deux constantes suivantes :
• Gtk::PACK_START : à gauche des onglets ;
• Gtk::PACK_END : à droite des onglets.
Il faut afficher manuellement ce widget avec show() sinon il n'apparaîtra pas !

Exemple
Cette fois, nous allons utiliser trois fichiers (main.cpp, Fenetre.cpp et Fenetre.hpp).

main.cpp
Il est comme à l'accoutumée :
#include "Fenetre.hpp"

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Fenetre fenetre;

Gtk::Main::run(fenetre);

return 0;

Fenetre.hpp
#ifndef DEF_FENETRE

#define DEF_FENETRE

#include <gtkmm/box.h>

#include <gtkmm/buttonbox.h>
#include <gtkmm/button.h>

#include <gtkmm/image.h>

#include <gtkmm/label.h>

#include <gtkmm/main.h>

#include <gtkmm/notebook.h>

#include <gtkmm/stock.h>

#include <gtkmm/window.h>

class Fenetre : public Gtk::Window {

public :

Fenetre();

void modifierTitre(Gtk::Widget* page, int numPage);

private :

Gtk::Notebook barreOnglets;

Gtk::Notebook barreOnglets2;

Gtk::HButtonBox boiteBoutonsH;

Gtk::VBox boiteV;

Gtk::Button boutonNouvelOnglet;

Gtk::Button boutonPrecedent;

Gtk::Button boutonSuivant;

Gtk::Button boutonSupprimerOnglet;

Gtk::Image imageNouveau;

Gtk::Image imageSupprimer;

Gtk::Label texte1;

Gtk::Label texte2;

Gtk::Label texte3;

Gtk::Label texte4;

Gtk::Label texte5;
Gtk::Label texte6;

Gtk::Label texte7;

};

#endif

Voici quelques explications :


modifierTitre() est une fonction de rappel.
Elle modifiera le titre lors du changement d'onglets de la première barre d'onglets.
Ceci crée une image :
Gtk::Image imageNouveau;

Cette image sera créée à partir d'un Stock Item (une image prédéfinie).

Fenetre.cpp
C'est un assez gros fichier (n'ayez pas peur, je vais vous l'expliquer ):
#include "Fenetre.hpp"

Fenetre::Fenetre() : boutonPrecedent(Gtk::Stock::GO_BACK),
boutonSuivant(Gtk::Stock::GO_FORWARD), texte1("Ceci est la première page de
l'onglet."), texte2("Ceci est la deuxième page de l'onglet."), texte3("Ceci est
la troisième page de l'onglet."), texte4("Ceci est la quatrième page de
l'onglet."), texte5("Ceci est la cinquième page de l'onglet."), texte6("Ceci est
la sixième page de l'onglet."), texte7("Ceci est la septième page de
l'onglet."), imageNouveau(Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON),
imageSupprimer(Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON) {

add(boiteV);

boutonPrecedent.set_can_focus(false);

boiteBoutonsH.pack_start(boutonPrecedent);

boutonSuivant.set_can_focus(false);

boiteBoutonsH.pack_start(boutonSuivant);

boiteV.pack_start(boiteBoutonsH);
/*

* Création de la barre d'onglets

* */

//S'il y a trop d'onglets, nous pouvons naviguer à l'aide de flèche.

barreOnglets.set_scrollable();

//Ajouter un menu contextuel lors du clic-droit pour la navigation.

barreOnglets.popup_enable();

//Empêcher la barre d'avoir le focus.

barreOnglets.set_can_focus(false);

//Définir le groupe de la barre d'onglets (pour pouvoir échanger des onglets


avec d'autres barre d'onglets).

barreOnglets.set_group_name("onglets");

//Ajout d'une page

//Ajout de texte1 avec "Page 1" écrit sur l'onglet.

barreOnglets.append_page(texte1);

barreOnglets.set_tab_reorderable(texte1); //Cet onglet peut être réordonné.

barreOnglets.set_tab_detachable(texte1); //Cet onglet peut être détaché.

//Ajout de texte2 avec "Page 2" écrit sur l'onglet.

barreOnglets.append_page(texte2);

barreOnglets.set_tab_reorderable(texte2);

barreOnglets.set_tab_detachable(texte2);

//Ajout de texte3 avec "Onglet 3" écrit sur l'onglet.

barreOnglets.append_page(texte3, "Onglet 3");


//Cet onglet ne peut pas être réordonné, ni détaché.

//Ajout de texte4 avec "Page 4" écrit sur l'onglet. Un clic sur Alt-4 nous
amène à cet onglet.

barreOnglets.append_page(texte4, "Page _4", true);

//Le trait de soulignement (_) est situé devant le raccourci.

barreOnglets.set_tab_reorderable(texte4);

barreOnglets.set_tab_detachable(texte4);

barreOnglets.append_page(texte5, "Page 5");

barreOnglets.set_tab_reorderable(texte5);

barreOnglets.set_tab_detachable(texte5);

barreOnglets.append_page(texte6, "Page 6");

barreOnglets.set_tab_reorderable(texte6);

barreOnglets.set_tab_detachable(texte6);

//Enlever le 6e onglet. Il est aussi possible de donner le widget en


paramètre.

barreOnglets.remove_page(5);

/*

* Création d'un bouton d'action.

* */

boutonNouvelOnglet.set_can_focus(false);

boutonNouvelOnglet.set_image(imageNouveau); //Modifier l'image du bouton.

boutonSupprimerOnglet.set_can_focus(false);

boutonSupprimerOnglet.set_image(imageSupprimer); //Modifier l'image du


bouton.
//Ajout d'un widget d'action à la fin de la barre d'onglets.

barreOnglets.set_action_widget(&boutonNouvelOnglet, Gtk::PACK_END);

//Ajout d'un widget d'action au début de la barre d'onglets.

barreOnglets.set_action_widget(&boutonSupprimerOnglet, Gtk::PACK_START);

boutonNouvelOnglet.show(); //Il ne faut pas oublier de l'afficher


manuellement.

boutonSupprimerOnglet.show();

/*

* Création d'un bouton d'action.

* */

boiteV.pack_start(barreOnglets);

/*

* Création de la barre d'onglets

* */

/*

* Création d'une deuxième barre d'onglets

* */

barreOnglets2.set_scrollable();

barreOnglets2.popup_enable();

barreOnglets2.set_can_focus(false);

barreOnglets2.set_group_name("onglets");
barreOnglets2.append_page(texte7, "Page 7");

barreOnglets2.set_tab_reorderable(texte7);

barreOnglets2.set_tab_detachable(texte7);

boiteV.pack_start(barreOnglets2);

/*

* Création d'une deuxième barre d'onglets

* */

//Connexions

boutonPrecedent.signal_clicked().connect(sigc::mem_fun(barreOnglets,
&Gtk::Notebook::prev_page));

boutonSuivant.signal_clicked().connect(sigc::mem_fun(barreOnglets,
&Gtk::Notebook::next_page));

barreOnglets.signal_switch_page().connect(sigc::mem_fun(*this,
&Fenetre::modifierTitre));

show_all();

void Fenetre::modifierTitre(Gtk::Widget* page, int numPage) {

//Modifier le titre de la fenêtre selon l'onglet choisi.

set_title(barreOnglets.get_tab_label_text(*barreOnglets.get_nth_page(numPage)));

Voici le résultat :
Vous commencez à faire des fenêtre de plus en plus complexes.
Que d'explications s'imposent.

La liste d'initialisation
À la ligne 3, nous commençons la liste d'initialisation en créant deux boutons à partir de Stock Item.
Ces boutons permettront à l'utilisateur de naviguer dans les onglets.
Toujours dans la liste d’initialisation, nous créons les widgets qui seront dans les onglets.
À la fin de cette liste, nous créons des images... à partir de Stock Item.
Le premier paramètre est le Stock Item et le deuxième, la taille de l'image.

Ajout de widgets
Ensuite, nous ajoutons une boîte verticale à la fenêtre et des boutons à une autre boîte.
Nous ajoutons la boîte pour les boutons dans la boîte verticale.

La première barre d'onglets


Par la suite, nous créons une barre d'onglet, puis, nous la modifions quelque peu.

Notez que le troisième onglet ne peut pas être déplacé, ni détaché (essayez de le faire ).
Le quatrième onglet est ajouté d'une façon différente ; nous envoyons un troisième paramètre qui
vaut true.
Cela signifie que nous voulons lui ajouter une mnémonique.
Cette mnémonique permettra d’accéder à l’onglet avec le combinaison de touches Alt + le
caractère précédé d'un trait de soulignement (_).
Pour "Page _4", le raccourci est donc Alt-4.

À la ligne 58, nous enlevons le 6e onglet ainsi que son widget.


Plus loin, nous ajoutons une image différente pour chaque bouton avec set_image() et nous
ajoutons les boutons à la barre d'onglets avec set_action_widget().
Il ne faut pas oublier des afficher avec show() !

Nous faisons de même avec un second bouton.

La deuxième barre d'onglets


Nous la créons et la modifions ; vous devriez comprendre facilement.

Connexions des signaux aux fonctions de rappel


Aux lignes 108 et 109, nous connectons le clic sur chacun des boutons à une méthode de
Gtk::Notebook qui permet d'avancer et de reculer dans la barre d'onglets.

À la ligne 110, nous voyons un nouveau signal (nous n'avons pas vraiment eu l'occasion d'en voir
d'autres dans ce chapitre ):
switch_page()
Ce signal est émis lorsqu'il y a un changement de page.
Nous le connectons à notre fonction de rappel personnalisé (modifierTitre()).

Enfin, nous affichons tous les widgets avec show_all().

Voyons notre fonction de rappel qui comporte quelques nouveautés :


void Fenetre::modifierTitre(Gtk::Widget* page, int numPage) {

//Modifier le titre de la fenêtre selon l'onglet choisi.

set_title(barreOnglets.get_tab_label_text(*barreOnglets.get_nth_page(numPage)));

Elle appelle set_title() afin de modifier le titre de la fenêtre.

En paramètre, nous avons... un gros morceau :


barreOnglets.get_tab_label_text(*barreOnglets.get_nth_page(numPage))

La méthode get_tab_label_text() nous donne le texte présent sur l'onglet d'un widget
particulier.
Et get_nth_page() nous donne un pointeur vers le widget de la page numPage.
numPage est un paramètre transmis par le signal pour nous dire à quelle page la barre d'onglets est.

Veuillez noter que nous aurions pu également utiliser cette ligne :


set_title(barreOnglets.get_tab_label_text(*page));
Elle est plus courte, mais elle vous montre moins de méthodes.

C'est tout, mais je vous demande un petit exercice.

Exercice
Modifiez le code ci-dessus pour que les boutons à gauche et à droite de la barre d'onglet enlèvent et
ajoutent des onglets.
Pensez à utiliser Gtk::manage(), car vous devez gérer un nombre indéterminé de widgets.
Ça ne devrait pas vous causer des problèmes.

Les barres de défilement


Si un widget prend trop de place pour être affiché au complet, nous pouvons utiliser les barres de
défilement pour n'afficher qu'une partie de ce widget, mais avec des barres de défilement pour se
déplacer sur ce widget.
Vous avez un exemple sous les yeux :
Eh oui ! Votre navigateur web utilise des barres de défilement, car la plupart des pages Web sont
trop grandes pour être affichées dans la fenêtre sans ces barres.
Vous pouvez, grâces à elles, vous déplacer dans la page Web.

Utilisation
Avant toute utilisation, il faut inclure la bonne classe :
#include <gtkmm/scrolledwindow.h>

Les barres de défilement s'utilise le plus simplement du monde.


Il suffit de créer un objet de type Gtk::ScrolledWindow et d'y ajouter un widget.
C'est un autre conteneur à enfant unique (il ne peut contenir qu'un seul widget).
Exemple :
Gtk::ScrolledWindow barresDeDefilement;

Une de ses méthodes mérite votre attention :


void Gtk::ScrolledWindow::set_policy(PolicyType hscrollbar_policy, PolicyType
vscrollbar_policy);

Cela permet de dire au widget si vous voulez toujours voir les barres de défilement, seulement
lorsque nécessaire ou jamais.
Personnellement, j'aime bien les afficher seulement lorsque nécessaire.
Afficher les barres de défilement seulement lorsque c'est nécessaire signifie qu'elles seront absentes
(invisibles) lorsque nous pouvons voir au complet le widget.
Il faut donc appeler la méthode comme ceci pour ce faire :
barresDeDefilement.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

Exemple
Voyons un exemple :
#include <gtkmm/main.h>

#include <gtkmm/scrolledwindow.h>

#include <gtkmm/textview.h>

#include <gtkmm/window.h>

int main(int argc, char* argv[]) {

Gtk::Main app(argc, argv);

Gtk::Window fenetre;

fenetre.set_border_width(10);

//Création d'un conteneur ayant des barres de défilement.

Gtk::ScrolledWindow barresDeDefilement;

//Afficher les barres de défilement seulement lorsque c'est nécessaire.

barresDeDefilement.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

fenetre.add(barresDeDefilement);

Gtk::TextView zoneDeTexte; //Création d'une zone de texte.

barresDeDefilement.add(zoneDeTexte); //Ajout de la zone de texte au


conteneur.

fenetre.show_all();
Gtk::Main::run(fenetre);

return 0;

À la ligne 13, nous créons le widget de type Gtk::ScrolledWindow.


À l’instruction suivante, nous appelons la méthode set_policy() pour que les barres de
défilement ne s'affichent que lorsque le widget n'est pas visible au complet dans la fenêtre.
Dans cet exemple, nous affichons une zone de texte multiligne.
Pour voir les barres de défilement, il faut entrer du texte jusqu'au bord de la fenêtre.
Cela donne ceci :

Si nous n'avions pas utilisé les barres de défilement pour afficher la zone de texte, la fenêtre se
serait vu agrandir au fur et à mesure que nous ajoutons du texte.

Cela devient gênant lorsque celle-ci devient plus grande que l'écran .
Avec les conteneurs, vous pouvez vraiment organiser vos fenêtres comme vous le voulez.
Après avoir vu deux notions importantes (les signaux et les conteneurs), nous allons relaxer un peu
en voyant de nouveaux widgets (donc, pas de nouvelles notions ).
Maintenant, de nombreuses possibilités de conception de fenêtres s'offrent à vous.
Je vous lance un petit défi : créez-moi un jeu de morpion (Tic-tac-toe).
Cela mettra en pratique vos notions de signaux et de conteneurs.
Si vous n'êtes pas capable d'implémenter une petite intelligence artificielle pour pouvoir jouer
contre, faites un morpion à deux joueurs humains.

Si vous êtes capables d'implémenter une IA, faites les deux modes de jeux.
Le jeu final peut ressembler à ceci :
Comment vais-je faire pour savoir quel bouton a été cliqué par l'utilisateur ?
Dois-je créer une fonction de rappel par bouton ?
Non, ce serait beaucoup trop lourd.
Vous vous rappelez le chapitre sur les signaux (moi, oui )?
Eh bien, vous devrez créer une fonction de rappel qui prend un paramètre (un Gtk::Button) que
vous lierez avec sigc::bind, comme vous l'avez appris.
De cette façon, vous pourrez savoir quel bouton a été cliqué sans avoir à faire 9 fonctions de rappel.

Vous aimerez peut-être aussi