How to stop a QThread from the GUI

后端 未结 3 522
死守一世寂寞
死守一世寂寞 2020-12-14 23:43

This is a follow up question to a previous one I posted earlier. The problem is how to stop (terminate|quit|exit) a QThread from the GUI when using the recommended method

相关标签:
3条回答
  • 2020-12-14 23:52

    I know its long ago but i just stumbled over the same problem.

    I have been also searching for an appropriate way to do this. Finally it was easy. When exiting the application the task needs to be stopped and the thread needs to be stopped calling its quit method. See stop_thread method on bottom. And you need to wait for the thread to finish. Otherwise you will get QThread: Destroyed while thread is still running' message at exit.

    (I also changed my code to use pyside)

    import time, sys
    from PySide.QtCore  import *
    from PySide.QtGui import *
    
    class Worker(QObject):
        'Object managing the simulation'
    
        stepIncreased = Signal(int)
    
        def __init__(self):
            super(Worker, self).__init__()
            self._step = 0
            self._isRunning = True
            self._maxSteps = 20
    
        def task(self):
            if not self._isRunning:
                self._isRunning = True
                self._step = 0
    
            while self._step  < self._maxSteps  and self._isRunning == True:
                self._step += 1
                self.stepIncreased.emit(self._step)
                time.sleep(0.1)
    
            print "finished..."
    
        def stop(self):
            self._isRunning = False
    
    
    class SimulationUi(QDialog):
        def __init__(self):
            super(SimulationUi, self).__init__()
    
            self.btnStart = QPushButton('Start')
            self.btnStop = QPushButton('Stop')
            self.currentStep = QSpinBox()
    
            self.layout = QHBoxLayout()
            self.layout.addWidget(self.btnStart)
            self.layout.addWidget(self.btnStop)
            self.layout.addWidget(self.currentStep)
            self.setLayout(self.layout)
    
            self.thread = QThread()
            self.thread.start()
    
            self.worker = Worker()
            self.worker.moveToThread(self.thread)
            self.worker.stepIncreased.connect(self.currentStep.setValue)
    
            self.btnStop.clicked.connect(lambda: self.worker.stop())
            self.btnStart.clicked.connect(self.worker.task)
    
            self.finished.connect(self.stop_thread)
    
        def stop_thread(self):
            self.worker.stop()
            self.thread.quit()
            self.thread.wait()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        simul = SimulationUi()
        simul.show()
        sys.exit(app.exec_())
    
    0 讨论(0)
  • 2020-12-14 23:54

    I found out that my original question was actually two questions in one: in order to stop a secondary thread from the main one, you need two things:

    1. Be able to communicate from the main thread down to the secondary thread

    2. Send the proper signal to stop the thread

    I haven't been able to solve (2), but I figured out how to solve (1), which gave me a workaround to my original problem. Instead of stopping the thread, I can stop the thread's processing (the longRunning() method)

    The problem is that a a secondary thread can only respond to signals if it runs its own event loop. A regular Qthread (which is what my code used) does not. It is easy enough, though, to subclass QThread to that effect:

    class MyThread(QThread):
        def run(self):
            self.exec_()
    

    and used self.simulThread = MyThread() in my code instead of the original self.simulThread = Qthread(). This ensures that the secondary thread runs an event loop. That was not enough, though. The longRunning() method needs to have a chance to actually process the event coming down from the main thread. With the help of this SO answer I figured out that the simple addition of a QApplication.processEvent() in the longRunning() method gave the secondary thread such a chance. I can now stop the processing carried out in the secondary thread, even though I haven't figured out how to stop the thread itself.

    To wrap up. My longRunning method now looks like this:

    def longRunning(self):
        while self._step  < self._maxSteps  and self._isRunning == True:
            self._step += 1
            self.stepIncreased.emit(self._step)
            time.sleep(0.1)
            QApplication.processEvents() 
    

    and my GUI thread has these three lines that do the job (in addition to the QThread subclass listed above):

        self.simulThread = MyThread()
        self.simulRunner.moveToThread(self.simulThread)
        self.stopButton.clicked.connect(self.simulRunner.stop)
    

    Comments are welcome!

    0 讨论(0)
  • 2020-12-15 00:00

    You can stop the thread by calling exit() or quit() . In extreme cases, you may want to forcibly terminate() an executing thread. However, doing so is dangerous and discouraged. Please read the documentation for terminate() and setTerminationEnabled() for detailed information.

    src: https://doc.qt.io/qtforpython/PySide2/QtCore/QThread.html

    0 讨论(0)
提交回复
热议问题