Search code examples
pythonpyqtpyqt5qtimer

the pyqt5 timer event not working in a qthread?


I need to calculate a number in a thread and then to process the data, but the timer in a thread not working.

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt, QObject, QThread, pyqtSlot, QTimer
import sys, time

class Worker(QObject):
    def __init__(self):
        super().__init__()

        self.i = 0
        self.startTimer(100)

    def timerEvent(self, QTimerEvent):
        self.i += 2

    @pyqtSlot()
    def run(self):
        while True:
            print(self.i)
            time.sleep(1)

class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.worker = Worker()
        self.thread = QThread()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.start()

app = QApplication(sys.argv)
demo = Demo()
demo.show()
app.exec()

The teminal show 0 always, can anyone help me.


Solution

  • The signals and events need an event loop to work. When you use a QThread you create that event loop but when using time.sleep() you are blocking it preventing timerEvent() method from being invoked. So the solution is to replace time.sleep() with another option (QEventLoop + QTimer) that does not block it.

    from PyQt5 import QtCore, QtWidgets
    
    
    class Worker(QtCore.QObject):
        def __init__(self):
            super().__init__()
    
            self.i = 0
            self.m_id = self.startTimer(100)
    
        def timerEvent(self, event):
            if self.m_id == event.timerId():
                self.i += 2
            super().timerEvent(event)
    
        @QtCore.pyqtSlot()
        def run(self):
            while True:
                print(self.i)
                loop = QtCore.QEventLoop()
                QtCore.QTimer.singleShot(1000, loop.quit)
                loop.exec()
    
    
    class Demo(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            thread = QtCore.QThread(self)
            self.worker = Worker()
            self.worker.moveToThread(thread)
            thread.started.connect(self.worker.run)
            thread.start()
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        demo = Demo()
        demo.show()
        sys.exit(app.exec())