Answer a question

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 of NOT subclassing Qthread, but rather vreating a QObject and then moving it to a QThread. Below if a working example. I can start the GUI and the Qthread and I can have the latter update the GUI. However, I cannot stop it. I tried several methods for qthread (quit(), exit(), and even terminate()) to no avail. Help greatly appreciated.

Here is the complete code:

import time, sys
from PyQt4.QtCore  import *
from PyQt4.QtGui import * 

class SimulRunner(QObject):
    'Object managing the simulation'

    stepIncreased = pyqtSignal(int, name = 'stepIncreased')
    def __init__(self):
        super(SimulRunner, self).__init__()
        self._step = 0
        self._isRunning = True
        self._maxSteps = 20

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

    def stop(self):
        self._isRunning = False

class SimulationUi(QDialog):
    'PyQt interface'

    def __init__(self):
        super(SimulationUi, self).__init__()

        self.goButton = QPushButton('Go')
        self.stopButton = QPushButton('Stop')
        self.currentStep = QSpinBox()

        self.layout = QHBoxLayout()
        self.layout.addWidget(self.goButton)
        self.layout.addWidget(self.stopButton)
        self.layout.addWidget(self.currentStep)
        self.setLayout(self.layout)

        self.simulRunner = SimulRunner()
        self.simulThread = QThread()
        self.simulRunner.moveToThread(self.simulThread)
        self.simulRunner.stepIncreased.connect(self.currentStep.setValue)


        self.stopButton.clicked.connect(simulThread.qui)  # also tried exit() and terminate()
        # also tried the following (didn't work)
        # self.stopButton.clicked.connect(self.simulRunner.stop)
        self.goButton.clicked.connect(self.simulThread.start)
        self.simulThread.started.connect(self.simulRunner.longRunning)
        self.simulRunner.stepIncreased.connect(self.current.step.setValue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    simul = SimulationUi()
    simul.show()
    sys.exit(app.exec_())

Answers

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!

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐