web-dev-qa-db-fra.com

Comment déconnecter temporairement un signal avec un slot dans Qt?

Je connecte un slot avec un signal. Mais maintenant, je veux les déconnecter temporairement.

Voici une partie de ma déclaration de classe:

class frmMain : public QWidget
{
    ...
private:
    QTimer *myReadTimer;
    ...
private slots:
    void on_btnDownload_clicked();
    ...
};

Dans le constructeur de frmMain, je connecte myReadTimer avec un slot pour que ReadMyCom soit appelé toutes les 5 secondes:

myReadTimer=new QTimer(this);
myReadTimer->setInterval(5000);
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));

Mais, dans la fente on_btnDownload_clicked. Je ne veux pas que myReadTimer émette un signal dans on_btnDownload_clicked portée. Je veux donc les déconnecter au début de on_btnDownload_clicked et les reconnecter à la fin. Comme ça:

void frmMain::on_btnDownload_clicked()
{
    //some method to disconnect the slot & singal

    ...//the code that I want myReadTimer to leave me alone

    //some method to reconnect the slot & singal
}

J'ai cherché dans Stackoverflow et j'ai obtenu une réponse comme appeler le destructeur QObject. Mais je ne sais pas comment l'utiliser.

J'ai également essayé d'utiliser disconnect, comme:

QMetaObject::Connection myConnect;
myConnect=connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
...
disconnect(& myConnect);

Mais ça ne marche toujours pas. Alors, quelqu'un pourrait-il m'aider à faire cela?

19
ctxrr

Il y a une fonction très agréable dans QObject qui est utile de temps en temps: QObject::blockSignals()

Voici une classe de feu et d'oublie très simple qui fera ce que vous voulez. Je ne prends aucun crédit pour son design, je l'ai trouvé sur Internet quelque part il y a longtemps. Attention cependant, cela bloquera tous les signaux vers tous les objets. Si ce n'est pas ce que vous voulez, vous pouvez modifier la classe en fonction de vos besoins.

class SignalBlocker{
public:
    SignalBlocker(QObject *o): object(o), alreadyBlocked(object->signalsBlocked()){
        if (!alreadyBlocked){
            object->blockSignals(true);
        }
    }
    ~SignalBlocker() {
        if (!alreadyBlocked){
            object->blockSignals(false);
        }
    }

private:
    QObject *object;
    bool alreadyBlocked;
};

L'utilisation, dans votre cas, devient triviale

void frmMain::on_btnDownload_clicked()
{
    SignalBlocker timerSignalBlocker(myReadTimer);

    ...//the code that I want myReadTimer to leave me alone

    // signals automatically unblocked when the function exits
}

MISE À JOUR:

Je vois qu'à partir de Qt 5.3, une classe très similaire a été officiellement ajoutée à l'API. Il fait un travail similaire à celui ci-dessus avec un ensemble de fonctionnalités légèrement plus grand. Je vous suggère d'utiliser à la place la classe officielle QSignalBlocker afin de garder votre base de code à jour avec toutes les modifications de l'API.

Cependant, l'utilisation reste exactement la même.

19
RobbieE

Syntaxe de déconnexion/reconnexion

Il existe de nombreuses façons d'appeler déconnecter, selon exactement ce que vous voulez déconnecter. Voir la page de documentation de QObject pour une explication de leur fonctionnement.

Voici un exemple utilisant 0 pour signifier "déconnecter tous les emplacements".

void frmMain::on_btnDownload_clicked()
{
    // disconnect everything connected to myReadTimer's timeout
    disconnect(myReadTimer, SIGNAL(timeout()), 0, 0);    

    ...//the code that I want myReadTimer to leave me alone

    // restore the connection
    connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
}

Ou vous pouvez spécifier la paire signal-slot exacte à déconnecter en copiant votre syntaxe de connexion, comme ceci:

disconnect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));

Arrêt de la minuterie

Puisque vous travaillez avec une minuterie, cela peut être plus simple:

void frmMain::on_btnDownload_clicked()
{
    // stop the timer (so you won't get any timeout signals)
    myReadTimer->stop();    

    ...//the code that I want myReadTimer to leave me alone

    // restart the timer (using whatever interval was set previously)
    myReadTimer->start();
}

Différences par rapport à votre approche d'origine:

  • Puisque vous arrêtez et redémarrez le minuteur, la prochaine fois qu'il se déclenchera sera interval une fois votre fonction de slot terminée.

Devez-vous faire quelque chose de spécial?

Dans une application Qt à un seul thread, si vous gérez déjà un signal, un autre signal ne "sautera pas au milieu" de ce code. Au lieu de cela, il sera mis en file d'attente pour être traité immédiatement après le retour de l'emplacement actuel.

Donc, vous n'avez peut-être pas du tout besoin d'arrêter ou de déconnecter votre minuterie.

Différences par rapport à votre approche d'origine:

  • Si on_btnDownload_clicked prend un certain temps à s'exécuter, vous pouvez avoir plusieurs événements ReadMyCom mis en file d'attente après on_btnDownload_clicked termine. (Notez qu'à ce stade, vous auriez une opération qui "verrouille" votre interface graphique pendant un certain temps de toute façon; il peut être plus judicieux de refactoriser la fonction ou de lui donner son propre thread.)
10
Alex P