Search code examples
pythonpyqtpyqt5qthreadqtimer

QTimer in worker thread blocking GUI


I am trying to create a worker thread whose job is to monitor the status bit of a positioning platform.

To do this I connect a QTimer timeout signal to a function that queries the platform.

class expSignals(QtCore.QObject):
    pause=QtCore.pyqtSignal()

class motorpositioner(QtCore.QObject):
def __init__(self):
    QtCore.QThread.__init__(self)
    self.timer = QtCore.QTimer()
    self.timer.start(100)
    self.timer.timeout.connect(self.do_it)
    self.lock=QtCore.QMutex()
    self.running=True
    self.stat=0
def do_it(self):
        with QtCore.QMutexLocker(self.lock):
            #self.stat = self.motors.get_status()
            print(self.stat)
        time.sleep(5)
@QtCore.pyqtSlot()
def stop1(self):
    self.timer.stop()
    print('stop heard')

The GUI stuff looks like this:

class MyApp(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.thread=QtCore.QThread(self)
        #worker
        self.mot=motorpositioner()

# =============================================================================
#         Putting buttons and GUI stuff in place
# =============================================================================
        self.button=QtWidgets.QPushButton('Derp',self)
        layout = QtWidgets.QHBoxLayout()
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.setGeometry( 300, 300, 350, 300 )

# =============================================================================
#         Connecting signals
# =============================================================================

        self.sig=expSignals()
        self.sig2=expSignals()
        self.button.clicked.connect(self.stop)
        self.sig.pause.connect(self.mot.stop1)
        self.sig2.pause.connect(self.thread.quit)


        self.mot.moveToThread(self.thread)
        self.thread.start()
    def stop(self):
        self.sig.pause.emit()

    def closeEvent(self,event):
        self.sig2.pause.emit()
        event.accept()

However the way it is written now the GUI is unresponsive. However if I comment out self.timer.timeout.connect(self.do_it) and put do_it in a while(True) loop, the GUI isn't being blocked.

Why is the main thread being blocked when using QTimer?


Solution

  • I do not know what is expSignals() and I think it is not relevant, and neither is the button.

    Your code has the following errors:

    • You are starting the timer before the thread starts, so the task will run on the GUI thread.

    • QTimer is not a child of motorpositioner so if motorpositioner moves to the new thread QTimer will not. For him to move he must be a son so you must pass him as a parent to self.

    • I do not know if it is a real error, but you are firing the QTimer every 100 ms but the task takes 5 seconds, although the QMutex helps to have no problems because it is blocked.


    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    import time
    
    class motorpositioner(QtCore.QObject):
        def __init__(self):
            QtCore.QThread.__init__(self)
            self.timer = QtCore.QTimer(self)
    
            self.lock = QtCore.QMutex()
            self.running = True
            self.stat = 0
    
        def start_process(self):
            self.timer.timeout.connect(self.do_it)
            self.timer.start(100)
    
        def do_it(self):
            with QtCore.QMutexLocker(self.lock):
                #self.stat = self.motors.get_status()
                print(self.stat)
                time.sleep(5)
    
        @QtCore.pyqtSlot()
        def stop1(self):
            self.timer.stop()
            print('stop heard')
    
    class MyApp(QtWidgets.QMainWindow):
        def __init__(self):
            QtWidgets.QMainWindow.__init__(self)
    
            self.thread = QtCore.QThread(self)
            self.mot = motorpositioner()
            self.mot.moveToThread(self.thread)
            self.thread.started.connect(self.mot.start_process)
            self.thread.start()
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        ex = MyApp()
        ex.show()
        sys.exit(app.exec_())