Newsletter Developpez.com

Inscrivez-vous gratuitement au Club pour recevoir
la newsletter hebdomadaire des développeurs et IT pro

FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 26, nombre de questions : 298, dernière mise à jour : 6 mai 2017 

 
OuvrirSommaireModulesQt CoreSignaux et slots

Les signaux et slots sont l'implémentation par Nokia du pattern observer. Ce pattern permet de prévenir les classes voulant observer un événement.

Pour utiliser ce système, il faut hériter de QObject, soit directement, soit à l'aide d'un des objets dérivés de QObject. Ensuite, il faut définir la macro Q_OBJECT qui servira à qmake puis à moc pour générer le code nécessaire à l'utilisation de ces fonctions.

Pour déclarer des signaux, il suffit d'indiquer :

 
Sélectionnez
signals:
     void monSignal();

Pour que moc génère le code qui va bien et pour que le signal puisse être utilisé. On appellera un signal par :

 
Sélectionnez
emit monSignal();

Ces signaux peuvent être connectés à d'autres signaux ou à des slots. Ces slots sont des fonctions du développeur qui seront appelées dès que possible.

 
Sélectionnez
public slots:
     void monSlot();

Une fois les slots définis, il suffit de connecter les signaux et les slots entre eux. Comme il s'agit de connexion directe, il n'est pas possible de connecter un signal sans paramètre à un slot avec un paramètre. Pour cela, il faut utiliser l'intermédiaire QSignalMapper.

 
Sélectionnez
connect(this, SIGNAL(monSignal(), somethingElse, SLOT(monSlot()));

On constatera aussi que signaux et slots ne peuvent pas retourner de valeur, pour l'instant.

Mis à jour le 7 mai 2012  par Matthieu Brucher

Lien : Les signaux et slots
Lien : Tutoriel sur les signaux et slots de Qt 4par Miles

Dans la question Les signaux et autres slots nous avons vu comment s'interface une connexion entre signaux et slots simples, c'est-à-dire sans transmission de valeur. Or, les signaux et slots ont la capacité de se transmettre des données par le biais de leurs arguments.

Prenons l'exemple de la classe QLineEdit de Qt.

Son signal textChanged(const QString&) permet de récupérer le texte présent dans le QLineEdit au moment de l'émission du signal. De même, son slot setText(constQString&) permet de redéfinir le texte contenu dans le QLineEdit à l'aide d'un objet QString.

Rappel : il est possible de connecter un signal avec un slot, mais on peut également connecter un signal à un autre signal. Pour créer une connexion signal/slot (ou signal/signal) qui permette la transmission de valeurs, il suffit d'écrire la signature complète de chaque signal ou slot dans la fonction connect().
Autrement dit, il faut indiquer le nom des signaux/slots en question, ainsi que les types des arguments qu'ils prennent en paramètres.

Exemple de signature complète
Sélectionnez
monSignal(QString,int) //signature d'un signal
monSlot(QString,int) //signature d'un slot
connect(objet1,SIGNAL(monSignal(QString,int)),objet2,SLOT(monSlot(QString,int)))

Attention, car il faut prendre certaines précautions. Dans une fonction connect(), les types des arguments des deux fonctions doivent être compatibles et placés dans le même ordre. Des arguments sont compatibles si le même objet est en jeu. Par exemple, const QString & est compatible avec QString. Par contre, QString* et QString ne le sont pas. Voici un exemple de connexion avec deux types compatibles.

 
Sélectionnez
connect(this, SIGNAL(monSignal(QString, int)), somethingElse, SLOT(monSlot(const QString&, int)));

Cependant, le signal ou le slot qui est situé dans la partie droite de la fonction connect() peut avoir un nombre d'arguments inférieur ou égal à celui du signal situé à gauche. Par exemple la connexion suivante est valide. L'argument int sera simplement ignoré.

 
Sélectionnez
connect(this,SIGNAL(monSignal(QString,int)),somethingElse,SLOT(monSlot(QString)))
Mis à jour le 7 mai 2012  par Nykoo

Cette question nécessite d'avoir compris la création de signaux/slots simples (sans arguments) décrite dans la question Les signaux et autres slots ?

Pour créer un signal ou un slot avec des arguments, il suffit d'ajouter les noms des types dans le prototype.

 
Sélectionnez
class myClass : public xxx
{
    Q_OBJECT
    ...
signals:
    void monSignal(type1,type2)
public slots:
    void monSlot(type1,type2);
    ...
}

Ainsi, pour émettre son signal il suffit d'utiliser le mot clé emit.

Emission d'un signal
Sélectionnez
void fonction()
{
    nom_type t;
    emit monSignal(t);
}

La connexion se fera avec un signal ou un slot qui prend un argument de type nom_type.

Mis à jour le 7 juillet 2009  par Nykoo

Sous Qt, les connexions entre les signaux et slots peuvent être mises en place, soit manuellement, soit automatiquement, en utilisant la capacité qu'a QMetaObject d'établir des liens entre ces derniers.

Cette partie ne concerne pas les connexions manuelles : elle traite de la méthode automatique.

Bien qu'il soit plutôt aisé d'implémenter un slot et de le connecter dans le constructeur, nous pouvons tout aussi bien utiliser l'outil d'autoconnexion de QMetaObject pour connecter le signal clicked() de myButton à un slot dans notre classe, ou pour connecter les signaux et les slots clicked() :

 
Sélectionnez
QMetaObject::connectSlotsByName(QObject *object);

Cette fonction du QMetaObject de Qt connecte automatiquement tout slot qui respecte la convention on_Nomobjet_Nomsignal() au signal Nomsignal() correspondant de l'objet Nomobjet. Deux conditions doivent être remplies.

  • L'objet NomObjet doit être un enfant de l'objet passé en paramètre à la méthode connectSlotByName().
  • Les objets (parents et enfants) doivent être nommés avec la fonction setObjectName(constQString& name).
Exemple
CacherSélectionnez

Supposons, cette fois-ci, que nous créons notre fenêtre via Qt Designer et non plus en ligne de code. Nous possédons alors un fichier supplémentaire, à savoir : mainwindows.ui. Lors de la compilation, l'outil uic de Qt se charge de générer le code de la fonction setupUi() de notre fenêtre mainwindows.ui. Le code généré utilise alors de la même façon :

Il est donc possible d'utiliser, de la même façon, les autoconnexions sur une GUI codée à la main que sur une GUI créée à l'aide de Qt Designer.

Si deux enfants ont le même nom, l'auto-connect ne se fera que sur le premier enfant trouvé.

Si QMetaObject::connectSlotsByName est appelé plusieurs fois, les connections générées seront multiples. Lors de l'utilisation d'un *.ui, ne pas oublier que la fonction setupUi() appelle l'autoconnexion.

Mis à jour le 7 mai 2012  par mac&cheese

Téléchargement : Utilisation de autoconnect sans *.ui
Téléchargement : Utilisation de autoconnect avec *.ui

Qt propose de nombreuses classes avec de nombreux signaux déjà définis. Cependant, il peut parfois être intéressant d'ajouter de l'information à ces signaux afin de les paramétrer selon l'objet émetteur. On pourrait ainsi souhaiter que différents boutons réalisant une action identique, par exemple ouvrir une page Web lorsque l'on clique dessus, soient connectés à un même slot auquel on précise l'URL à utiliser. Cependant, le signal clicked() de la classe QPushButton ne transmet aucun paramètre, ce qui ne permet pas de spécifier l'URL.

Une première solution consiste alors à connecter le signal clicked() de chaque bouton à un slot différent qui se contentera d'appeler la fonction d'ouverture de la page Web avec l'URL correspondante. Cependant, comme il est nécessaire de créer un slot différent par bouton, cela rallonge inutilement la taille du code, surtout lorsqu'il y a un grand nombre de boutons.

Une autre solution est d'utiliser la classe QSignalMapper. Dans notre exemple, celle-ci va s'occuper d'appeler le slot ouvrant la page Web avec un paramètre configuré pour chacun des boutons (l'URL de la page). Nous avons donc d'un côté les signaux clicked() des différents QPushButton, et de l'autre un slot openUrl(const QString& url) et le QSignalMapper au milieu pour faire les correspondances.

Tout d'abord il faut créer un objet QSignalMapper, puis connecter les signaux des boutons à son slot map(). On définit alors, pour chacun des boutons, le paramètre à utiliser pour l'appel à openUrl() via setMapping().

 
Sélectionnez
mapper = new QSignalMapper();

// Bouton 1
connect(bouton1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(bouton1, "http://url1");

// Bouton 2
connect(bouton2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(bouton2, "http://url2");

// Autres boutons ?

Enfin, il suffit de connecter le signal mapped() de QSignalMapper à notre slot final. Ainsi quand un bouton émettra son signal, le slot openUrl() sera utilisé avec le paramètre correspondant au bouton.

 
Sélectionnez
connect(mapper, SIGNAL(mapped(const QString &)), this, SLOT(openUrl(const QString &)));

Note : QSignalMapper ne se limite pas à des paramètres de type QString. Il est également possible d'utiliser des entiers ou encore des QWidget* et des QObject*. Il faut dans ce cas utiliser le signal mapped() correspondant au type que l'on veut transmettre.

Mis à jour le 7 mai 2012  par Kinj1

Un slot n'est, finalement, rien de plus qu'une fonction normale à laquelle sont rattachées quelques informations liées au concept de métaobjet de Qt. En conséquence, il est tout à fait possible de les appeler comme une fonction, et ce, de façon inter-thread (avec les précautions qui s'imposent dans un tel contexte). Comme précisé dans Comment fonctionne QThread ?, l'utilisation correcte et sûre de slots dans un thread requiert l'exécution d'une boucle d'événements (eventloop).

On peut répertorier les méthodes d'appel dans deux catégories :

  • appel direct (à la façon d'une fonction normale) ;
  • appel par un signal (paradigme classique et récurrent lors de l'écriture d'une application utilisant Qt).

Cette dernière catégorie peut être elle-même divisée en deux autres catégories :

  • appel direct ;
  • appel placé en queue de la boucle d'événement du thread récepteur.

Dans le premier cas, le slot est exécuté dans le contexte du thread appelant.

Dans le second cas, le contexte d'exécution du thread dépend du mode de connexion fourni à QObject::connect :

  • directe (Qt::DirectConnection) : le slot est appelé immédiatement après l'émission du signal, et il est exécuté dans le thread contenant l'émission du signal ;
  • placé en queue (Qt::QueuedConnection) : le signal est traité comme un événement et le slot concerné est donc appelé lors du prochain passage dans la boucle d'événement du thread dans lequel le destinataire vit (c'est-à-dire le thread dans lequel il a été créé ou, si QObject::moveToThread() a été utilisé, dans le thread qui a été donné à cette fonction) ;
  • automatique (Qt::AutoConnection, paramètre par défaut): si le destinataire du signal vit dans le même thread, la connexion correspond à une connexion directe ; dans le cas contraire le comportement est celui d'une connexion en queue.
 
CacherSélectionnez

Cet exemple produira une sortie semblable à ceci.

 
Sélectionnez
MyThread::threadSlot(int), appel depuis le thread # 0xa28 : n= 4
MyThread::threadSlot(int), appel depuis le thread # 0xa24 : n= 1764
MyThread::threadSlot(int), appel depuis le thread # 0xa24 : n= 42 

Le slot qui nous intéresse est ici MyThread::threadSlot(int). Il est appelé trois fois dans ce programme :

  • dans void MyThread::run(), threadSlot() est exécuté dans le contexte du thread exécutant run(), car appelé directement ;
  • dans void Window::onCallThreadSlot(), threadSlot() est exécuté dans le contexte du thread principal, car appelé directement depuis celui-ci ;
  • dans void Window::onCallThreadSlotSig(), threadSlot() est exécuté dans le contexte du thread principal, car appelé indirectement par l'émission d'un signal à partir de ce thread.
Mis à jour le 7 mai 2012  par Denys Bulant

Dans un slot, la méthode sender() retourne le QObject qui a déclenché ce slot via un signal (peu importe lequel).

Prenons un exemple.

monSlot est un slot personnalisé de la classe MaClasse.

 
Sélectionnez
MaClasse::MaClasse()
{
    QPushButton * btn = new QPushButton("bouton");
    QObject::connect(btn, SIGNAL(clicked()), this, SLOT(monSlot()));
}

MaClasse::monSlot()
{
    QObject * emetteur = sender();
    // emetteur contient le QPushButton btn si on clique sur ce bouton
}

Entendu, mais que faire de ce QObject ? C'est le QPushButton que l'on voulait !

Pas de panique ! Pour accéder pleinement à l'émetteur, la prochaine étape est de le caster. Voyons comment faire ça proprement avec qobject_cast.

 
Sélectionnez
MaClasse::monSlot()
{
    QObject * emetteur = sender();

    // On caste le sender en ce que nous supposons qu'il soit
    QPushButton * emetteurCasted = qobject_cast<QPushButton*>(emetteur);

    // On teste la réussite du cast avant de procéder à un quelconque accès dessus !
    if(emetteurCasted) //emetteurCasted vaut 0 si le cast à échoué
    {
         // Suite du traitement
    }
}

Cette méthode ne fonctionne pas en multithread ! Si l'émetteur et le récepteur ne sont pas dans le même thread, sender() ne renvoie rien (0). Dans ce cas, vous devez utiliser QSignalMapper

Créé le 30 octobre 2009  par Niak74

Lien : Comment récupérer et utiliser l'objet déclencheur d'un slot avec QSignalMapper ?

Une alternative à la méthodesender() est l'utilisation d'un QSignalMapper. Cette classe permet d'associer un signal sans paramètre d'un objet à un autre signal pourvu d'un paramètre défini. Ce paramètre peut être de type int, QString, QWidget* ou QObject*.

Voici un exemple simple d'utilisation, issu de la documentation Qt.

Dans le cas présent, on a une liste de boutons, contenant du texte, créée à partir d'une liste de QString. On souhaite lier l'action de presser un bouton (signal clicked()) à un signal qui contient le texte de ce bouton (signal clicked(QString)).

 
CacherSélectionnez
 
CacherSélectionnez

Voici comment fonctionne cet exemple.

On connecte le signal déclencheur souhaité de chaque objet avec le slot map() du QSignalMapper.

On indique au QSignalMapper des liens entre des objets et des paramètres via la méthode setMapping() (ici de type QString, mais on peut aussi le faire avec des int, QObject* ou QWidget*).

À chaque appel au slot map(), le QSignalMapper identifie l'entité qui déclenche le slot map() (sender()), cherche si cette entité est liée à un paramètre, et émet le signal mapped() avec le paramètre trouvé.

Créé le 30 octobre 2009  par Niak74

Lien : Comment récupérer et utiliser l'objet déclencheur d'un slot ?

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 - 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.