web-dev-qa-db-fra.com

Comment émettre un signal cross-thread dans Qt?

La documentation de Qt indique que les signaux et les emplacements peuvent être direct, queued et auto.

Il a également déclaré que si l'objet qui possède le slot `` vit '' dans un thread différent de l'objet qui possède le signal, émettre un tel signal sera comme publier un message - le signal émis reviendra instantanément et la méthode du slot sera appelée dans la boucle d'événements du thread cible.

Malheureusement, la documentation ne précise pas que "vies" signifie et aucun exemple n'est disponible. J'ai essayé le code suivant:

main.h:

class CThread1 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        msleep( 200 );
        std::cout << "thread 1 started" << std::endl;
        MySignal();
        exec();
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

main.cpp:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & oThread2, SLOT( MySlot() ) );
    oThread1.start();
    oThread2.start();
    oThread1.wait();
    oThread2.wait();
    return a.exec();
}

La sortie est:

thread 2 started
thread 1 started

MySlot() n'est jamais appelée :(. Qu'est-ce que je fais mal?

58
grigoryvp

Il y a pas mal de problèmes avec votre code:

  • comme l'a dit Evan, le mot clé emit est manquant
  • tous vos objets vivent dans le thread principal, seul le code des méthodes d'exécution vit dans d'autres threads, ce qui signifie que l'emplacement MySlot serait appelé dans le thread principal et je ne suis pas sûr que ce soit ce que vous voulez
  • votre slot ne sera jamais appelé car la boucle d'évènement principale ne sera jamais lancée: vos deux appels à wait () n'expireront que très longtemps (et vous tuerez probablement votre application avant que cela n'arrive) et je ne pense pas c'est ce que vous voulez non plus, de toute façon ils n'ont vraiment aucune utilité dans votre code.

Ce code fonctionnerait très probablement (même si je ne l'ai pas testé) et je pense qu'il fait ce que vous voulez qu'il fasse:

class MyObject : public QObject
{
    Q_OBJECT
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

class CThread1 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 1 started" << std::endl;
        int i = 0;
        while(1)
        {
           msleep( 200 );
           i++;
           if(i==1000)
              emit MySignal();
        }
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    MyObject myObject;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & myObject, SLOT( MySlot() ) );
    oThread2.start();
    myObject.moveToThread(&oThread2)
    oThread1.start();
    return a.exec();
}

Maintenant, MyObject vivra dans thread2 (grâce à moveToThread).

MySignal devrait être envoyé depuis le thread1 (je ne suis pas sûr de celui-là, il pourrait être envoyé depuis le thread principal, cela n'a pas vraiment d'importance).

Aucune boucle d'événement n'est nécessaire dans thread1 car l'émission d'un signal n'a pas besoin d'une boucle d'événement. Une boucle d'événement est nécessaire dans thread2 (lancé par exec ()) pour recevoir le signal.

MySlot sera appelé dans thread2.

46
Aiua

Ne pas sous-classer QThread pour Qt 4.4+

Bien que la réponse d'Aiua soit bonne, je tiens à souligner certains problèmes avec QThread et Qt 4.6 ou 4.7.

Cet article le résume: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

Manque de documentation de la part de Qt

Malheureusement, le problème provient d'un manque de mises à jour de la documentation. Avant Qt 4.4, QThread n'avait pas d'implémentation run () par défaut, ce qui signifiait que vous deviez sous-classer QThread pour l'utiliser.

Si vous utilisez Qt 4.6 ou 4.7, vous devriez certainement pas sous-classe QThread.

Utilisez moveToThread

La clé pour obtenir des emplacements à exécuter dans un thread de travail est d'utiliser la méthode moveToThread comme l'a souligné Aiua.

36
Will Bickford

vous devez émettre le signal pour démarrer votre fonction de thread comme

emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);

vous pouvez ajouter plusieurs arguments dans ce signal

0
kuake56