問題描述
我有一些計(jì)算量很大的任務(wù),我想在每 5 秒后循環(huán)運(yùn)行一次,而不會(huì)阻塞主事件循環(huán).為此,我打算使用 QTimer
和單獨(dú)的線程來運(yùn)行它.我已經(jīng)嘗試了以下代碼,但到目前為止還沒有成功:
I have some computationally heavy task that I want to run in a loop after every 5 seconds without blocking the main event-loop. For this, I intend to use a QTimer
and a separate thread to run it. I have tried the following code but it has not worked so far:
@pyqtSlot()
def heavy_task_function():
# Sleep for 10 seconds to simulate heavy computation
time.sleep(10)
print "First Timer Fired"
if __name__ == "__main__":
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
threaded_timer = ModbusComThread(heavy_task_function)
threaded_timer.start()
sys.exit(app.exec_())
地點(diǎn):
class ModbusComThread(QThread):
def __init__(self, slot_function):
QThread.__init__(self)
self.slot_function = slot_function
self.send_data_timer = None
def run(self):
print "Timer started on different thread"
self.send_data_timer = QTimer(self)
self.send_data_timer.timeout.connect(self.slot_function)
self.send_data_timer.start(5000)
def stop(self):
self.send_data_timer.stop()
slot_function
永遠(yuǎn)不會(huì)被 threaded_timer
中的 QTimer
觸發(fā).我的線程架構(gòu)是否正確?
The slot_function
is never fired by the QTimer
in threaded_timer
. Is my threading architecture correct?
推薦答案
一個(gè) QTimer
需要一個(gè)正在運(yùn)行的事件循環(huán).默認(rèn)情況下,QThread.run()
將為線程啟動(dòng)一個(gè)本地事件循環(huán),但如果你以你所做的方式完全覆蓋它,那將不會(huì)發(fā)生 - 所以計(jì)時(shí)器事件永遠(yuǎn)不會(huì)被處理.
A QTimer
needs a running event-loop. By default, QThread.run()
will start a local event-loop for the thread, but if you completely override it in the way that you have done, that won't happen - so the timer events will never be processed.
通常,當(dāng)您需要本地事件循環(huán)時(shí),您應(yīng)該創(chuàng)建一個(gè)工作對(duì)象來完成所有處理,然后使用 moveToThread 將其放在單獨(dú)的線程中.如果沒有,完全可以覆蓋 QThread.run()
.
In general, when you need a local event-loop you should create a worker object to do all the processing and then use moveToThread to put it in a separate thread. If not, it's perfectly okay to override QThread.run()
.
下面的演示展示了如何做到這一點(diǎn).請(qǐng)注意,在線程啟動(dòng)后創(chuàng)建計(jì)時(shí)器非常重要,否則它將在錯(cuò)誤的線程中創(chuàng)建,并且其計(jì)時(shí)器事件不會(huì)被線程的事件循環(huán)處理.同樣重要的是,工作線程和主線程之間的所有通信都是通過信號(hào)完成的,以確保線程安全.永遠(yuǎn)不要嘗試在主線程之外直接執(zhí)行 GUI 操作,因?yàn)?Qt 根本不支持.出于演示的目的,主線程中的第二個(gè)計(jì)時(shí)器用于在固定時(shí)間間隔后停止所有處理.如果有 GUI,用戶通過按鈕進(jìn)行干預(yù)也能實(shí)現(xiàn)同樣的效果.
The demo below shows how to do this. Note that it's very important to create the timer after the thread has started, otherwise it would be created in the wrong thread and its timer-events wouldn't be processed by the thread's event-loop. It's also important that all communication between the worker thread and the main thread is done via signals, so as to ensure thread-safety. Never try to directly perform GUI operations outside the main thread, as Qt does not support that at all. For the purposes of the demo, a second timer in the main thread is used to stop all processing after a fixed interval. If there was a GUI, user intervention via a button would achieve the same thing.
演示:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ModbusComWorker(QObject):
finished = pyqtSignal()
def start(self):
self._timer = QTimer(self)
self._timer.timeout.connect(self.process)
self._timer.start(2000)
def stop(self):
self._timer.stop()
self.finished.emit()
def process(self):
print('processing (thread: %r)' % QThread.currentThread())
QThread.sleep(3)
if __name__ == "__main__":
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
thread = QThread()
worker = ModbusComWorker()
worker.moveToThread(thread)
def finish():
print('shutting down...')
thread.quit()
thread.wait()
app.quit()
print('stopped')
worker.finished.connect(finish)
thread.started.connect(worker.start)
thread.start()
timer = QTimer()
timer.setSingleShot(True)
timer.timeout.connect(worker.stop)
timer.start(15000)
print('starting (thread: %r)' % QThread.currentThread())
sys.exit(app.exec_())
輸出:
starting (thread: <PyQt5.QtCore.QThread object at 0x7f980d096b98>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
shutting down...
stopped
這篇關(guān)于如何在單獨(dú)的 QThread 中使用 QTimer的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!