我正在寫一個演示應用程序來緩解我的QT學習曲線。我的目標是更新來自作爲數據生成器在後臺運行的線程的值。我編寫了QML,並使用QT標準數據綁定方法(即Q_Property)將C++成員綁定到它。目前該解決方案按預期工作,但希望確認這是否是實施相同的正確方法。使用多層信號從線程更新QML是否正確?
思想
- 在一個線程(類DemoData)生成數據
- 發射信號,以通知另一個類(類VitalData)
- 發光Q_PROPERTY信號(來自類VitalData)來更新UI
查詢
- 我應該生成數據並通知UI有關單個類中的更改並將該類實例發送到新線程嗎?在這種情況下,我可以使用單個信號來更新UI。
- 基於目前的設計是否會受到糟糕的性能影響,或者在最糟糕的情況下,由於快速信號時隙,UI部分可能會遺漏一些數據?
我的目標是保持數據生成器類的解耦。
最後的代碼
//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();
}
爲線程退出
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();
}
};
那麼最初我去定時器,轉移到線程詳細瞭解QT線程。我來自Win32/MFC/C#的背景,所以在QT中嘗試所有可能的東西。是的'getData'應該改成與setter相關的名字,將會改正。最後一個簡短的問題。在app退出時優雅地關閉線程的正確方法是什麼?我可以通過休息檢查將一秒鐘的睡眠分成最短的毫秒。正在嘗試連接'aboutToQuit()'插槽,但這並沒有幫助我。 – Anup
即使QThreads有他們自己的事件循環。我會更新相應的答案 – Felix
如果我正確理解了你的話,我需要把'while(!QThread :: currentThread() - > isInterruptionRequested())'趕上中斷並激發'aboutToQuit'信號的中斷。即'QObject :: connect(qApp,&QCoreApplication :: aboutToQuit,&thread,[&thread]()thread.requestInterruption(); thread.wait(1000); });' – Anup