I am using PyQt 5 for a GUI app, and I am having a threading issue.
There is a close button and once it is clidked, a QTimer starts and then it waits in a while loop that is conditioned on a value of a variable in which is being incremented in the QTimer handler.
The problem is that the timer does not start. Interestingly, if I comment the while loop, the timer works properly.
Here is the code:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QHBoxLayout import sys import time class MyClass(QMainWindow): myTimer = None myCounter = 0 def __init__(self): QMainWindow.__init__(self) myButton = QPushButton(text='Close') myButton.clicked.connect(self.myButtonClicked) centralWidget = QWidget(self) self.setCentralWidget(centralWidget) ly = QHBoxLayout() ly.addWidget(myButton) centralWidget.setLayout(ly) self.myTimer = QTimer() self.myTimer .timeout.connect(self.__myTimerHandler) def myButtonClicked(self): self.myCounter= 0 self.myTimer.start(1000) print('Loop Start') while self.myCounter < 10: self.__DoNothing() print('Loop END') def __DoNothing(self): print('Nothing') #time.sleep(2) def __myTimerHandler(self): self.myCounter = self.myCounter + 1 print('Counter:' + str(self.myCounter)) if __name__ == "__main__": app = QApplication(sys.argv) mainWin = MyClass() mainWin.show() sys.exit( app.exec_() )
Output:
Loop Start Nothing Nothing Nothing Nothing Nothing Nothing Nothing
As I explained, with this code the QTImer (my timer) does not start. But if I comment the while loop, the QTimer starts properly and the handler is called every second.
Commenting out the time.sleep line in the DoNothing function does not help.
I can imagine it is something about multithreading and accessing the same variable, but I have no idea how to resolve it.
Advertisement
Answer
As @musicamante mentioned in the comments “No blocking function or loop should ever happen in the main Qt thread”. And in the the code provided in the question, the thread is in fact being started, but because of the blocking loop, the handler doesn’t get the chance to be called.
As @ekhumoro mentioned in the comments, the solution is to process events in the wait loop using “QApplication.processEvents()”. And also adding an sleep function in the “__DoNothing” function.
So the final solution will be as below:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QHBoxLayout import sys import time class MyClass(QMainWindow): myTimer = None myCounter = 0 def __init__(self): QMainWindow.__init__(self) myButton = QPushButton(text='Close') myButton.clicked.connect(self.myButtonClicked) centralWidget = QWidget(self) self.setCentralWidget(centralWidget) ly = QHBoxLayout() ly.addWidget(myButton) centralWidget.setLayout(ly) self.myTimer = QTimer() self.myTimer .timeout.connect(self.__myTimerHandler) def myButtonClicked(self): self.myCounter= 0 self.myTimer.start(1000) print('Loop Start') while self.myCounter < 10: self.__DoNothing() QApplication.processEvents() print('Loop END') def __DoNothing(self): print('Nothing') time.sleep(0.250) def __myTimerHandler(self): self.myCounter = self.myCounter + 1 print('Counter:' + str(self.myCounter)) if __name__ == "__main__": app = QApplication(sys.argv) mainWin = MyClass() mainWin.show() sys.exit( app.exec_() )
And the output would be exactly as expected as below:
Loop Start Nothing Nothing Nothing Nothing Counter:1 Nothing Nothing Nothing Nothing Counter:2 Nothing Nothing Nothing Nothing Counter:3 Nothing Nothing Nothing Loop Start Nothing Nothing Nothing Nothing Counter:1 Nothing Nothing Nothing Nothing Counter:2 Nothing Nothing Nothing Nothing Nothing Counter:3 Nothing Nothing Nothing Counter:4 Nothing Nothing Nothing Nothing Counter:5 Nothing Nothing Nothing Nothing Counter:6 Nothing Nothing Nothing Nothing Counter:7 Nothing Nothing Nothing Nothing Counter:8 Nothing Nothing Nothing Nothing Counter:9 Nothing Nothing Nothing Nothing Counter:10 Loop END Loop END
Obviously the time difference between the two counters call backs is not necessarily the same.