如何在单独的 QThread 中使用 QTimer

2022-01-12 00:00:00 python python-2.7 pyqt5 qthread qtimer

问题描述

我有一些计算量很大的任务,我想在每 5 秒后循环运行一次,而不会阻塞主事件循环.为此,我打算使用 QTimer 和单独的线程来运行它.我已经尝试了以下代码,但到目前为止还没有成功:

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_())

地点:

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 永远不会被 threaded_timer 中的 QTimer 触发.我的线程架构是否正确?

The slot_function is never fired by the QTimer in threaded_timer. Is my threading architecture correct?


解决方案

一个 QTimer 需要一个正在运行的事件循环.默认情况下,QThread.run() 将为线程启动一个本地事件循环,但如果你以你所做的方式完全覆盖它,那将不会发生 - 所以计时器事件永远不会被处理.

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.

通常,当您需要本地事件循环时,您应该创建一个工作对象来完成所有处理,然后使用 moveToThread 将其放在单独的线程中.如果没有,完全可以覆盖 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().

下面的演示展示了如何做到这一点.请注意,在线程启动后创建计时器非常重要,否则它将在错误的线程中创建,并且其计时器事件不会被线程的事件循环处理.同样重要的是,工作线程和主线程之间的所有通信都是通过信号完成的,以确保线程安全.永远不要尝试在主线程之外直接执行 GUI 操作,因为 Qt 根本不支持.出于演示的目的,主线程中的第二个计时器用于在固定时间间隔后停止所有处理.如果有 GUI,用户通过按钮进行干预也能实现同样的效果.

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

相关文章