IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

FAQ Qt 5 et erreurs courantes

FAQ Qt 5 et erreurs courantesConsultez toutes les FAQ

Nombre d'auteurs : 1, nombre de questions : 16, dernière mise à jour : 6 février 2022 

 
OuvrirSommaireSignaux/slots
précédentsommairesuivant
 
 

Lorsque vous connectez un signal à un slot, les paramètres d'entrée du slot sont obtenus à partir de ceux émis par le signal. Par exemple depuis le signal QLineEdit::textChanged(const QString &text) vers un slot MonWidget::onTextChanged(const QString &text)

 
Sélectionnez
connect(monLineEdit, &QLineEdit::textChanged,
        this,        &MonWidget::onTextChanged);
// ou ancienne forme :
connect(monLineEdit, SIGNAL(textChanged(const QString &)),
        this,        SLOT(onTextChanged(const QString &)));

Lorsque le signal est émis avec la ligne emit textChanged(/* le nouveau texte */); au sein de la classe QLineEdit, notre slot onTextChanged() est exécuté avec en entrée le nouveau texte.

Autant il est possible de connecter un signal avec un nombre de paramètres supérieur ou égal au slot : le slot recevant les paramètres correspondants, autant l'inverse n'est pas possible. Si par exemple vous voulez suite à l'appui sur un certain bouton (signal QPushButton::clicked() sans paramètre) exécuter ce même slot MonWidget::onTextChanged(const QString &text) possédant un paramètre, le signal sera incapable de fournir ce paramètre et vous obtiendrez une erreur (à la compilation pour la première forme « error: static assertion failed: Signal and slot arguments are not compatible. » ou durant l'exécution pour l'ancienne « QObject::connect: Incompatible sender/receiver arguments »).

Lorsque vous connaissez à l'avance le paramètre à passer au moment où vous effectuez la connexion, ceci ne peut PAS se faire en remplaçant la partie const QString & par votre string :

 
Sélectionnez
connect(monLineEdit, SIGNAL(textChanged(const QString &)),
        this,        SLOT(onTextChanged("Texte")));

ou

 
Sélectionnez
connect(monLineEdit, SIGNAL(textChanged(const QString &)),
        label,       SLOT(setText("Texte")));

remplacer ainsi un type par une variable ou expression n'a pas grand sens.

Il vous faut :

  • soit utiliser la classe QSignalMapper (C++03) comme indiqué dans ce sujet ou ce cours, ou plus simple une fonction lambda (depuis Qt 5, C++11) :

    C++03 Qt 5, C++11
     
    Sélectionnez
    QSignalMapper * signalMapper = new QSignalMapper(this);
    connect(button,       SIGNAL(clicked()), 
            signalMapper, SLOT(map()));
    
    connect(signalMapper, SIGNAL(mapped(QString const &)), 
            this,         SLOT(onTextChanged(QString const &)));
    connect(signalMapper, SIGNAL(mapped(QString const &)), 
            label,        SLOT(setText(QString const &)));
    
    signalMapper->setMapping(button, "Texte");
     
    Sélectionnez
    connect(button,   &QPushButton::clicked,
            this,     [this]() { onTextChanged("Texte"); });
     
    Sélectionnez
    connect(button,   &QPushButton::clicked,
            label,    [this]() { label->setText("Texte"); });

    (Nous devons capturer this, pour sa fonction membre (implicitement this->)onTextChanged et son membre (implicitement this->)label afin de pouvoir les utiliser au sein de la fonction lambda.)

    Notez qu'il est préférable, notamment si les objets this et label peuvent être détruits avant le bouton, d'ajouter le 3e paramètre de contexte ou d'utiliser par dessus la classe QPointer et avant de les utiliser dans la fonction lambda, de vérifier s'ils n'ont pas été détruits entre la capture et l'exécution (cf. la note de fin).

  • soit connecter un slot intermédiaire sans paramètre dans lequel on retrouvera la ligne label->setText("Texte").

Lorsque vous capturez une référence, un pointeur nu ou this dans une fonction lambda, prêtez une attention particulière à leur durée de vie par rapport au sender. Si l'objet est détruit et qu'un nouveau signal est émis, leur utilisation au sein de la fonction lambda entraînera un comportement indéfiniQu'est-ce qu'un « comportement indéfini » (Undefined Behaviour / « UB ») ? (cf. dangling pointer/referenceQu'est-ce qu'un « dangling pointer », une « dangling reference » ?). En effet, une connexion normale est automatiquement détruite si le sender ou le receiver sont détruits, mais lors d'une connexion sans contexte avec une fonction lambda, seule la destruction du sender supprime la connexion.

Pour palier à cela, vous pouvez comme montré ci-dessus ajouter un contexte grâce à cette surcharge de la fonction connect() qui entraînera la suppression de la connexion lors de la destruction l'objet. Je vous conseille aussi fortement d'utiliser la classe QPointer qui permet d'observer à la manière d'un pointeur intelligent la durée de vie d'un objet dont la classe hérite de QObject.

Vous pouvez consulter à ce sujet l'item 31 d'Effective Modern C++ de Scott Meyers ainsi que le billet Use carefully lambda function with Qt's connections de Guillaume Belz.

À voir également

Créé le 13 décembre 2017  par Winjerome

Lorsque votre signal ou slot possède plusieurs surcharges, l'écriture &VotreClasse::votreSlot devient insuffisante. Votre compilateur n'est pas capable de déterminer la surcharge que vous souhaitez utiliser, et vous donne l'erreur :

 
Sélectionnez
no matching function for call to 'QObject::connect(QComboBox*&, <unresolved overloaded function type> […]

Il vous faut effectuer un cast à l'aide de static_cast qui explicite la signature de la fonction sous la forme :

 
Sélectionnez
static_cast(void (VotreClasse::*)(types des paramètres)(&VotreClasse::votreSlot))

Voici un exemple pour la fonction membre currentIndexChanged() de la classe QComboBox qui possède les deux surcharges suivantes :

 
Sélectionnez
QComboBox * comboBox = new QComboBox;

// utilisation de : void currentIndexChanged(int index)
connect(comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
        /* ... */);

// utilisation de : void currentIndexChanged(const QString & text)
connect(comboBox, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged),
        /* ... */);

Pour alléger (un peu) la syntaxe, il existe depuis la version Qt 5.7 la variable template qOverload() (ainsi que qConstOverload() et qNonConstOverload() pour des surcharges constantes et non constantes) :

 
Sélectionnez
class VotreClasse {
    void fonctionSurchargee();        // (1)
    void fonctionSurchargee(QString); // (2)
    void fonctionSurchargee(int, QString);       // (3)
    void fonctionSurchargee(int, QString) const; // (4)
};

Ces variables template nécessitant C++14, vous avez en C++11 les classes équivalentes :

C++14 C++11
 
Sélectionnez
qOverload<>(&VotreClasse:fonctionSurchargee)                     // (1)
qOverload<QString>(&VotreClasse:fonctionSurchargee)              // (2)
qNonConstOverload<int, QString>(&VotreClasse:fonctionSurchargee) // (3)
qConstOverload<int, QString>(&VotreClasse:fonctionSurchargee)    // (4)
 
Sélectionnez
QOverload<>::of(&VotreClasse:fonctionSurchargee)                     // (1)
QOverload<QString>::of(&VotreClasse:fonctionSurchargee)              // (2)
QNonConstOverload<int, QString>::of(&VotreClasse:fonctionSurchargee) // (3)
QConstOverload<int, QString>::of(&VotreClasse:fonctionSurchargee)    // (4)

L'exemple précédent devient alors :

 
Sélectionnez
// utilisation de : void currentIndexChanged(int index)
connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
        /* ... */);

// utilisation de : void currentIndexChanged(const QString & text)
connect(comboBox, qOverload<const QString &>(&QComboBox::currentIndexChanged),
        /* ... */);

À voir également

Créé le 13 décembre 2017  par Winjerome
précédentsommairesuivant
 
 

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 © 2020 Winjerome. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.