Home > Back-end >  Cancel QThread in PyQt5
Cancel QThread in PyQt5

Time:10-15

I have a GUI in PyQt5, which starts a QThread that reads from a serial port. The thread does quit, when it read all the data, but I want to be able to stop it when I click on a stop button. How do I do that? Here is the basic code:

    # ...
    class Worker(QObject):
        finished = pyqtSignal()
        progress = pyqtSignal(list)

    def __init__(self):
        QObject.__init__(self)
        self._Reader = Reader()
        self._Reader.progress = self.progress
        self._Reader.finished = self.finished
    def run(self):
        self._Reader.read()
    
    class Ui(QtWidgets.QMainWindow):
        # ...
        def startClicked(self):
            self.thread = QThread()
            self.worker = Worker()
            self.worker.moveToThread(self.thread)

            self.thread.started.connect(self.worker.run)
            self.worker.finished.connect(self.thread.quit)
            self.worker.finished.connect(self.worker.deleteLater)
            self.worker.finished.connect(self.workerFinished)
            self.thread.finished.connect(self.thread.deleteLater)
            self.worker.progress.connect(self.reportProgress)
            self.thread.start()
        def stopClicked(self):
            # How do I stop the thread?
            pass


CodePudding user response:

when managing threads you can do, as states in the doc here: https://doc.qt.io/qt-5/qthread.html

You can stop the thread by calling exit() or quit().

https://doc.qt.io/qt-5/qthread.html#exit

exit:

Tells the thread's event loop to exit with a return code.

After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.

By convention, a returnCode of 0 means success, any non-zero value indicates an error.

https://doc.qt.io/qt-5/qthread.html#quit

quit:

Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).

This function does nothing if the thread does not have an event loop.

CodePudding user response:

I assume that you read data in some data processing loop. If this assumption is wrong, then the following is not valid, of course.

You cannot call secondary thread's quit() directly from the main thread and expect that the secondary thread will process it immediately and quit the thread. The reason is that the thread is busy reading the data in the data processing loop. So you need to break the data processing loop in the secondary thread to make the event loop idle.

(Btw. do not confuse the data processing loop with the event loop. Data processing loop is the one which you have written yourself to read data from the port. The event loop is the loop created by Qt automatically after you called QThread::start() and which is processing events, signals and slots in the secondary thread.)

In order to break the data processing loop, you need to do two things:

  1. call QThread::requestInterruption() from the main thread as response to some "Abort" button having been pressed (do not worry about thread safety, requesting interruption is thread safe/atomic)
  2. within the loop in the secondary thread you need to periodically check QThread::isInterruptionRequested(), and if this returns true, then break the loop and emit worker's finished() signal

Once you broke from the loop in the secondary thread, the event loop in the secondary thread becomes available for processing signals sent from the main thread.

I can see in your code that worker's finished() signal is connected to QThread::quit(). So emitting finished() from the secondary thread (after you broke from the data processing loop) will call thread's quit() which will be processed by the secondary thread's event loop (which is now idle) and it will quit the event loop and subsequently the thread and if you have connected everything correctly it will delete the worker and the thread. (though I have not checked this part of your code)

  • Related