您的線程不會更新MainWindow
,但它確實會在每次迭代時創建一個全新的QApplication
和MainWindow
。您的線程應該卡在QApplication::exec
之內,直到您退出應用程序(例如關閉窗口)。只有這樣你的線程循環才能取得進一步的進展。
通常,從主線程外部進行更新時必須非常小心,因爲通常GUI操作必須在主線程內執行。
想想使用QThread
,它已經有了它自己的事件循環,您可以使用它來使用相應的插槽通知/更新窗口。
沒有關於您實際嘗試實現什麼的進一步細節,不可能給您進一步的指示。至少,我建議您在主線程內創建您的QApplication
和MainWindow
(例如main
)。那麼這取決於你想要「更新」什麼。如果您需要處理一些數據,那麼您可以在第二個線程中執行此操作,並使用信號插槽將結果發送到您的MainWindow
實例。如果你需要在窗口上繪圖,那麼這個任務必須直接在主線程中完成,否則你可能會找到一種方法從你的線程中渲染到一個單獨的緩衝區(即QImage
),然後將這個緩衝區發送到主將其繪製到窗口中的線程。
我試圖描繪一下如何做這樣的事情。但是請注意,它既不完整也不可編譯,而僅僅是一個輪廓。
首先,你有你的MainWindow
並加上signal
,通知所有觀察者開始工作(一會兒就會變得清晰)。此外,您還可以添加slots
,只要其中一個值發生更改,就會調用slots
。在主線程中那些slots
運行(並且是MainWindow
的成員),因此可以更新窗口然而,他們需要:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// constructors and stuff
void startWorking()
{
emit startWorkers();
}
public slots:
void onModeChanged(Modes m)
{
// update your window with new mode
}
void onDistanceChanged(float distance)
{
// update your window with new distance
}
signals:
void startWorkers();
};
接下來,建立一個Worker
類,封裝了所有的「後臺工作」你喜歡做的(基本上是你的線程在原密碼一樣):
class Worker : public QObject
{
Q_OBJECT
public:
// constructors and stuff
public slots:
void doWork()
{
while(!done)
{
// do stuff ...
Modes m = // change mode
emit modeModified(m);
// do stuff ...
float distance = // compute distance
emit distanceModified(distance);
// do stuff ...
}
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
};
注意,那Worker
必須繼承QObject
和你doWork
方法必須是一個public slot
。此外,您還可以爲每個喜歡MainWindow
的值添加一個signal
。由於它是由Qt MOC(元對象編譯器)生成的,因此不需要實現它們。每當其中一個值發生變化時,只需emit
對應的signal
並傳遞新值。
最後,你把一切融合在一起:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
// create a worker object
Worker* worker = new Worker;
// connect signals and slots between worker and main window
QObject::connect(worker, &Worker::modeModified,
&window, &MainWindow::onModeChanged);
QObject::connect(worker, &Worker::distanceModified,
&window, &MainWindow::onDistanceChanged);
QObject::connect(&window, &MainWindow::startWorkers,
worker, &Worker::doWork);
// create a new thread
QThread* thread = new QThread;
// send worker to work inside this new thread
worker->moveToThread(thread);
thread->start();
// show window and start doing work
window.show();
window.startWorking();
// start main loop
int result = app.exec();
// join worker thread and perform cleanup
return result;
}
好吧,讓我們通過它去。首先,在主線程中創建您的QApplication
和MainWindow
。接下來,創建一個Worker
對象的實例(可以在此處創建多個實例)。然後您將worker
的信號添加到window
的插槽中,反之亦然。一旦這些連接建立起來,每當你有一個信號時,連接的時隙就會被Qt調用(並且傳送的值被傳送)。注意,這個連接可以跨越線程邊界。每當信號從與接收對象的線程不同的線程發出時,Qt將發送一條消息,在接收對象的線程中處理該消息。
然後,您告訴Qt,您希望您的worker
使用QObject::moveToThread
在另一個線程內生存。有關如何正確使用QThread
及其內部對象的詳細說明,請參見here。
其餘的就很簡單。 show
您的window
並開始處理。這裏可能有不同的方式。我只是在這裏調用startWorking
方法,然後emit
爲startWorkers
信號,它連接到doWork
的方法,使得doWork
將在另一個線程接收到該信號之後開始執行。
然後調用QApplication::exec
運行主線程的事件循環,所有這些信號都由Qt處理。一旦您的應用程序關閉(例如通過撥打quit
或關閉主窗口)exec
方法將返回並且您返回main
。請注意,您需要正確關閉線程(例如,通過發送一個停止while
循環的加法信號)並加入它。你也應該刪除所有分配的對象(worker
,thread
)。爲了簡化代碼示例,我在此省略了這一點。
回答你的問題
我有很多的功能,例如,updateClips和mavReceive應定期調用,相互獨立運行。我應該爲每個函數創建一個不同的Worker類,因爲每個函數都有不同的信號,並且每個函數都有一個QThread對象,對嗎?我不需要startTimer()了嗎?如果是的話,我怎麼能控制每個函數的調用間隔(使用與startTimer()
從註釋工作要做:
答案在很大程度上取決於究竟你的意思是「應該被稱爲定期「,誰應該給他們打電話?用戶?或者他們是否應該定期執行?
所以原則上,你可以在一個線程中有多個工作者,但是如果他們應該一直工作(在一個while循環中旋轉)它是沒有意義的,因爲一個正在運行並且所有其他的都被阻塞了。在這種情況下,每個worker都會有一個線程。
如果我理解正確,您有興趣定期更新某些內容(例如,每500ms)。在這種情況下,我強烈建議使用QTimer
。您可以設置一個時間間隔,然後啓動它。定時器將定期emit
timeout
信號,您可以連接到任何您想要執行的功能(更確切地說slot
)。
的Worker
的更新版本看起來是這樣的:
class Worker : public QObject
{
Q_OBJECT
public:
Worker()
{
QObject::connect(&modeTimer_, &QTimer::timeout,
this, &Worker::onModeTimerTimeout);
QObject::connect(&distanceTimer_, &QTimer::timeout,
this, &Worker::onDistanceTimerTimeout);
modeTimer_.start(500); // emit timeout() every 500ms
distanceTimer_.start(100); // emit timeout() every 100ms
}
public slots:
void onModeTimerTimeout()
{
// recompute mode
Modes m = // ...
emit modeModified(m);
}
void onDistanceTimerTimeout()
{
// recompute distance
float distance = // ...
emit distanceModified(distance);
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
private:
QTimer modeTimer_;
QTimer distanceTimer_;
};
通知,確立了在構造函數中的連接。只要其中一個定時器超時,就會調用連接的slot
。然後該槽可以計算它需要的任何值,然後使用與之前相同的signal
將結果發送回主線程中的MainWindow
。因此,正如你所看到的,你可以在一個Worker
(也就是一個線程)內有多個定時器/重新計算/更新信號。然而,實現的關鍵點是計算需要多長時間。如果它們花費很長時間(例如幾乎與區間一樣長),那麼您應該考慮使用多個線程來加速計算(意思是:在每個線程中執行一次計算)。當我慢慢地看到你想要獲得的更清晰的圖像時,我想知道是否僅僅是關於這些定期更新,你在你的問題中「濫用」了線索。如果確實如此,那麼你根本不需要那個線程和Worker
。然後只需將定時器添加到MainWindow
,並將它們的timeout
信號直接連接到MainWindow
的相應slot
即可。
乍一看,你的代碼似乎在很多方面都被破壞了...... –
你想用計時器實現什麼?爲什麼不直接從主窗口啓動窗口? – SPlatten
感謝您的回覆。 @nh_我是qt的新手。你能指出我的代碼中最重要的問題嗎? @SPlatten我的代碼主要運行在線程中,每個線程執行不同的功能並以不同的時間間隔調用。在主函數中,我只爲不同的函數調用'timerStart'。當我從main啓動窗口時,它凍結而其他線程運行。 –