Search code examples
pythonmultithreadingpython-multithreadingqthreadpyqt5

How to pause/play a thread in PyQT5?


I'm making a light GUI program with PyQT5.

But now I'm facing some problem about thread.

I just made simple test program like bottom:

the program simply trying to append numbers to textbox, but it crashes.

I don't know why but somehow I can prevent it by removing a comment(time.sleep)

import sys
import threading
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class Some(QWidget):
    e = threading.Event()

    def btnfunc(self):
        self.e.set()        

    def __init__(self):
        super().__init__()
        self.myButton = QPushButton('do next')
        self.logs = QTextEdit()

        self.mylay = QVBoxLayout()
        self.mylay.addWidget(self.myButton)
        self.mylay.addWidget(self.logs)

        self.setLayout(self.mylay)
        self.setGeometry(300, 300, 300, 550)
        self.setWindowTitle('mytest')
        self.show()
        t = threading.Thread(target=self.myfunc, args=( ))
        t.start()
        self.myButton.clicked.connect(self.btnfunc)

    def myfunc(self):
        for i in range(300):
            # time.sleep(0.4)
            self.logs.append(str(i))
            if i == 20:
                self.e.wait()

app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())

It would be better if sets time higher.

I thought it is because of resource accessing, since it is pyQT5 GUI.

So I've find QThread. and I tried like bottom,

import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class Some(QWidget):
    qw = QWaitCondition()
    qm = QMutex()

    def btnfunc(self):
        self.qw.wakeAll()

    def __init__(self):
        super().__init__()
        self.myButton = QPushButton('do next')
        self.logs = QTextEdit()

        self.mylay = QVBoxLayout()
        self.mylay.addWidget(self.myButton)
        self.mylay.addWidget(self.logs)

        self.setLayout(self.mylay)
        self.setGeometry(300, 300, 300, 550)
        self.setWindowTitle('mytest')
        self.show()    
        self.myButton.clicked.connect(self.btnfunc)

        self.thread = QThread()
        self.thread.started.connect(self.myfunc)
        self.thread.start()

    def myfunc(self):
        for i in range(300):
            self.logs.append(str(i))
            if i == 20:
                self.qw.wait(self.qm)

app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())

But crashes, doesn't work. and tried QThread+threading.Event(). It freezes GUI.

Now I don't know how to proceed it...

Edit: I just realized about thread. Should not be accessed from other thread except QThread. Then I will keep find about QWaitCondition


Solution

  • You should not control GUI directly via multithreading. Since two different threads are trying to control the GUI this results to freeze or crash. I have learnt about this concept from here http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html

    Here is your code that will work perfectly.

    import sys
    import threading
    import time
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    
    # Added new
    class Communicate(QObject):
        signal = pyqtSignal(str)
    
    class Some(QWidget):
        e = threading.Event()
    
        def btnfunc(self):
            self.e.set()        
    
        def __init__(self):
            super().__init__()
    
            #communicate object
            self.comm = Communicate()
            self.comm.signal.connect(self.append_data)
    
            self.myButton = QPushButton('do next')
            self.logs = QTextEdit()
    
            self.mylay = QVBoxLayout()
            self.mylay.addWidget(self.myButton)
            self.mylay.addWidget(self.logs)
    
            self.setLayout(self.mylay)
            self.setGeometry(300, 300, 300, 550)
            self.setWindowTitle('mytest')
            self.show()
            t = threading.Thread(target=self.myfunc, args=( ))
            t.start()
            self.myButton.clicked.connect(self.btnfunc)
    
        def myfunc(self):
            for i in range(300):
                # time.sleep(0.4)
                #self.logs.append(str(i))
                self.comm.signal.emit(str(i))
                if i == 20:
                    self.e.wait()
    
        def append_data(self, data):
            self.logs.append(data)
    
    app = QApplication(sys.argv)
    ex = Some()
    sys.exit(app.exec_())