Qt multi-thread avec interface graphique

Je ne peux pas produire un exemple très simple pour commencer avec Qt multi-thread. J’ai lu beaucoup de messages et de tutoriels mais cela ne fonctionne toujours pas.

Objectif

Avoir un travailleur de fond indépendant de l’interface graphique. Oh wow…

Ce que j’ai fait

Un exemple simple:

  • créer la classe Engine
  • qui montre un QMainWindow
  • et démarre un QTimer qui imprime un nombre

Mais si vous cliquez sur la barre de titre de l’interface graphique tout en maintenant le bouton gauche de la souris enfoncé (c.-à-d. Sur le bouton de réduction), le compteur s’arrête ! Même s’il a été créé dans un environnement non graphique et déplacé dans un autre thread!

Pourquoi?

main.cpp

 #include "engine.h" #include  int main(int argc, char *argv[]) { QApplication a(argc, argv); Engine e; return a.exec(); } 

moteur.h

 #ifndef ENGINE_H #define ENGINE_H #include  #include  #include  #include "mainwindow.h" class Engine : public QObject { Q_OBJECT public: explicit Engine(QObject *parent = 0); private: MainWindow mainWindow; QThread *thread; QTimer *timer; private slots: void foo(); }; #endif // ENGINE_H 

moteur.c

 #include "engine.h" #include  Engine::Engine(QObject *parent) : QObject(parent) { thread = new QThread(this); timer = new QTimer(); timer->setInterval(100); connect(timer, &QTimer::timeout, this, &Engine::foo); connect(thread, &QThread::started, timer, static_cast(&QTimer::start)); timer->moveToThread(thread); thread->start(); mainWindow.show(); } void Engine::foo() { static int i; qDebug() << ++i; } 

QMainWindow ne contient aucun code.

Fondamentalement, Qt a un thread qui traite l’interface graphique (généralement le thread principal). Tous les objects spécifiques à ce fil seront bloqués par l’interface graphique. Vous devez conserver l’interface graphique en dehors de vos partenaires en interaction.

Pour être plus spécifique, votre object Engine réside dans le thread graphique / principal. Même si votre timer est envoyée à un thread de travail, ses signaux sont envoyés au slot foo() dans le thread principal.

Vous devez désactiver Engine et la fenêtre principale pour que Engine puisse résider dans son propre thread et traiter les signaux lorsque l’interface graphique est bloquée.

On dirait que vous avez déplacé une instance de QTimer dans votre thread personnalisé, mais que vous n’avez pas déplacé une instance de moteur dans ce thread; par conséquent, le foo slot de la classe Engine sera exécuté dans le thread principal.

Je peux vous suggérer d’utiliser une instance supplémentaire de la classe dérivée de QObject au sein de la classe Engine et de la déplacer vers votre nouveau thread dans le constructeur du moteur.

Avec les modifications suivantes, la solution fonctionne correctement, même avec le bouton de réduction activé (j’ai commenté des endroits où j’ai ajouté ou modifié quoi que ce soit. J’ai également ajouté la nouvelle classe dérivée de QObject, EngineWorker ):

Moteur.h

 #ifndef ENGINE_H #define ENGINE_H #include  #include  #include  #include "mainwindow.h" #include "engineworker.h" class Engine : public QObject { Q_OBJECT public: explicit Engine(QObject *parent = 0); private: MainWindow mainWindow; QThread *thread; QTimer *timer; //additional QObject-derived class EngineWorker *worker; private slots: void foo(); }; #endif // ENGINE_H 

Engine.cpp

 #include "engine.h" #include  Engine::Engine(QObject *parent) : QObject(parent) { thread = new QThread(this); timer = new QTimer(); //Creating instance of Engine worker worker = new EngineWorker(); timer->setInterval(100); //Connecting Engine worker' foo slot to timer connect(timer, &QTimer::timeout, worker, &EngineWorker::foo); connect(thread, &QThread::started, timer, static_cast(&QTimer::start)); timer->moveToThread(thread); //Moving worker to custom thread worker->moveToThread(thread); thread->start(); mainWindow.show(); } void Engine::foo() { static int i; qDebug() << ++i; } 

EngineWorker.h

 #ifndef ENGINEWORKER_H #define ENGINEWORKER_H #include  #include  class EngineWorker : public QObject { Q_OBJECT public: explicit EngineWorker(QObject *parent = 0); signals: public slots: void foo(); }; #endif // ENGINEWORKER_H 

EngineWorker.cpp

 #include "engineworker.h" EngineWorker::EngineWorker(QObject *parent) : QObject(parent) { } //foo slot of EngineWorker class void EngineWorker::foo() { static int j; qDebug() <<"Worker: "<< ++j; }