Multithreading dans les widgets Qt

Lors de l'exécution d'une application avec une interface fenêtrée, il est important de s'assurer qu'il n'y a pas de gel. Pour cela, les calculs complexes doivent être exécutés dans un thread séparé. Le concept d'une application multi-thread va bien avec l'approche des créneaux de signaux Qt, sans avoir besoin de redéfinir une méthode run ().

Idée principale. Dans une application multi-thread, les calculs sont effectués dans un thread séparé, à la fin un signal est émis, transmettant le résultat dans ses arguments. Un emplacement appartenant déjà à MainWindow sera appelé. Les résultats du calcul seront dans les arguments du slot et il ne sera pas difficile de les sortir.

En dessinant une analogie avec les microcontrôleurs, un signal est une transition le long d'un vecteur d'interruption et un slot est lui-même un gestionnaire d'interruption. Pour qu'une «interruption» se produise, le signal doit être émis: emit mysignalvoid (); alors Qt commencera à le chercher avec un «handler» (slot). Dans Qt, vous pouvez accrocher plusieurs emplacements sur un seul signal.

La figure suivante montre l'algorithme de l'application de démonstration. Le fil du traitement interroge chaque seconde selon la légende un périphérique USB HID. Ensuite, un signal d'émission est émis, qui traite un slot qui appartient déjà à MainWindow et qui a donc la possibilité de dessiner sur le formulaire.



Nous avons donc des classes qui peuvent utiliser l'approche signal-slot. Pour ce faire, leur déclaration utilise la macro Q_OBJECT.

class Worker : public QObject { Q_OBJECT //   -   public: QTimer *timerDeviceRead; //      GuiUpdateCallback Worker(); public slots: void updateElectropipData(); signals: void GuiUpdatePlease(uint8_t const *arrptr,size_t); }; class MainWindow : public QMainWindow //   GUI { Q_OBJECT public slots: void GuiUpdateCallback(uint8_t const *arrptr, size_t); private: Ui::MainWindow *ui; //       QThread *thread; 

Pour passer le résultat des calculs dans les arguments, il est nécessaire d' enregistrer les types utilisés pour les arguments. Vous pouvez le faire à différents endroits, mais généralement dans le constructeur de la classe, qui fonctionnera ensuite avec ces types:

 Worker::Worker(){ qRegisterMetaType<std::size_t>("size_t"); qRegisterMetaType<uint8_t const *>("uint8_t const *"); 


Ensuite, un temporisateur est créé dans le constructeur du thread de traitement. Chaque seconde, le slot de traitement updateUSBDataCallback sera appelé. Faites attention à la syntaxe de la connexion signal-slot dans Qt5.

  this->timerDeviceRead = new QTimer(); connect(Worker::timerDeviceRead, &QTimer::timeout, this, &Worker::updateUSBDataCallback); this->timerDeviceRead->start(); 

Ce qui suit est le corps de la fente du thread de traitement. Tout le code longuement pensé devrait être ici.

 void Worker::updateUSBDataCallback(){ size_t mysize = 65; uint8_t buf[65] = { "I like USB HID" }; emit GuiUpdatePlease(buf,mysize); //  //: for(int i =0;i<64;i++){ buf[i]=i+'0'; if (i+'0' > 250) i=0; } } 

Pour illustrer ici, immédiatement après l'émission du signal vers la fente MainWindow, le contenu de la matrice est effrontément modifié. Et puisque le tableau est passé par pointeur, une lecture sale est obtenue. Pour éviter cette situation, le signal provenant du thread de traitement doit être connecté à l'emplacement GuiUpdateCallback () d'une certaine manière:

 MainWindow::MainWindow{ connect(worker, &Worker::GuiUpdatePlease, this, &MainWindow::GuiUpdateCallback, Qt::BlockingQueuedConnection); 

Dans ce cas, en émettant un signal, le thread de traitement est bloqué jusqu'à la fin du slot GuiUpdateCallback ().

Si le long "uint8_t const *" vous confond dans le texte du programme, alors vous pouvez obtenir le synonyme TU8PTR:

 typedef uint8_t const * TU8PTR; 

Code source

Source: https://habr.com/ru/post/fr485324/


All Articles