« Programmation Qt/Signaux et slots » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
<source> bientôt obsolète, remplacé par <syntaxhighlight>
Ligne 20 : Ligne 20 :


{{FichierDébut|Telechargement.h|largeur=70%|info=(extrait) Définition du signal|icon=Crystal128-source-h.svg}}
{{FichierDébut|Telechargement.h|largeur=70%|info=(extrait) Définition du signal|icon=Crystal128-source-h.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
class Telechargement: public QObject
class Telechargement: public QObject
{
{
Ligne 32 : Ligne 32 :
// ...
// ...
}
}
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


Ligne 39 : Ligne 39 :


{{FichierDébut|Telechargement.cpp|largeur=70%|info=(extrait) Utilisation du signal|icon=Crystal128-source-cpp.svg}}
{{FichierDébut|Telechargement.cpp|largeur=70%|info=(extrait) Utilisation du signal|icon=Crystal128-source-cpp.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
void Telechargement::Terminer()
void Telechargement::Terminer()
{
{
Ligne 47 : Ligne 47 :
emit fichierRecu( this->m_chemin_fichier );
emit fichierRecu( this->m_chemin_fichier );
}
}
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


Ligne 57 : Ligne 57 :


{{FichierDébut|Stockage.h|largeur=70%|info=(extrait) Déclaration des slots|icon=Crystal128-source-h.svg}}
{{FichierDébut|Stockage.h|largeur=70%|info=(extrait) Déclaration des slots|icon=Crystal128-source-h.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
class Stockage: public QObject
class Stockage: public QObject
{
{
Ligne 71 : Ligne 71 :
// ...
// ...
}
}
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


{{FichierDébut|Stockage.cpp|largeur=70%|info=(extrait) Définition des slots|icon=Crystal128-source-cpp.svg}}
{{FichierDébut|Stockage.cpp|largeur=70%|info=(extrait) Définition des slots|icon=Crystal128-source-cpp.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
// ...
// ...


Ligne 94 : Ligne 94 :


// ...
// ...
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


Ligne 117 : Ligne 117 :


{{FichierDébut|Main.cpp|info=(extrait) Connextion entre signaux et slots|icon=Crystal128-source-cpp.svg}}
{{FichierDébut|Main.cpp|info=(extrait) Connextion entre signaux et slots|icon=Crystal128-source-cpp.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
// ...
// ...


Ligne 129 : Ligne 129 :


// ...
// ...
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


Ligne 135 : Ligne 135 :
Par exemple, pour successivement copier puis supprimer le fichier et enfin afficher un message quand le téléchargement est terminé :
Par exemple, pour successivement copier puis supprimer le fichier et enfin afficher un message quand le téléchargement est terminé :
{{FichierDébut|Main.cpp|info=(extrait) Connextion entre signaux et slots|icon=Crystal128-source-cpp.svg}}
{{FichierDébut|Main.cpp|info=(extrait) Connextion entre signaux et slots|icon=Crystal128-source-cpp.svg}}
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
// ...
// ...


Ligne 152 : Ligne 152 :


// ...
// ...
</syntaxhighlight>
</source>
{{FichierFin}}
{{FichierFin}}


Ligne 169 : Ligne 169 :


<code>connect()</code> est une méthode statique. On écrit donc :
<code>connect()</code> est une méthode statique. On écrit donc :
<source lang="cpp-qt">QObject::connect();</source>
<syntaxhighlight lang="cpp-qt">QObject::connect();</syntaxhighlight>


La méthode <code>connect</code> prend 4 arguments :
La méthode <code>connect</code> prend 4 arguments :
Ligne 179 : Ligne 179 :


===Exercice : la fenêtre se ferme après avoir cliqué sur un bouton===
===Exercice : la fenêtre se ferme après avoir cliqué sur un bouton===
Lançons-nous dans la création du code de <code>mafenetre.cpp</code>.<source lang="cpp-qt">
Lançons-nous dans la création du code de <code>mafenetre.cpp</code>.
<syntaxhighlight lang="cpp-qt">
//main.cpp
//main.cpp
#include <QApplication>
#include <QApplication>
Ligne 194 : Ligne 196 :
return app.exec();
return app.exec();
}
}
</syntaxhighlight>
</source>


<source lang="cpp-qt">
<syntaxhighlightlang="cpp-qt">
//MaFenetre.h
//MaFenetre.h
#ifndef DEF_MAFENETRE
#ifndef DEF_MAFENETRE
Ligne 215 : Ligne 217 :
#endif
#endif
</syntaxhighlight>
</source>


<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
//MaFenetre.cpp
//MaFenetre.cpp
#include "MaFenetre.h"
#include "MaFenetre.h"
Ligne 232 : Ligne 234 :
QObject::connect(m_bouton, SIGNAL(clicked()), qApp, SLOT(quit()));
QObject::connect(m_bouton, SIGNAL(clicked()), qApp, SLOT(quit()));
}
}
</syntaxhighlight>
</source>


Exécutons la commande <code>qmake</code>, puis testons notre code. Une fenêtre s'ouvre.
Exécutons la commande <code>qmake</code>, puis testons notre code. Une fenêtre s'ouvre.
Ligne 246 : Ligne 248 :


Voici le code:
Voici le code:
<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
//MaFenetre.h
//MaFenetre.h
#ifndef DEF_MAFENETRE
#ifndef DEF_MAFENETRE
Ligne 267 : Ligne 269 :
#endif
#endif
</syntaxhighlight>
</source>


<source lang="cpp-qt">
<syntaxhighlight lang="cpp-qt">
#include "MaFenetre.h"
#include "MaFenetre.h"
Ligne 286 : Ligne 288 :
QObject::connect(m_aPropos, SIGNAL(clicked()), qApp, SLOT(aboutQt()));
QObject::connect(m_aPropos, SIGNAL(clicked()), qApp, SLOT(aboutQt()));
}
}
</syntaxhighlight>
</source>


On compile maintenant, et le bouton "Quitter" ferme toujours l'application. Quant à "À propos", il provoque l'ouverture de la fenêtre "À propos de Qt".
On compile maintenant, et le bouton "Quitter" ferme toujours l'application. Quant à "À propos", il provoque l'ouverture de la fenêtre "À propos de Qt".

Version du 13 avril 2020 à 17:18


Dans le premier exemple du chapitre précédent, cliquer sur le bouton "Hello world!" ne déclenchait aucune action. Pour qu'un bouton puisse être utile, il faut connecter le signal clicked() à un slot ayant la même signature.

Définition

Les signaux et slots permettent d'interconnecter des objets Qt entre eux :

  • un signal est un évènement envoyé par un objet (exemple : clic de la souris sur un bouton) ;
  • un slot est une fonction réalisant l'action associée à un signal.

Tout objet Qt peut définir des signaux, et des slots pour recevoir des signaux en provenance d'autres objets Qt.

Conclusion : en appuyant par exemple sur un bouton, celui-ci envoie le signal clicked(). Quand on recevra ce signal de la part de ce bouton on va déclencher une action par un slot : par exemple la fenêtre qui quitte.

Définir un signal

Un signal est défini dans le fichier d'en-tête déclarant la classe :

Telechargement.h
(extrait) Définition du signal
class Telechargement: public QObject
{
    Q_OBJECT

    // ...

public signals:
    void fichierRecu(QString chemin_fichier);

    // ...
}


Aucune implémentation n'est à définir dans la classe. Le signal est utilisable dans la définition de la classe :

Telechargement.cpp
(extrait) Utilisation du signal
void Telechargement::Terminer()
{
    // Nettoyage...
    // ...
    // Signaler la fin
    emit fichierRecu( this->m_chemin_fichier );
}


Définir un slot

Un slot est similaire à une méthode de la classe :

  • Il doit être déclaré dans l'en-tête, dans une section slots ;
  • Il doit être implémenté dans la définition de la classe.
Stockage.h
(extrait) Déclaration des slots
class Stockage: public QObject
{
    Q_OBJECT

    // ...

public slots:
    bool stockerFichier(QString chemin_fichier);
    bool supprimerFichier(QString chemin_fichier);
    void finArchivage();

    // ...
}


Stockage.cpp
(extrait) Définition des slots
// ...

bool Stockage::stockerFichier(QString chemin_fichier)
{
    // ... copie du fichier spécifié dans le lieu de stockage
}

bool Stockage::supprimerFichier(QString chemin_fichier)
{
    // ... suppression du fichier spécifié
}

void finArchivage()
{
    // ... afficher le message "Fin de l'archivage"
}

// ...


Connexion

Un signal peut être connecté :

  • à plusieurs slots : la méthode slot de l'objet est appelée quand le signal est émis ;
  • à plusieurs autres signaux : l’émission du signal provoque également l'émission de ces autres signaux.

Un slot peut recevoir plusieurs signaux, c'est à dire qu'on peut connecter le même slot à plusieurs signaux.

La connexion entre un signal et un slot s'effectue en appelant la méthode statique connect de la classe QObject. Cette méthode possède 5 paramètres :

sender
L'objet source du signal ;
signal
Le signal émis par l'objet source;
receiver
L'objet receveur du signal ;
slot
Le slot du receveur connecté au signal ;
type
(optionnel) Type de connexion (automatique par défaut).

Seuls les 4 premiers sont obligatoires.

Exemple utilisant les classes Téléchargement et Stockage vues auparavant :

Main.cpp
(extrait) Connextion entre signaux et slots
// ...

Telechargement *telechargePage = new Telechargement( /*...*/ );
Stockage *stockageCleUsb = new Stockage( /*...*/ );

QtObject::connect(
    telechargePage, SIGNAL(fichierRecu(QString)),   // signal fichierRecu émis par telechargePage
    stockageCleUsb, SLOT(stockerFichier(QString))   // connecté au slot stockerFichier de stockageCleUsb
    );

// ...


Si on connecte plusieurs slots à un signal, il seront invoqués séquentiellement dans l'ordre où ils sont connectés. Par exemple, pour successivement copier puis supprimer le fichier et enfin afficher un message quand le téléchargement est terminé :

Main.cpp
(extrait) Connextion entre signaux et slots
// ...

QtObject::connect(
    telechargePage, SIGNAL(fichierRecu(QString)),
    stockageCleUsb, SLOT(stockerFichier(QString))
    );
QtObject::connect(
    telechargePage, SIGNAL(fichierRecu(QString)),
    stockageCleUsb, SLOT(supprimerFichier(QString))
    );
QtObject::connect(
    telechargePage, SIGNAL(fichierRecu(QString)),
    stockageCleUsb, SLOT(finArchivage())
    );

// ...


Le slot doit posséder une signature compatible avec le signal auquel il est connecté. C'est-à-dire :

  • Il doit posséder le même nombre de paramètre ou moins que le signal. Les paramètres manquants seront ignorés (voir l'exemple précédent pour le slot finArchivage() connecté au signal fichierRecu(QString)).
  • Ces paramètres doivent être du même type.
  • Un signal retourne toujours void.
  • Un slot peut retourner une valeur, cependant celle-ci est ignorée par le signal. Elle ne sert que si le slot est invoqué directement, comme n'importe quel autre méthode de la classe.

Utilisation

Maintenant, on peut donc découvrir l'exemple qui sera sûrement un des plus utilisés.

La classe QPushButton a un signal clicked(), qui s'active quand l'utilisateur clique sur le bouton. On le connecte donc au slot quit() de l'objet qApp, votre application.

Découverte de connect()

connect() est une méthode statique. On écrit donc :

QObject::connect();

La méthode connect prend 4 arguments :

  • Un pointeur vers l'objet qui émet le signal
  • Le nom du signal que l'on souhaite envoyer.
  • 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.

Exercice : la fenêtre se ferme après avoir cliqué sur un bouton

Lançons-nous dans la création du code de mafenetre.cpp.

//main.cpp
#include <QApplication>
#include "MaFenetre.h"
 
 
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    MaFenetre fenetre;
    fenetre.show();
 
    return app.exec();
}

<syntaxhighlightlang="cpp-qt"> //MaFenetre.h

  1. ifndef DEF_MAFENETRE
  2. define DEF_MAFENETRE
  1. include <QApplication>
  2. include <QWidget>
  3. include <QPushButton>

class MaFenetre : public QWidget // On hérite de QWidget (IMPORTANT) {

   public:
   MaFenetre();

   private:
   QPushButton *m_bouton; 

};

  1. endif

</syntaxhighlight>

//MaFenetre.cpp
#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()));
}

Exécutons la commande qmake, puis testons notre code. Une fenêtre s'ouvre.

Rien de bien extraordinaire à première vue. Sauf que... si vous cliquez sur le bouton "Quitter", le programme s'arrête !

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".

Voici le code:

//MaFenetre.h
#ifndef DEF_MAFENETRE
#define DEF_MAFENETRE
 
#include <QApplication>
#include <QWidget>
#include <QPushButton>
 
class MaFenetre : public QWidget // On hérite de QWidget (IMPORTANT)
{
    public:
    MaFenetre();
 
    private:
    QPushButton *m_quitter; 
    QPushButton *m_aPropos; 

};
 
#endif
#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()));
}

On compile maintenant, et le bouton "Quitter" ferme toujours l'application. Quant à "À propos", il provoque l'ouverture de la fenêtre "À propos de Qt".

Dans ce code, nous sommes dans la classe MaFenetre, qui hérite de QWidget, et donc de QObject. Comme connect() appartient à QObject, dans ce cas, vous pouvez le supprimer avec les ::.

Paramétrage des signaux et des slots

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.

Maintenant, comment faire un slider, à l'aide d'un QSlider qui quand la valeur change fait changer la valeur d'un afficheur LCD (QLCDNumber) ? Eh bien, tout simplement, il y a des paramètres : c'est-à-dire que le signal est valueChanged(int) et que le slot setDigitalCounter(int) reçoit le nombre envoyé par le slider. En fait, cela est simple, car on ne change vraiment pas beaucoup le code :

QSlider *m_slider = new QSlider();
QLCDNumber *m_lcd = new QLCDNumber();
connect(m_slider, SIGNAL(valueChanged(int)), m_lcd, SLOT(setDigitalCounter(int)));

On remarque donc qu'il suffit de seulement mettre le type de valeur et non la valeur elle-même, car elle varie en fonction du choix de l'utilisateur.

En travauxlink={{{link}}}

Cette page est en travaux. Tant que cet avis n'aura pas disparu, veuillez en considérer le plan et le contenu encore incomplets, temporaires et sujets à caution. Si vous souhaitez participer, il vous est recommandé de consulter sa page de discussion au préalable, où des informations peuvent être données sur l'avancement des travaux.