如何更改Qthread内Qtimer的间隔?

2024-05-23 14:24:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望能够在QThread内部更改QTimer的间隔时间。这是我的密码

import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, QTimer, QThread


class Worker(QObject):
    def __init__(self):
        QObject.__init__(self)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.work)

    def start(self):
        self.timer.start(1000)

    def work(self):
        print("Hello World...")

    def set_interval(self, interval):
        self.timer.setInterval(interval)


def main():
    # Set up main window
    app = QApplication(sys.argv)
    win = QMainWindow()
    win.setFixedSize(200, 100)
    spinbox_interval = QtWidgets.QSpinBox(win)
    spinbox_interval.setMaximum(5000)
    spinbox_interval.setSingleStep(500)
    spinbox_interval.setValue(1000)

    worker = Worker()
    thread = QThread()
    worker.moveToThread(thread)
    thread.started.connect(worker.start)
    thread.start()

    def change_interval():
        value = spinbox_interval.value()
        worker.set_interval(value)

    spinbox_interval.valueChanged.connect(change_interval)

    win.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

如果在启动计时器后调用worker.setInterval(),则超时信号不再发出信号。有人能解释一下我做错了什么吗


Tags: fromimportselfmaindefsysthreadstart
1条回答
网友
1楼 · 发布于 2024-05-23 14:24:38

要了解问题,必须在console/CMD中运行以获取错误消息,从而了解原因,如果执行此操作,则会得到以下错误消息:

QObject::killTimer: Timers cannot be stopped from another thread
QObject::startTimer: Timers cannot be started from another thread

要理解此错误消息,您必须知道:

  • QObject不是线程安全的,因此不能从其他线程修改它们
  • QObject的子对象与父对象生活在同一线程中

因此,作为工作线程的子线程的计时器作为其父线程存在于次线程中,因此不能从其他线程修改它。在这种情况下,将在次线程中发送修改信息,为此有几个选项:

  • QMetaObject.invokeMethod()具有pyqtSlot:

    import sys
    from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
    from PyQt5.QtCore import pyqtSlot, QMetaObject, QObject, Qt, QTimer, QThread, Q_ARG
    
    
    class Worker(QObject):
        def __init__(self):
            QObject.__init__(self)
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.work)
    
        def start(self):
            self.timer.start(1000)
    
        def work(self):
            print("Hello World...")
    
        @pyqtSlot(int)
        def set_interval(self, interval):
            self.timer.setInterval(interval)
    
    
    def main():
        # Set up main window
        app = QApplication(sys.argv)
        win = QMainWindow()
        win.setFixedSize(200, 100)
        spinbox_interval = QSpinBox(win)
        spinbox_interval.setMaximum(5000)
        spinbox_interval.setSingleStep(500)
        spinbox_interval.setValue(1000)
    
        worker = Worker()
        thread = QThread()
        worker.moveToThread(thread)
        thread.started.connect(worker.start)
        thread.start()
    
        def change_interval():
            value = spinbox_interval.value()
            QMetaObject.invokeMethod(
                worker, "set_interval", Qt.QueuedConnection, Q_ARG(int, value)
            )
    
        spinbox_interval.valueChanged.connect(change_interval)
    
        win.show()
        ret = app.exec_()
    
        QMetaObject.invokeMethod(worker.timer, "stop")
        thread.quit()
        thread.wait()
        sys.exit(ret)
    
    
    if __name__ == "__main__":
        main()
    
  • 带有插槽的自定义信号:

    import sys
    from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QMetaObject, QObject, Qt, QTimer, QThread
    
    
    class Worker(QObject):
        updateInterval = pyqtSignal(int)
    
        def __init__(self):
            QObject.__init__(self)
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.work)
    
            self.updateInterval.connect(self.set_interval)
    
        def start(self):
            self.timer.start(1000)
    
        def work(self):
            print("Hello World...")
    
        @pyqtSlot(int)
        def set_interval(self, interval):
            self.timer.setInterval(interval)
    
    
    def main():
        # Set up main window
        app = QApplication(sys.argv)
        win = QMainWindow()
        win.setFixedSize(200, 100)
        spinbox_interval = QSpinBox(win)
        spinbox_interval.setMaximum(5000)
        spinbox_interval.setSingleStep(500)
        spinbox_interval.setValue(1000)
    
        worker = Worker()
        thread = QThread()
        worker.moveToThread(thread)
        thread.started.connect(worker.start)
        thread.start()
    
        def change_interval():
            value = spinbox_interval.value()
            worker.updateInterval.emit(value)
    
        spinbox_interval.valueChanged.connect(change_interval)
    
        win.show()
        ret = app.exec_()
    
        QMetaObject.invokeMethod(worker.timer, "stop")
        thread.quit()
        thread.wait()
        sys.exit(ret)
    
    
    if __name__ == "__main__":
        main()
    
  • 自定义QEvent:

    import sys
    from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
    from PyQt5.QtCore import QEvent, QMetaObject, QObject, Qt, QTimer, QThread
    
    
    class IntervalEvent(QEvent):
        def __init__(self, interval):
            QEvent.__init__(self, QEvent.User + 1000)
            self._interval = interval
    
        @property
        def interval(self):
            return self._interval
    
    
    class Worker(QObject):
        def __init__(self):
            QObject.__init__(self)
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.work)
    
        def start(self):
            self.timer.start(1000)
    
        def work(self):
            print("Hello World...")
    
        def set_interval(self, interval):
            self.timer.setInterval(interval)
    
        def event(self, e):
            if isinstance(e, IntervalEvent):
                self.set_interval(e.interval)
            return Worker.event(self, e)
    
    
    def main():
        # Set up main window
        app = QApplication(sys.argv)
        win = QMainWindow()
        win.setFixedSize(200, 100)
        spinbox_interval = QSpinBox(win)
        spinbox_interval.setMaximum(5000)
        spinbox_interval.setSingleStep(500)
        spinbox_interval.setValue(1000)
    
        worker = Worker()
        thread = QThread()
        worker.moveToThread(thread)
        thread.started.connect(worker.start)
        thread.start()
    
        def change_interval():
            value = spinbox_interval.value()
            event = IntervalEvent(value)
            QApplication.postEvent(worker, event)
    
        spinbox_interval.valueChanged.connect(change_interval)
    
        win.show()
        ret = app.exec_()
    
        QMetaObject.invokeMethod(worker.timer, "stop")
        thread.quit()
        thread.wait()
        sys.exit(ret)
    
    
    if __name__ == "__main__":
        main()
    
  • QTimer.singleShot()functools.partial()(仅适用于PyQt5,不适用于PySide2)

    import sys
    from functools import partial
    
    from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
    from PyQt5.QtCore import QMetaObject, QObject, Qt, QTimer, QThread
    
    
    class Worker(QObject):
        def __init__(self):
            QObject.__init__(self)
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.work)
    
        def start(self):
            self.timer.start(1000)
    
        def work(self):
            print("Hello World...")
    
        def set_interval(self, interval):
            print(interval)
            self.timer.setInterval(interval)
    
    
    def main():
        # Set up main window
        app = QApplication(sys.argv)
        win = QMainWindow()
        win.setFixedSize(200, 100)
        spinbox_interval = QSpinBox(win)
        spinbox_interval.setMaximum(5000)
        spinbox_interval.setSingleStep(500)
        spinbox_interval.setValue(1000)
    
        worker = Worker()
        thread = QThread()
        worker.moveToThread(thread)
        thread.started.connect(worker.start)
        thread.start()
    
        def change_interval():
            value = spinbox_interval.value()
            wrapper = partial(worker.set_interval, value)
            QTimer.singleShot(0, wrapper)
    
        spinbox_interval.valueChanged.connect(change_interval)
    
        win.show()
        ret = app.exec_()
    
        QMetaObject.invokeMethod(worker.timer, "stop")
        thread.quit()
        thread.wait()
        sys.exit(ret)
    
    
    if __name__ == "__main__":
        main()
    

相关问题 更多 >