Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

PyQt not responding with example code



  • Hello i made simple code like this. While my function is counting my program is not responding.

    Please can somebody show me how to change this code to get it work with that "Threads" thing ?

    THANK YOU !

    import sys
    from PyQt5 import QtWidgets
    from PyQt5.QtWidgets import *
    
    
    
    class Window(QMainWindow):
    
       def __init__(self):
           super().__init__()
    
           self.setFixedSize(300, 300)
           self.move(100,100)
           self.setWindowTitle("Hello")
    
           self.label = QLabel(self)
           self.label.setText("Not done")
           self.label.resize(400,500)
           self.label.move(0,5)
    
    
           self.b1 = QtWidgets.QPushButton(self)
           self.b1.setText("Count")
           self.b1.move(100, 120)
           self.b1.resize(130, 32)
           self.b1.clicked.connect(self.Count)
    
           self.show()
    
       def Count(self):
           self.i = 0
    
           while self.i != 50000000:
                  self.i +=1
    
           self.label.setText("DONE")
    
    
    
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = Window()
        sys.exit(app.exec())
    

  • Lifetime Qt Champion

    Hi,

    Because your counting loop is blocking Qt's event loop.



  • And how can i improve this code to not doing it ? Can you tell me what to add to this code ? Please.


  • Lifetime Qt Champion

    If you want to run long lasting tasks without blocking your GUI either use QtConcurrent or move it to another thread.



  • I just can not find on internet how to properly use this. Where to put this command or how to work with it.


  • Lifetime Qt Champion

    What command are you talking about ?



  • I mean this QtConcurrent. I dont know how to use it, where to put this thing in this code.


  • Lifetime Qt Champion

    Did you read the documentation I linked ?



  • @Samuel-Bachorik This tutorial about multithreading may help you solve your problem.



  • @unplug Thank you man !!!!!


  • Banned

    @Samuel-Bachorik first I am going to explain issues within your MRE/MUC then I am going to give you an example that I have shared with all my students on how to implement a non-blocking QThread.

    #These are redundant
    #from PyQt5 import QtWidgets
    # This is the preferred of the two although explicitness is even better
    from PyQt5.QtWidgets import *
    
    # Always declare all other imports after your Qt Imports as
    # this prevents the miss association issue that sometimes 
    # occurs with Qt
    import sys
    
    
    class Window(QMainWindow):
       def __init__(self):
          # You should not use super( ) in Python as it introduces 4 known major issues 
          # that must be handled properly. Further there were still actual bugs within
          # the usage of super( ) when used in Python. Yes while super( ) works fine 
          # within C++ it does not work as seamlessly within Python due to the major 
          # differences between these 2 languages. Next the reason it was created was 
          # to handle a rather rare issue and unless you are doing some complicated 
          # inheritance you will most likely never run into this extremely rare issue
          # However the 4 major issues that get included by using super( ) are much 
          # more likely to occur than that rare issue its meant to solve. Of course
          # using the basic explicit method, as follows, does not cause these issues and 
          # is as just as simple as using `super( )` further you do not actually gain 
          # anything useful by using `super( )` in Python that could not be done in a 
          # much safer manner.
          # super().__init__()
            QMainWindow.__init__(self)
    
           self.setFixedSize(300, 300)
           self.move(100,100)
           self.setWindowTitle("Hello")
    
           self.label = QLabel(self)
           self.label.setText("Not done")
           self.label.resize(400,500)
           self.label.move(0,5)
    
           self.b1 = QtWidgets.QPushButton(self)
           self.b1.setText("Count")
           self.b1.move(100, 120)
           self.b1.resize(130, 32)
           self.b1.clicked.connect(self.Count)
    
       def Count(self):
         # As soon as you start this code block you prevent the Main Event Handler
         # from processing any more Events until this process is complete this is 
         # not a threaded function -- but even if it were a threaded function due 
         # to the Python GIL it would again prevent the Main Event Handler from 
         # processing any more Events within its Queue the only way to get away 
         # from this is to explicitly pass back control to the Main Event Handler
         # which can be dangerous if you are not coding to prevent issues or you
         # can launch an actual second process using multiprocessing
           self.i = 0
    
           while self.i != 50000000:
                  self.i +=1
    
           self.label.setText("DONE")
    
    if __name__ == "__main__":
      # If you are not going to handle Command Line arguments then do not include sys.argv
      # However if you do plan to use Command Line arguments then look into the argparser
      # library as this handles them much more intuitively, concisely, and efficiently no sense
      # reinventing a quality wheel one you can simply employ the one that already exists 
        MainEvntThred = QApplication([])
    
        MainApplication = Window()
        MainApplication.show()
    
      # The sys.exit is not necessary with PyQt5 as it has been handled now but if you need
      # it for some other reason it does not hurt to include it just remember extraneous code
      # can muddy the waters and make your code more complex than it needs to be
      #  sys.exit(app.exec())
        MainEvntThred.exec()
    

  • Banned

    Here is the example QThreaded Application MUC that I share with all my online students to help them understand the basics of using a QThread -- just be forewarned though there are a lot gotchas when using QThreads that you may encounter that I have not fully documented within this simple example but they are only potential issues and if you are doing something simple you may not ever run into them. My usage of QThread with Multiprocessing is not so simple so I have experienced these gotchas I speak of and had to resolve them. Also this example using QCoreApplication.processEvents() which if you do not handle your events properly can lead to issues within your code. Of course, if you do keep this in mind while you are coding and make sure that you handle your events properly so that you do not step on yourself then everything works just fine. Note this issue is actually an issue that occurs in many event driven languages so it is not something new just something you need to be aware of when writing your code.

    from PyQt5.QtCore    import QCoreApplication, QObject, QThread, pyqtSignal, pyqtSlot
    from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
    from PyQt5.QtWidgets import QPushButton, QLineEdit
    
    from time import sleep as tmSleep
    
    class Processor(QObject):
        sigCount = pyqtSignal(int)
    
        def __init__(self):
            QObject.__init__(self)
            self.Connected = True
            self.StreamRdy = False
            self.Count = 0
            self.Delay = 0
            
            print('From within Thread')
    
        def ProcessRunner(self):
            print('Starting Thread Process')
            while self.Connected:
                if self.StreamRdy:
                    self.Count += 1
                    self.sigCount.emit(self.Count)
                    tmSleep(0.5)
                else:
                    self.Delay -= 1
                    self.sigCount.emit(self.Delay)
                    tmSleep(0.5)
                QCoreApplication.processEvents()
    
        @pyqtSlot()
        def CountUp(self):
            print('..... Counting Up')
            self.StreamRdy = True
    
        @pyqtSlot()
        def CountDown(self):
            print('..... Counting Down')
            self.StreamRdy = False
    
        @pyqtSlot()
        def StopCount(self):
            print('..... Stopped Counting')
            self.Connected = False
    
        @pyqtSlot()
        def RestartCount(self):
            print('..... Restarted Counting')
            if not self.Connected:
                self.Connected = True
                self.ProcessRunner()
            self.Delay = 0
            self.Count = 0
    
    
    class MainWindow(QWidget):
        sigCntUp  = pyqtSignal()
        sigCntDn  = pyqtSignal()
        sigStpCnt = pyqtSignal()
        sigRestrt = pyqtSignal()
    
        def __init__(self):
            QWidget.__init__(self)
    
            self.setWindowTitle('Main Window')    
            self.setGeometry(150, 150, 200, 200)
    
            self.btnCntUp = QPushButton('Up')
            self.btnCntUp.clicked.connect(self.CountUp)
    
            self.btnCntDn = QPushButton('Down')
            self.btnCntDn.clicked.connect(self.CountDwn)
    
            self.btnStopCnt = QPushButton('Stop')
            self.btnStopCnt.clicked.connect(self.StopCnt)
    
            self.btnRestart = QPushButton('Restart')
            self.btnRestart.clicked.connect(self.RestrtCnt)
    
            self.btnTermnat = QPushButton('Quit')
            self.btnTermnat.clicked.connect(self.TerminateThread)
            
            self.lneOutput = QLineEdit()
            
            HBox = QHBoxLayout()
            HBox.addWidget(self.btnCntUp)
            HBox.addWidget(self.btnCntDn)
            HBox.addWidget(self.btnStopCnt)
            HBox.addWidget(self.btnRestart)
            HBox.addWidget(self.btnTermnat)
            HBox.addStretch(1)
            
            VBox = QVBoxLayout()
            VBox.addWidget(self.lneOutput)
            VBox.addLayout(HBox)
            
            self.setLayout(VBox)
            
            self.EstablishThread()
    
        def EstablishThread(self):
          # Create the Object from Class
            self.Prcssr = Processor()
          # Assign the Database Signals to Slots
            self.Prcssr.sigCount.connect(self.CountRecieve)
          # Assign Signals to the Database Slots
            self.sigCntUp.connect(self.Prcssr.CountUp)
            self.sigCntDn.connect(self.Prcssr.CountDown)
            self.sigStpCnt.connect(self.Prcssr.StopCount)
            self.sigRestrt.connect(self.Prcssr.RestartCount)
    
          # Create the Thread
            self.ThredHolder = QThread()
          # Move the Listener to the Thread
            self.Prcssr.moveToThread(self.ThredHolder)
          # Assign the Listener Starting Function to the Thread Call
            self.ThredHolder.started.connect(self.Prcssr.ProcessRunner)
          # Start the Thread which launches Listener.Connect( )
            self.ThredHolder.start()
    
        @pyqtSlot(int)
        def CountRecieve(self, Count):
            self.lneOutput.setText('Count : ' + str(Count))
    
        def CountUp(self):
            print('Count Up')
            self.sigCntUp.emit()
    
        def CountDwn(self):
            print('Count Down')
            self.sigCntDn.emit()
    
        def StopCnt(self):
            print('Stop Counting')
            self.sigStpCnt.emit()
    
        def RestrtCnt(self):
            print('Restart Counting')
            self.sigRestrt.emit()
    
        def TerminateThread(self):
            print('Close Thread')
            self.ThredHolder.quit()
            if not self.ThredHolder.isRunning():
                self.ThredHolder.wait()
                print('Thread Active ',self.ThredHolder.isRunning())
            else:
                print('Thread is still Active cannot Quit')
                QCoreApplication.processEvents()
    
    if __name__ == '__main__':
        MainThred = QApplication([])
    
        MainApplication = MainWindow()
        MainApplication.show()
    
        MainThred.exec()
    

Log in to reply