Search code examples
pythonmultithreadingpyqtpyqt4

window freezes when I try to show an evolving value in a Qt window using threads with python


My main objective is to show a constantly evolving value on a Qt-window textEdit. (this window contains only a checkBox and a textEdit). Sadly, I cannot click on the checkbox and the window is frozen until I shutdown the terminal.

import sys
from threading import Thread
from random import randint
import time
from PyQt4 import QtGui,uic


class MyThread(Thread):

    def __init__(self):
        Thread.__init__(self)

    #function to continually change the targeted value
    def run(self):
        for i in range(1, 20):
            self.a = randint (1, 10)
            secondsToSleep = 1
            time.sleep(secondsToSleep)


class MyWindow(QtGui.QMainWindow,Thread):
    def __init__(self):
        Thread.__init__(self)
        super(MyWindow,self).__init__()
        uic.loadUi('mywindow.ui',self)
        self.checkBox.stateChanged.connect(self.checkeven)
        self.show()

    #i show the value only if the checkbox is checked
    def checkeven(self):
        while self.checkBox.isChecked():
            self.textEdit.setText(str(myThreadOb1.a)) 



# Run following code when the program starts
if __name__ == '__main__':
   app = QtGui.QApplication(sys.argv)


   # Declare objects of MyThread class
   myThreadOb1 = MyThread()
   myThreadOb2 = MyWindow()

   # Start running the threads!
   myThreadOb1.start()
   myThreadOb2.start()

   sys.exit(app.exec_())

At the moment I'm using a thread to set a random value to a, but at the end it is supposed to be a bit more complex as I will have to take the values from an automation.

Do you have any clue why my code is acting like this? Thank you very much for your help.


Solution

  • The problem is that the while self.checkBox.isChecked() is blocking, preventing the GUI from handling other events.

    Also you should not run a PyQt GUI on another thread than the main one.

    If you want to send data from one thread to another, a good option is to use the signals.

    Doing all these considerations we have the following:

    import sys
    from threading import Thread
    from random import randint
    import time
    from PyQt4 import QtGui, uic, QtCore
    
    
    class MyThread(Thread, QtCore.QObject):
        aChanged = QtCore.pyqtSignal(int)
    
        def __init__(self):
            Thread.__init__(self)
            QtCore.QObject.__init__(self)
        #function to continually change the targeted value
        def run(self):
            for i in range(1, 20):
                self.aChanged.emit(randint(1, 10))
                secondsToSleep = 1
                time.sleep(secondsToSleep)
    
    
    class MyWindow(QtGui.QMainWindow):
        def __init__(self):
            super(MyWindow,self).__init__()
            uic.loadUi('mywindow.ui',self)
            self.thread = MyThread()
            self.thread.aChanged.connect(self.on_a_changed, QtCore.Qt.QueuedConnection)
            self.thread.start()
            self.show()
    
        #i show the value only if the checkbox is checked
        @QtCore.pyqtSlot(int)
        def on_a_changed(self, a):
            if self.checkBox.isChecked():
                self.textEdit.setText(str(a)) 
    
    
    
    # Run following code when the program starts
    if __name__ == '__main__':
       app = QtGui.QApplication(sys.argv)
    
    
       # Declare objects of MyThread class
       w = MyWindow()
    
       sys.exit(app.exec_())
    

    An option more to the style of Qt would be to use QThread since it is a class that handles a thread and is a QObject so it handles the signals easily.

    import sys
    from random import randint
    from PyQt4 import QtGui, uic, QtCore
    
    
    class MyThread(QtCore.QThread):
        aChanged = QtCore.pyqtSignal(int)
        def run(self):
            for i in range(1, 20):
                self.aChanged.emit(randint(1, 10))
                secondsToSleep = 1
                QtCore.QThread.sleep(secondsToSleep)
    
    
    class MyWindow(QtGui.QMainWindow):
        def __init__(self):
            super(MyWindow,self).__init__()
            uic.loadUi('mywindow.ui',self)
            self.thread = MyThread()
            self.thread.aChanged.connect(self.on_a_changed, QtCore.Qt.QueuedConnection)
            self.thread.start()
            self.show()
    
        #i show the value only if the checkbox is checked
        @QtCore.pyqtSlot(int)
        def on_a_changed(self, a):
            if self.checkBox.isChecked():
                self.textEdit.setText(str(a)) 
    
    
    
    # Run following code when the program starts
    if __name__ == '__main__':
       app = QtGui.QApplication(sys.argv)
    
    
       # Declare objects of MyThread class
       w = MyWindow()
    
       sys.exit(app.exec_())