J'écris une application de démonstration pour faciliter ma courbe d'apprentissage QT. Mon but est de mettre à jour les valeurs d'un thread qui s'exécute en arrière-plan en tant que générateur de données. J'ai écrit le QML et lié les membres C++ en utilisant l'approche de liaison de données standard QT, c'est-à-dire Q_Property. Actuellement, la solution fonctionne comme prévu mais a voulu confirmer si c'est la bonne façon d'implémenter la même chose.Est-ce la bonne façon de mettre à jour QML à partir du thread en utilisant des signaux multi-couche?
Idea
- Générer des données dans un filetage (classe DemoData)
- signaux émettant de notifier une autre classe (classe VitalData)
- signaux Emitting Q_PROPERTY (de classe VitalData) pour mettre à jour l'interface utilisateur
Recherche
- Dois-je générer des données et informer l'interface utilisateur des modifications apportées à une seule classe et envoyer cette instance de classe à un nouveau thread? Comme je peux utiliser un seul signal pour mettre à jour l'interface utilisateur dans ce cas.
- Selon la conception actuelle, est-ce que les performances seront médiocres ou, dans le pire des cas, certaines données peuvent être manquées à l'interface utilisateur en raison d'une fente de signal rapide?
Mon but est de garder la classe du générateur de données découplée.
Enfin, le code
//A data generator class - this can be altered by some other class if neccessary
class DemoData : public QObject
{
Q_OBJECT
int nextUpdateIndex = 0;
public slots:
void generateData()
{
int hrValIndex = 0, spo2ValIndex = 0, respValIndex = 0, co2ValIndex = 0;
while(true) {
switch(nextUpdateIndex) {
case 0:
emit valueUpdated(nextUpdateIndex, demoHRRates[hrValIndex]);
if(hrValIndex == ((sizeof demoHRRates)/(sizeof(int))) - 1)
hrValIndex = 0;
else
hrValIndex++;
nextUpdateIndex = 1;
break;
}
QThread::sleep(1);
}
}
signals:
//Signal to notify the UI about new value
void valueUpdated(int index, int data);
};
//Class to interact with QML UI layer. This class only hold properties and it's binding
class VitalData : public QObject
{
Q_OBJECT
Q_PROPERTY(int hrRate READ getHrRate NOTIFY hrRateChanged)
public:
int getHrRate() const {
return m_hrRate;
}
public slots:
void getData(int index, int value)
{
switch(index){
case 0:
m_hrRate = value;
emit hrRateChanged();
break;
}
}
signals:
//This signal actually notifies QML to update it value
void hrRateChanged();
};
int main()
{
QGuiApplication app(argc, argv);
//Data generator class is getting linked with UI data feeder class
VitalData med;
DemoData demo;
QObject::connect(&demo, SIGNAL(valueUpdated(int, int)), &med, SLOT(getData(int, int)));
//Standard way to launch QML view
QQuickView view;
view.rootContext()->setContextProperty("med", &med);
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
//Moving data generator to a background thread
QThread thread;
demo.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &demo, SLOT(generateData()));
thread.start();
return app.exec();
}
Nouveau code pour la sortie de fil
int main()
{
QThread thread;
demo.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &demo, SLOT(generateData()));
QObject::connect(qApp, &QCoreApplication::aboutToQuit, &thread, [&thread](){
thread.requestInterruption();
thread.wait();
});
thread.start();
}
class DemoData : public QObject
{
Q_OBJECT
public slots:
void generateData()
{
while(!QThread::currentThread()->isInterruptionRequested()) {
switch(nextUpdateIndex) {
case 0:
break;
}
QThread::msleep(200);
qDebug() << "Thread running..";
}
//This quit was necessary. Otherwise even with requestInterruption call thread was not closing though the above debug log stopped
QThread::currentThread()->quit();
}
};
Eh bien d'abord je suis allé pour la minuterie et décalée à enfiler pour en savoir plus sur les threads QT. Je viens d'un arrière-plan Win32/MFC/C#, essayant ainsi toutes les choses possibles dans QT. Oui 'getData' devrait être changé en un nom associé à setter, corrigera cela. Enfin une question rapide. Quelle est la bonne façon de fermer le fil gracieusement à la sortie de l'application? Je peux diviser le sommeil d'une seconde en milliseconde plus courte avec un contrôle de rupture. J'essayais de connecter le slot 'aboutToQuit()' mais cela ne m'a pas aidé. – Anup
Même QThreads possède sa propre boucle d'événements. Je vais mettre à jour la réponse en conséquence – Felix
Si je vous ai bien compris, je dois mettre 'while (! QThread :: currentThread() -> isInterruptionRequested())' pour intercepter l'interruption et déclencher l'interruption sur le signal 'aboutToQuit'. c'est-à-dire 'QObject :: connect (qApp, & QCoreApplication :: aboutToQuit, & thread, [& thread]() { thread.requestInterruption(); thread.wait (1000); });' – Anup