GeckoGeek.fr

Paradigme d'un lézard

Lundi 24 Juillet 2017

Signal et Slot avec QT 4.5 et QObject

Par Vinz le 06/10/2009 dans Programmation | 2 commentaires

Il peut arriver que vous souhaitiez discuter entre certaines classes vraiment éloignées. Les applications sont diverses : rafraichir des zones graphiques à certains moments, envoyer une variable à l’autre bout du monde, ou tout simplement effectuer une action quand un bouton est cliqué…
Quoiqu’il en soit QT offre un moyen facile de résoudre ces intentions qui peuvent rapidement devenir un problème avec certaines langages. Je vais donc essayer de résumer le plus simplement possible les bases qui permettent de manier les signaux (en Anglais cela donne “signals”) et les slots (celui là on le laisse en Anglais :-p).

Logo QT

Introduction

Imaginons que votre programme ressemble à un sport collectif. Chaque personne représente une classe plus ou moins indépendante. Pour augmenter vos chances de gagner il faut que vos joueurs communiquent entre eux. Hélas ils n’ont pas de talkies walkies intégrés et doivent ainsi se trouver un moyen de parler. Les signaux et les slots fonctionnent en fait assez simplement. Quand un de vos joueurs souhaite envoyer une information à un autre joueur, il suffit qu’il déclenche un “signal”. Une fois ce signal émit, une information sera directement transmise dans le “slot” de l’autre joueur. Toutefois pour pouvoir communiquer ainsi, il faut préalablement que les deux joueurs aient été mis au courant par une “connexion” entre le “signal” et le “slot”.

Dit plus rapidement en langage “courant”, vous connectez deux méthodes entre elles. Ces deux méthodes peuvent appartenir à des classes différentes, cela ne gène pas. Par ailleurs vous pouvez aussi envoyer de l’information (des variables simples, des objets, …) mais là aussi il faut le prévoir à l’avance :-]

Bien établir ses besoins

Avant de commencer à rajouter des lignes par-ci par-là dans votre code je vous conseille de bien analyser vos besoins. Par ailleurs les signaux et slots ne sont pas forcément à utiliser dans tous les cas. Si par exemple les deux fonctions à appeler sont dans la même classe ou si le slot (méthode qui reçoit l’information) est dans une classe inférieure, alors il y a sans aucun doute un moyen d’éviter d’utiliser cette fonctionnalité. Le but n’est pas non plus de trop pourrir le code :-]

Que dois-je clarifier ?
– La classe qui va envoyer le “signal”. Il n’y aura pas besoin de créer de fonction supplémentaire dans cette classe, juste en définir une dans le header file.
– La méthode qui va recevoir l’information.
Les informations à faire transiter (facultatif)
La classe parente qui va s’occuper de créer la “connexion” (dit autrement, la classe qui aura un pointeur sur les deux autres classes ciblées).

Déclarer et créer les méthodes

Pour notre exemple on va travailler sur quelque chose d’assez simple. Je ne vais pas vous afficher des codes complets (header et source) de toutes les classes impliquées sinon on ne s’en sortira pas :-p (et vous seriez un peu pommé ^^). Exemple bateau hummm une voiture ! On va travailler avec les classes Bateau, Moteur et Capitaine. L’exemple ne sera pas des plus “beau” en terme d’architecture, toutefois c’est histoire de bien faire comprendre le concept ^^ J’oublierai intentionnellement tous les includes nécessaires et n’afficherai que les classes et quelques bouts de codes. Veuillez à bien inclure les “QObjects” nécessaires :-) Vous pouvez vous référer à la page officielle.

Ci-dessous le header de votre classe “parente” qui possède un lien entre les deux classes qui nous intérèssent. Rien de particulier si vous connaissez déjà quelques trucs sous QT. Elle contient deux pointers sur Moteur et Capitaine, classes qu’on va essayer de faire communiquer via un signal / slot.

class Bateau : public QWidget {

	Q_OBJECT

	public:
    		Bateau(QWidget *parent);
		~Bateau();

	private:
		Moteur		*myMoteur;
		Capitaine	*myCapitaine;

};

Maintenant déclarons le header file de la classe qui possède le “signal”. Ici nous dirons que le Capitaine décide de faire accélérer le bateau, et les moteurs devront accélérer en conséquence. Il représente donc la classe qui décide d’envoyer un signal :

class Capitaine : public QWidget {

	Q_OBJECT

	public:
    		Capitaine(QWidget *parent);
		~Capitaine();

	signals:
		void changeVitesseBateau(int valeur);

};

Vous pouvez voir ici que le signal se déclare après la balise “signals:”. Pas besoin d’inclure une source pour la méthode “changeVitesseBateau()” car ce n’est qu’un signal. Vous ne ferez que dire “Hey ho” et c’est la méthode “slot” qui se changera d’executer du code. En parlant d’elle, voici la classe qui héberger la fonction dont il est question. Pour rappel c’est la classe Moteur :

class Moteur : public QWidget {

	Q_OBJECT

	public:
    		Moteur(QWidget *parent);
		~ Moteur();

	public slots:
		void vitesseBateauAlteree(int valeur);

};

Ici nous remarquons la présence d’un “public slots:” avant la définition de la méthode. Cette fois ci elle va devoir être inclue dans le fichier source (.cpp). Rien de bien compliqué car au final c’est une fonction comme une autre. La subtilité se fait juste dans sa déclaration :

void Moteur::vitesseBateauAlteree(int valeur) {

	// Altération des variables dans votre classe

}

Connecter un signal et un slot

Maintenant que nos méthodes sont présentes on va pouvoir faire le “lien”. Il n’y a que QObject à rajouter dans la “classe parente”. On imagine que les classes Capitaine et Moteur ont déjà été instanciées plus haut. Si on résume encore une fois : le Capitaine émet le signal et les Moteurs agissent :

QObject::connect(this->myCapitaine, SIGNAL(changeVitesseBateau(int)),
			this->myMoteur, SLOT(vitesseBateauAlteree(int)));

Si on découpe la méthode statique connect() de la classe QObject on arrive à quelque chose d’assez simple :

  1. Pointeur sur la classe qui contient le signal
  2. Nom de la méthode qui fait office de signal
  3. Pointeur sur la classe qui contient le slot
  4. Nom de la méthode qui fait office de slot

Vous aurez remarqué que pour les méthodes nous n’indiquons que le type de la variable : int, string, objet, etc. Si vous n’avez pas de variable à faire passer, laisser les parenthèses vides. Veuillez à bien respecter l’ordre indiqué ci-dessus :-]

Vous pouvez très bien faire communiquer avec la classe courante. Pour ce soir il suffit juste d’indiquer “this” comme pointeur de classe, et puis le nom de la méthode en signal ou slot :-)

Connecter deux signaux

Vous pouvez avoir envie de construire une cascade de signaux. Ainsi lorsqu’un signal sera émis, l’autre aussi et ainsi de suite si vous en avez plusieurs de connectés. Attention, l’exemple ci-dessous ne reprend aucune méthode déclaré auparavant.

QObject::connect(source1, SIGNAL(methodeSignal1()),
			source2, SIGNAL(methodeSignal2()));

Emettre un signal

C’est bien beau, on a définit les signaux et les slots et les différentes liaisons, mais on ne sait toujours pas comment activer tout ça. Là encore rien de très compliqué, il suffit d’appeler la méthode “emit” dans la classe qui contient la définition de votre signal (donc ici la classe Capitaine). Petit exemple :

emit changeVitesseBateau(10); // On envoie le signal

Supprimer une connexion

Il peut arriver que vous souhaitiez arrêter l’observation d’un signal. Prenons notre exemple de bateau comme cas :

QObject::disconnect(this->myCapitaine, SIGNAL(changeVitesseBateau(int)),
			this->myMoteur, SLOT(vitesseBateauAlteree(int)));

Aller plus loin…

Il existe d’autres possibilités et des méthodes que nous ne décrirons pas ici. Pour plus de renseignements, rendez-vous sur la documentation officielle qobject :-] N’hésitez pas à laisser un commentaire si ce billet vous a été utile ou si vous avez une question !

Commentaires (2)
  1. adil le 23 Mar 2010 à 01:27

    juste une petite erreur de frape, il faut changer (*myCapintaire;) par (*myCapintaine;)
    et merci pour l’explication.

  2. Vinz le 23 Mar 2010 à 09:42

    Hello,
    Merci pour avoir souligné cette coquille :-) c’est maintenant corrigé.
    Content que cela ait pu te servir :-)
    A+ !


Laisser un commentaire