1

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.

8
  • 1
    No blocking function or loop should ever happen in the main Qt thread: your timer is started, but it cannot be processed since Qt event loop is blocked in the while. Just check the variable in the __myTimerHandler. Commented Feb 5, 2021 at 13:55
  • @musicamante Thanks foe the comment, now I understand that this is the problem, but the idea of this was to wait for certain amount of repetition of calling the handler, then do other stuff, for instance, before closing the window have a count down number from 20 to 0. and then close the window. Commented Feb 5, 2021 at 15:55
  • 1
    I don't understand where is the problem: you already got the counter working, if you want it to repeat 20 times and then stop (or do anything else), just add an if self.myCounter >= 20: ... Commented Feb 5, 2021 at 16:02
  • That's I am trying to say, in the way that you propose I have to write this if condition line in the __myTimerHandler, however I would like to do it in the myButtonClicked function. Another benefit of this implementation is that you can use this timer as a delay in several functions. For instance if you want to have certain amount of time delay between two line of codes. Commented Feb 5, 2021 at 16:07
  • 1
    @Majidkhalili Put QApplication.processEvents() in your while-loop, and time.sleep(1) in __DoNothing. This will allow timer-events to be processed whilst running code that would otherwise block the event-loop. Commented Feb 5, 2021 at 17:44

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.