QRunnable 中的计时器

Posted

技术标签:

【中文标题】QRunnable 中的计时器【英文标题】:Timer in a QRunnable 【发布时间】:2020-05-26 15:45:56 【问题描述】:

我在this answer 的帮助下创建了以下玩具类:

class Worker(QtCore.QThread):
    def work(self):
        print("message")

    def __init__(self):
        super(Worker, self).__init__()
        self.timer = QtCore.QTimer() 
        self.timer.moveToThread(self)
        self.timer.timeout.connect(self.work)

    def run(self):
        self.timer.start(1000)
        loop = QtCore.QEventLoop()
        loop.exec_()

当我使用QThreadPool 时,如何从新线程启动计时器?

我需要定期重复更新 GUI,但如果我在主线程中添加 QTimer,整个应用程序会感觉非常缓慢。我的理解是,通过 QThreadPool 将其包含在单独的线程中,这可能是一个更有效的解决方案,因为新线程可以在完成后自动自行删除。

但是,每当我在上面的类中将 QtCore.QThread 更改为 QtCore.QRunnable 并尝试使用下面的代码启动线程时,我都会收到错误:

self.threadpool = QtCore.QThreadPool()
worker = Worker()
self.threadpool.start(worker)

【问题讨论】:

【参考方案1】:

如果您想使用 QThreadPool 每 T 秒运行一次任务,那么 QTimer 不必存在于另一个线程中,而是 QTimer 启动 QRunnable:

import json
import random
import threading
import time

from PyQt5 import QtCore, QtGui, QtWidgets
import sip


class Signaller(QtCore.QObject):
    dataChanged = QtCore.pyqtSignal(object)


class TimerRunnable(QtCore.QRunnable):
    def __init__(self):
        super().__init__()
        self._signaller = Signaller()

    @property
    def signaller(self):
        return self._signaller

    def run(self):
        print("secondary thread:", threading.current_thread())
        time.sleep(0.5)
        r = random.choice(("hello", (1, 2, 3), "key": "value"))
        print("send:", r)
        if not sip.isdeleted(self.signaller):
            self.signaller.dataChanged.emit(r)


class Widget(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.text_edit = QtWidgets.QTextEdit()
        self.setCentralWidget(self.text_edit)

        timer = QtCore.QTimer(self, timeout=self.on_timeout, interval=1000)
        timer.start()

    @QtCore.pyqtSlot()
    def on_timeout(self):
        runnable = TimerRunnable()
        runnable.signaller.dataChanged.connect(self.on_data_changed)
        QtCore.QThreadPool.globalInstance().start(runnable)

    @QtCore.pyqtSlot(object)
    def on_data_changed(self, data):
        text = json.dumps(data)
        self.text_edit.append(text)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = Widget()
    w.show()

    print("main thread:", threading.current_thread())

    sys.exit(app.exec_())

【讨论】:

我明白你的意思,但是如果我在主 GUI 线程中有多个计时器,这不会降低它的响应能力吗? @Giuseppe QTimer 只有一个 在本例中是。但是,如果我有其他计时器更新 GUI 的不同部分,这不会成为问题吗? @Giuseppe QTimers 不会产生开销,您有多少个 QTImer? @Giuseppe mmm,plop,4 个 QTimer 不会产生开销。每个 QTimer 都使用事件循环来查看是否到了触发信号的时间。一般来说,信号使用事件循环,它们不排队。

以上是关于QRunnable 中的计时器的主要内容,如果未能解决你的问题,请参考以下文章

TCP中的计时器

javascript中的普通倒计时计时器

MVC 中的倒数计时器

计时器从 Notification RemoteViews 中的特定值开始

WinForms 中的计时器

具有长时间运行任务的单独线程中的计时器