[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Officiels
> Programmation
> Apprenez à programmer en C++ ! > [Pratique] Créez vos propres fenêtres avec Qt > Les signaux et les slots
> Lecture du tutoriel
Les signaux et les slots
Nous commençons à maîtriser petit à petit la création d'une fenêtre. Dans le chapitre précédent, nous avons posé de solides bases pour développer par la suite notre application. Nous avons réalisé une classe personnalisée, héritant de QWidget.
Nous allons maintenant découvrir le mécanisme des
signaux et des slots, un principe propre à Qt qui est clairement un de ses points forts. Il s'agit d'une technique séduisante pour gérer les évènements au sein d'une fenêtre.
Par exemple, si on clique sur un bouton, on voudrait qu'une fonction soit appelée pour réagir au clic. C'est précisément ce que nous apprendrons à faire dans ce chapitre, qui va enfin rendre votre application dynamique
Le principe est plutôt simple à comprendre : une application de type GUI réagit à partir d'évènements. C'est ce qui rend votre fenêtre dynamique.
Ceux d'entre vous qui ont déjà essayé la bibliothèque SDL se souviennent peut-être de la gestion des évènements : interception des touches du clavier, des déplacements de la souris, du joystick, etc.
Ce que Qt propose, c'est la même chose mais à plus haut niveau : c'est donc beaucoup plus facile à gérer.
On parle de signaux et de slots, mais qu'est-ce que c'est concrètement ? C'est un concept inventé par Qt. Voici une petite définition en guise d'introduction :
- Un signal : c'est un message envoyé par un widget lorsqu'un évènement se produit.
Exemple : on a cliqué sur un bouton.
- Un slot : c'est la fonction qui est appelée lorsqu'un évènement s'est produit. On dit que le signal appelle le slot. Concrètement, un slot est une méthode d'une classe.
Exemple : le slot quit() de la classe QApplication, qui provoque l'arrêt du programme.
Les signaux et les slots sont considérés par Qt comme des éléments d'une classe à part entière, en plus des attributs et des méthodes.
Voici un schéma qui montre ce qu'un objet pouvait contenir avant Qt, ainsi que ce qu'il peut contenir maintenant qu'on utilise Qt :
Qt rajoute des éléments appelés "Signaux" et "Slots" aux objets
Avant Qt, un objet était constitué d'attributs et de méthodes. C'est tout.
Qt rajoute en plus la possibilité d'utiliser ce qu'il appelle des signaux et des slots pour gérer les évènements.
Un signal est un message envoyé par l'objet (par exemple "on a cliqué sur le bouton").
Un slot est une... méthode. En fait,
c'est une méthode classique comme toutes les autres, à la différence près qu'elle a le droit d'être connectée à un signal.
Avec Qt, on dit que l'
on connecte des signaux et des slots entre eux. Supposons que vous ayez deux objets, chacun ayant ses propres attributs, méthodes, signaux et slots (je n'ai pas représenté les attributs et les méthodes sur mon schéma pour simplifier) :
Sur le schéma ci-dessus, on a connecté le signal 1 de l'objet 1 avec le slot 2 de l'objet 2.
Il est possible de connecter un signal à plusieurs slots. Ainsi, un clic sur un bouton pourrait appeler non pas une mais plusieurs méthodes. Attention, si un signal est connecté à plusieurs slots, il est impossible de prédire dans quel ordre Qt appellera les slots.
Comble du raffinement, il est aussi possible de connecter un signal à un autre signal. Le signal d'un bouton peut donc provoquer la création du signal d'un autre widget, qui peut à son tour appeler des slots (voire appeler d'autres signaux pour provoquer une réaction en chaîne !). C'est un peu particulier et on ne verra pas ça dans ce chapitre.
Voyons un cas très concret. Je vais prendre 2 objets, l'un de type QPushButton, et l'autre de type QApplication. Dans le schéma ci-dessous, ce que vous voyez sont de
vrais signaux et slots que vous allez pouvoir utiliser :
Regardez attentivement ce schéma. Nous avons d'un côté notre bouton appelé "m_bouton" (de type QPushButton), et de l'autre notre application (de type QApplication, utilisé dans le main).
Nous voudrions par exemple connecter le signal "bouton cliqué" au slot "quitter l'application". Ainsi, un clic sur le bouton provoquerait l'arrêt de l'application.
Pour ce faire, nous devons utiliser une méthode statique de la classe QObject : connect().
Le principe de la méthode connect()
connect() est une méthode statique. Vous vous souvenez ce que ça veut dire ?
Une méthode statique est une méthode d'une classe que l'on peut appeler sans créer d'objet. C'est en fait exactement comme une fonction classique du langage C.
Pour appeler une méthode statique, il faut faire précéder son nom du nom de la classe dans laquelle elle est déclarée. Comme connect() appartient à la classe QObject, il faut donc écrire :
Code : C++
La méthode connect prend 4 arguments :
- Un pointeur vers l'objet qui émet le signal.
- Le nom du signal que l'on souhaite "intercepter".
- Un pointeur vers l'objet qui contient le slot récepteur.
- Le nom du slot qui doit s'exécuter lorsque le signal se produit.
Pour que vous puissiez vous repérer, j'ai remis ci-contre le schéma qu'on a vu un peu plus haut. Les couleurs sont les mêmes, cela devrait vous permettre de bien visualiser à quoi correspond chaque attribut.
Il existe aussi une méthode disconnect() permettant de casser la connexion entre 2 objets, mais on n'en parlera pas ici car on en a rarement besoin.
Utilisation de la méthode connect() pour quitter
Revenons au code, et plus précisément au constructeur de MaFenetre (fichier MaFenetre.cpp). Ajoutez cette ligne :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13 | #include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(300, 150);
m_bouton = new QPushButton("Quitter", this);
m_bouton->setFont(QFont("Comic Sans MS", 14));
m_bouton->move(110, 50);
// Connexion du clic du bouton à la fermeture de l'application
QObject::connect(m_bouton, SIGNAL(clicked()), qApp, SLOT(quit()));
}
|
connect() est une méthode de la classe QObject. Comme notre classe MaFenetre hérite de QObject indirectement, elle possède elle aussi cette méthode. Cela signifie que dans ce cas, et dans ce cas uniquement, on peut enlever le préfixe QObject:: devant le connect() pour appeler la méthode statique.
J'ai choisi de conserver ce préfixe dans le cours pour rappeler qu'il s'agit d'une méthode statique, mais sachez donc qu'il n'a rien d'obligatoire si la méthode est appelée depuis une classe fille de QObject.
Etudions attentivement cette ligne et plus particulièrement les paramètres que l'on envoie à connect() :
- m_bouton : c'est un pointeur vers le bouton qui va émettre le signal. Facile.
- SIGNAL(clicked()) : là c'est assez perturbant comme façon d'envoyer un paramètre. En fait, SIGNAL() est une macro du préprocesseur. Qt transformera ça en un code "acceptable" pour la compilation. Le but de cette technique est de vous faire écrire un code court et compréhensible. Ne cherchez pas à comprendre comment Qt fait pour transformer le code, on s'en fout

- qApp : c'est un pointeur vers l'objet de type QApplication que nous avons créé dans le main. D'où sort ce pointeur ? Euh... joker

En fait, Qt crée automatiquement un pointeur appelé qApp vers l'objet de type QApplication que nous avons créé. Ce pointeur est défini dans le header <QApplication>, que nous avons inclut dans "MaFenetre.h".
- SLOT(quit()) : c'est le slot qui doit être appelé lorsqu'on a cliqué sur le bouton. Là encore, il faut utiliser la macro SLOT() pour que Qt traduise ce code "bizarre" en quelque chose de compilable.
Le slot quit() de notre objet de type QApplication est un
slot prédéfini. Il en existe d'autres, comme aboutQt() qui affiche une fenêtre "A propos de Qt".
Parfois, pour ne pas dire souvent, les slots prédéfinis par Qt ne nous suffiront pas. Nous apprendrons dans la suite de ce chapitre à créer les nôtres.
Testons notre code ! La fenêtre qui s'ouvre est la suivante :
Rien de bien extraordinaire à première vue. Sauf que... si vous cliquez sur le bouton "Quitter", le programme s'arrête !
Hourra, on vient de réussir à connecter notre premier signal à un slot !
Utilisation de la méthode connect() pour afficher "A propos"
On peut faire un autre essai pour se faire un peu plus la main si vous voulez. Je vous ai parlé d'un autre slot de QApplication : aboutQt().
Je vous propose de créer un second bouton qui se chargera d'afficher la fenêtre "A propos de Qt".
Je vous laisse rédiger le code tous seuls comme des grands.
...
...
C'est bon ?
Voici le code final
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | #include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(300, 150);
m_quitter = new QPushButton("Quitter", this);
m_quitter->setFont(QFont("Comic Sans MS", 14));
m_quitter->move(110, 50);
QObject::connect(m_quitter, SIGNAL(clicked()), qApp, SLOT(quit()));
m_aPropos = new QPushButton("A propos", this);
m_aPropos->setFont(QFont("Comic Sans MS", 14));
m_aPropos->move(110, 90);
QObject::connect(m_aPropos, SIGNAL(clicked()), qApp, SLOT(aboutQt()));
}
|
Vous noterez que j'ai pris la liberté de nommer les boutons avec des noms un peu plus compréhensibles.
Bien entendu, le fichier MaFenetre.h a un peu changé lui aussi du coup pour déclarer les attributs "m_quitter" et "m_aPropos", mais vous êtes assez grands pour le faire sans moi
Le résultat est une fenêtre qui affiche 2 boutons :
Le bouton "Quitter" ferme toujours l'application.
Quant à "A propos", il provoque l'ouverture de la fenêtre "A propos de Qt".
La méthode statique
connect() est assez originale, vous l'avez vu. Il s'agit justement d'une des particularités de Qt que l'on ne retrouve pas dans les autres bibliothèques.
Ces autres bibliothèques, comme wxWidgets par exemple, utilisent à la place de nombreuses macros et se servent du mécanisme un peu complexe et délicat des pointeurs de fonction (pour indiquer l'adresse de la fonction à appeler en mémoire).
Il y a d'autres avantages à utiliser la méthode connect() avec Qt. On va ici découvrir que
les signaux et les slots peuvent s'échanger des paramètres !
Dessin de la fenêtre
Dans un premier temps, nous allons placer de nouveaux widgets dans notre fenêtre.
Vous pouvez enlever les boutons, on ne va plus s'en servir ici.
A la place, je souhaite vous faire utiliser 2 nouveaux widgets :
- QSlider : un curseur qui permet de définir une valeur.
- QLCDNumber : un widget qui affiche un nombre.
On va aller un peu plus vite, je vous donne le code directement pour créer ça.
Tout d'abord, le header :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #ifndef DEF_MAFENETRE
#define DEF_MAFENETRE
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLCDNumber>
#include <QSlider>
class MaFenetre : public QWidget
{
public:
MaFenetre();
private:
QLCDNumber *m_lcd;
QSlider *m_slider;
};
#endif
|
J'ai donc enlevé les boutons comme vous pouvez le voir, et rajouté un QLCDNumber et un QSlider.
Surtout, n'oubliez pas d'inclure le header de ces classes pour pouvoir les utiliser. J'ai gardé l'include du QPushButton ici, ça ne fait pas de mal de le laisser mais si vous ne comptez pas le réutiliser vous pouvez le virer sans crainte.
Et le fichier .cpp :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13 | #include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(200, 100);
m_lcd = new QLCDNumber(this);
m_lcd->setSegmentStyle(QLCDNumber::Flat);
m_lcd->move(50, 20);
m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setGeometry(10, 60, 150, 20);
}
|
Les détails ne sont pas très importants. J'ai modifié le type d'afficheur LCD pour qu'il soit plus lisible (avec setSegmentStyle). Quant au slider, j'ai rajouté un paramètre pour qu'il apparaisse horizontalement (sinon il est vertical).
Voilà qui est fait. Avec ce code, cette petite fenêtre devrait s'afficher :
Connexion avec des paramètres
Maintenant... connexiooooon !
C'est là que les choses deviennent intéressantes. On veut que l'afficheur LCD change de valeur en fonction de la position du curseur du slider.
On dispose du signal et du slot suivant :
- Le signal valueChanged(int) du QSlider : il est émis dès que l'on change la valeur du curseur du slider en le déplaçant. La particularité de ce signal est qu'il envoie un paramètre de type int (la nouvelle valeur du slider).
- Le slot display(int) du QLCDNumber : il affiche la valeur qui lui est passée en paramètre.
La connexion se fait avec le code suivant :
Code : C++1 | QObject::connect(m_slider, SIGNAL(valueChanged(int)), m_lcd, SLOT(display(int)));
|
Bizarre n'est-ce pas ?

Il suffit d'indiquer le type du paramètre envoyé, ici un int, sans donner de nom à ce paramètre. Qt fait automatiquement la connexion entre le signal et le slot et "transmet" le paramètre au slot.
Le transfert de paramètre se fait comme ceci :
Ici il n'y a qu'un paramètre à transmettre, c'est donc simple. Sachez toutefois qu'il pourrait très bien y avoir plusieurs paramètres.
Le type des paramètres doivent correspondre absolument !
Vous ne pouvez pas connecter un signal qui envoie (int, double) à un slot qui reçoit (int, int). C'est un des avantages du mécanisme des signaux et des slots : il respecte le type des paramètres. Veillez donc à ce que les signatures soient identiques entre votre signal et votre slot.
En revanche, un signal peut envoyer plus de paramètres à un slot que celui-ci ne peut en recevoir. Dans ce cas, les paramètres supplémentaires seront ignorés.
Résultat : quand on change la valeur du slider, le LCD affiche la valeur correspondante !
Mais comment je sais moi quels sont les signaux et les slots que proposent chacune des classes ? Et aussi, comment je sais qu'un signal envoie un int en paramètre ?
La réponse devrait vous paraître simple les amis :
la doc, la doc, la doc !
Si vous regardez la
documentation de la classe QLCDNumber, vous pouvez voir au début la liste de ses propriétés (attributs) et ses méthodes. Un peu plus bas, vous avez la liste des slots ("Public Slots") et des signaux ("Signals") qu'elle possède !
Exercice
Pour vous entraîner, je vous propose de réaliser une petite variation du code source précédent.
Au lieu d'afficher le nombre avec un QLCDNumber, affichez-le sous la forme d'une jolie barre de progression comme ceci :
Je ne vous donne que 3 indications qui devraient vous suffire :
- La barre de progression est gérée par un QProgressBar
- Il faut donner des dimensions à la barre de progression pour qu'elle apparaisse correctement, à l'aide de la méthode setGeometry() que l'on a déjà vue auparavant.
- Le slot récepteur du QProgressBar est setValue(int). Il s'agit d'un de ses slots, mais la documentation vous indique qu'il y en a d'autres. Par exemple, reset() remet à zéro la barre de progression. Pourquoi ne pas ajouter un bouton qui remettrait à zéro la barre de progression ?
C'est tout. Bon courage
Voici maintenant une partie très intéressante, bien que plus délicate. Nous allons créer nos propres signaux et slots.
En effet, si en général les signaux et slots par défaut suffisent, il n'est pas rare que l'on se dise "
Zut, le signal (ou le slot) dont j'ai besoin n'existe pas". C'est dans un cas comme celui-là qu'il devient indispensable de créer son widget personnalisé.
Pour pouvoir créer son propre signal ou slot dans une classe, il faut que celle-ci dérive directement ou indirectement de QObject. C'est le cas de notre classe MaFenetre : elle hérite de QWidget, qui hérite de QObject. On a donc le droit de créer des signaux et des slots dans MaFenetre.
Nous allons commencer par créer notre propre slot, puis nous verrons comment créer notre propre signal.
Créer son propre slot
Je vous rappelle tout d'abord qu'un slot n'est rien d'autre qu'une méthode que l'on peut connecter à un signal.
Nous allons donc créer une méthode, mais en suivant quelques règles un peu particulières...
Le but du jeu
Pour nous entraîner, nous allons inventer un cas où le slot dont on a besoin n'existe pas.
Je vous propose de conserver le QSlider (je l'aime bien celui-là

) et de ne garder que ça sur la fenêtre. Nous allons
faire en sorte que le QSlider contrôle la largeur de la fenêtre.
Votre fenêtre doit ressembler à cela :
Nous voulons que le signal valueChanged(int) du QSlider puisse être connecté à un slot de notre fenêtre (de type MaFenetre). Ce nouveau slot aura pour rôle de modifier la largeur de la fenêtre.
Comme
il n'existe pas de slot "changerLargeur" dans la classe QWidget, nous allons devoir le créer.
Pour créer ce slot, il va falloir modifier un peu notre classe MaFenetre. Commençons par le header.
Le header (MaFenetre.h)
Dès que l'on doit créer un signal ou un slot personnalisé, il est nécessaire de définir une macro dans le header de la classe.
Cette macro porte le nom de Q_OBJECT (tout en majuscules) et doit être placée tout au début de la déclaration de la classe :
Code : C++ 1
2
3
4
5
6
7
8
9
10 | class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre();
private:
QSlider *m_slider;
};
|
Pour le moment, notre classe ne définit qu'un attribut (le QSlider, privé) et une méthode (le constructeur, public).
La macro Q_OBJECT "prépare" en quelque sorte le compilateur à accepter un nouveau mot-clé : "slot". Nous allons maintenant pouvoir créer une section "slots", comme ceci :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13 | class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre();
public slots:
void changerLargeur(int largeur);
private:
QSlider *m_slider;
};
|
Vous noterez la nouvelle section "public slots". Je rends toujours mes slots publics. On peut aussi les mettre privés mais ils seront quand même accessibles de l'extérieur car Qt a besoin de pouvoir appeler un slot depuis n'importe quel autre widget.
A part ça, le prototype de notre slot-méthode est tout à fait classique. Il ne nous reste plus qu'à l'implémenter dans le .cpp.
L'implémentation (MaFenetre.cpp)
L'implémentation est d'une simplicité redoutable. Regardez :
Code : C++1
2
3
4 | void MaFenetre::changerLargeur(int largeur)
{
setFixedSize(largeur, 100);
}
|
Le slot prend en paramètre un entier : la nouvelle largeur de la fenêtre.
Il se contente d'appeler la méthode setFixedSize de la fenêtre et de lui envoyer la nouvelle largeur qu'il a reçue.
Connexion
Bien, voilà qui est fait. Enfin presque : il faut encore connecter notre QSlider au slot de notre fenêtre. Où va-t-on faire ça ? Dans le constructeur de la fenêtre (toujours dans MaFenetre.cpp) :
Code : C++ 1
2
3
4
5
6
7
8
9
10 | MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(200, 100);
m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setRange(200, 600);
m_slider->setGeometry(10, 60, 150, 20);
QObject::connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(changerLargeur(int)));
}
|
J'ai volontairement modifié les différentes valeurs que peut prendre notre slider pour le limiter entre 200 et 600 avec la méthode
setRange(). Ainsi, on est sûr que notre fenêtre ne pourra ni être plus petite que 200 pixels de largeur, ni être plus grande que 600 pixels de largeur.
La connexion se fait entre le signal
valueChanged(int) de notre QSlider, et le slot
changerLargeur(int) de notre classe MaFenetre. Vous voyez là encore un exemple où
this est indispensable : il faut pouvoir indiquer un pointeur vers l'objet actuel (la fenêtre) et seul
this peut faire ça !
Schématiquement, on a réalisé la connexion suivante :
Compilation
Avec toutes les nouveautés que nous venons d'utiliser par rapport au C++, la compilation par un make ne suffira pas.
Je vous avais dit qu'il fallait refaire un qmake à chaque fois que les fichiers du projet changeaient. En fait j'ai un peu menti

Comme vous utilisez la macro Q_OBJECT, Qt a besoin d'appeler un pré-compilateur qui lui est propre appelé le
moc (Meta-Object Compiler).
Rassurez-vous, vous n'avez rien à faire de spécial. Relancez juste un qmake avant de faire votre make, et Qt fera le travail de "traduction" du slot en quelque chose de compréhensible pour le compilateur C++.
Vous noterez que le qmake a provoqué la création d'un fichier intermédiaire moc_MaFenetre.cpp, ce qui est parfaitement normal. Ce fichier fournit des informations indispensables au compilateur.
Vous pouvez ensuite faire un make, la compilation devrait bien se passer.
Souvenez-vous ! Si jamais lors de la compilation vous rencontrez l'erreur suivante :
undefined reference to 'vtable for MaFenetre'
... cela signifie que vous n'avez pas fait de qmake avant. Si le moc ne s'est pas exécuté auparavant, la compilation échouera.
Vous pouvez enfin admirer le résultat. Ouf !
Amusez-vous à redimensionner la fenêtre comme bon vous semblera avec le slider. Comme nous avons fixé les limites du slider entre 200 et 600, la largeur de la fenêtre restera comprise entre 200 et 600 pixels.
Exercice : redimensionner la fenêtre en hauteur
Voici un petit exercice, mais qui va vous forcer à travailler (bande de fainéants, vous me regardez faire depuis tout à l'heure

).
Je vous propose de créer un second QSlider, vertical cette fois, qui contrôlera la hauteur de la fenêtre. Pensez à bien définir des limites appropriées pour les valeurs de ce nouveau slider.
Vous devriez obtenir un résultat qui ressemblera à ça :
Si vous voulez "conserver" la largeur pendant que vous modifiez la hauteur, et inversement, vous aurez besoin d'utiliser les méthodes accesseur width() (largeur actuelle) et height() (hauteur actuelle).
Vous comprendrez très certainement l'intérêt de ces informations lorsque vous coderez. Au boulot !
Créer son propre signal
Il est plus rare d'avoir à créer son signal que son slot, mais cela peut arriver.
Je vous propose de réaliser le programme suivant : si le slider horizontal arrive à sa valeur maximale (600 dans notre cas), alors on émet un signal "agrandissementMax". Notre fenêtre doit pouvoir émettre l'information comme quoi elle est agrandie au maximum.
Après, nous connecterons ce signal à un slot pour vérifier que notre programme réagit correctement.
Le header (MaFenetre.h)
Commençons par changer le header :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre();
public slots:
void changerLargeur(int largeur);
signals:
void agrandissementMax();
private:
QSlider *m_slider;
};
|
On a ajouté une section "signals". Les signaux se présentent en pratique sous forme de méthodes (comme les slots) à la différence près qu'on ne les implémente pas dans le .cpp. En effet, c'est Qt qui le fait pour nous. Si vous tentez d'implémenter un signal, vous aurez une erreur du genre "
Multiple definition of...".
Un signal peut passer un ou plusieurs paramètres. Dans notre cas, il n'en envoie aucun.
Un signal doit toujours renvoyer void.
L'implémentation (MaFenetre.cpp)
Maintenant que notre signal est défini, il faut que notre classe puisse l'émettre à un moment.
Quand est-ce qu'on sait que la fenêtre a été agrandie au maximum ? Dans le slot changerLargeur ! Il suffit de tester dans ce slot si la largeur correspond au maximum (600), et d'émettre alors le signal "
Youhou, j'ai été agrandie au maximum !".
Retournons dans MaFenetre.cpp et implémentons ce test qui émet le signal depuis changerLargeur :
Code : C++1
2
3
4
5
6
7
8
9 | void MaFenetre::changerLargeur(int largeur)
{
setFixedSize(largeur, height());
if (largeur == 600)
{
emit agrandissementMax();
}
}
|
Notre méthode s'occupe toujours de redimensionner la fenêtre, mais vérifie en plus si la largeur a atteint le maximum (600). Si c'est le cas, elle émet le signal agrandissementMax().
Pour émettre un signal, on utilise le mot-clé
emit, là encore un terme inventé par Qt qui n'existe pas en C++. L'avantage est que c'est très lisible, on comprend "
Emettre le signal agrandissementMax()".
Ici, notre signal n'envoie pas de paramètres. Toutefois, sachez que si vous voulez envoyer un paramètre c'est très simple. Il suffit d'appeler votre signal comme ceci : emit monSignal(parametre1, parametre2, ...);
Connexion
Il ne nous reste plus qu'à connecter notre nouveau signal à un slot. Vous pouvez connecter ce signal au slot que vous voulez. Personnellement, je propose de le connecter à l'application (à l'aide du pointeur global qApp) pour provoquer l'arrêt du programme.
Ca n'a pas trop de sens je suis d'accord, mais c'est juste pour s'entraîner et vérifier que ça fonctionne. Vous aurez l'occasion de faire des connexions plus logiques plus tard, je ne m'en fais pas pour ça
Dans le constructeur de MaFenetre, je rajoute donc :
Code : C++1 | QObject::connect(this, SIGNAL(agrandissementMax()), qApp, SLOT(quit()));
|
Vous pouvez tester le résultat : normalement le programme s'arrête quand la fenêtre est agrandie au maximum.
Le schéma des signaux qu'on vient d'émettre et connecter est le suivant :
Dans l'ordre, voici ce qui s'est passé :
- Le signal valueChanged du slider a appelé le slot changerLargeur de la fenêtre.
- Le slot a fait ce qu'il avait à faire (changer la largeur de la fenêtre) et a vérifié si la fenêtre était arrivée à sa taille maximale. Lorsque cela a été le cas, le signal personnalisé agrandissementMax() a été émis.
- Le signal agrandissementMax() de la fenêtre était connecté au slot quit() de l'application, ce qui a provoqué la fermeture du programme.
Et voilà comment le déplacement du slider peut, par réaction en chaîne, provoquer la fermeture du programme !
Bien entendu, ce schéma peut être aménagé et complexifié selon les besoins de votre application.
Maintenant que vous savez créer vos propres slots et signaux, vous avez toute la souplesse nécessaire pour
faire ce que vous voulez !
Eh ben dites donc les amis, que de nouveautés dans ce chapitre décidément !
Les signaux et les slots, c'est vraiment ce qui fait la force de Qt... mais ses détracteurs disent que c'est une erreur d'avoir voulu "modifier" le langage C++. En effet, la compilation est plus lourde car il y a des étapes de pré-compilation à effectuer impérativement si on veut que le code soit compilable. C'est un point de vue qui se défend.
L'avantage de ce système, et ça personne ne le discute, c'est qu'il est robuste. On dispose d'une extraordinaire souplesse pour faire communiquer des objets entre eux :
- Un signal peut appeler le slot d'un autre objet pour l'informer d'un évènement.
- Un signal peut appeler plusieurs slots d'objets différents si nécessaire pour faire plusieurs traitements.
- Un signal peut être connecté à un autre signal directement, qui lui-même peut être raccordé à un autre signal (réaction en chaîne) ou appeler un slot.
- La connexion entre un signal et un slot permet d'échanger un ou plusieurs paramètres.
- L'échange de paramètres entre le signal et le slot est sécurisé : Qt vérifie que la signature du signal correspond bien à celle du slot.
Les autres bibliothèques, comme wxWidgets, utilisent un ensemble de macros, moins lisibles mais qui ne nécessitent pas l'utilisation d'outils intermédiaires comme le moc.
Bref, profitez à fond des signaux et des slots, avec ça vous pouvez vraiment faire ce que vous voulez