1

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?

Jānis Šmits
  • 173
  • 1
  • 10
  • what is `expSignals`? – eyllanesc Jul 12 '18 at 14:18
  • sis, why am I suprised you used the QTimer instead of the standard library threading that ships with python. can't trust a framework to work all the time Please consider that something too ok. – surge10 Jul 12 '18 at 14:52
  • Using QTimer and QT threading following the advice from this post: [link](https://stackoverflow.com/questions/1595649/threading-in-a-pyqt-application-use-qt-threads-or-python-threads). Also unsure how to deal with two types approaches to threading as one is giving me a hard enough time as is. – Jānis Šmits Jul 12 '18 at 15:49

1 Answers1

2

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_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 1
    Thank you, moving the timer to child did the trick! Also to clarify - the button was added to test whether I can stop the timer from outside. The 5 second sleep was to exaggerate the GUI freezing as I hadn't noticed it with 100ms. – Jānis Šmits Jul 12 '18 at 15:45