Skip to content
Advertisement

PYQT QTimer does not start

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.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement