Как остановить QThread из графического интерфейса

Это вопрос, продолжающий предыдущий, который я разместил ранее. Проблема в том, как остановить (завершить | выйти | выйти) QThread из графического интерфейса пользователя при использовании рекомендованного метода НЕ подклассифицировать Qthread, а скорее создать QObject и затем переместить его в QThread. Ниже рабочий пример. Я могу запустить графический интерфейс и Qthread, и последний обновит графический интерфейс. Однако я не могу это остановить. Я пробовал несколько методов для qthread (quit (), exit () и даже terminate ()), но безрезультатно. Помощь очень ценится.

Вот полный код:

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_())

person stefano    schedule 26.04.2013    source источник
comment
[qt-project.org/doc/qt-4.8/qthread. html # quit] действительно должно быть подходящим вариантом ... попробуйте еще раз, может быть, с некоторой элементарной отладкой вокруг вызова функции?   -  person tmpearce    schedule 27.04.2013


Ответы (3)


Я знаю это давно, но я только что наткнулся на ту же проблему.

Я тоже искал подходящий способ сделать это. Наконец, это было легко. При выходе из приложения задача должна быть остановлена, а поток должен быть остановлен, вызывая его метод выхода. См. Метод stop_thread внизу. И нужно дождаться завершения потока. В противном случае вы получите сообщение QThread: Destroyed while thread is still running 'при выходе.

(Я также изменил свой код, чтобы использовать 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_())
person maggie    schedule 21.08.2015
comment
Небольшое улучшение: может быть лучше использовать requestInterruption () и isInterruptionRequested (), чем собственный флаг. - person sorbet; 04.01.2020

Я обнаружил, что мой первоначальный вопрос состоял из двух вопросов в одном: чтобы остановить вторичный поток от основного, вам понадобятся две вещи:

  1. Иметь возможность связываться из основного потока вниз со второстепенным потоком.

  2. Отправьте правильный сигнал, чтобы остановить поток

Мне не удалось решить (2), но я понял, как решить (1), что дало мне обходной путь к моей исходной проблеме. Вместо остановки потока я могу остановить обработку потока (метод longRunning())

Проблема в том, что вторичный поток может реагировать на сигналы только в том случае, если он запускает собственный цикл обработки событий. Обычный Qthread (который использовал мой код) - нет. Тем не менее, достаточно просто создать подкласс QThread для этого:

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

и использовал self.simulThread = MyThread() в моем коде вместо исходного self.simulThread = Qthread(). Это гарантирует, что вторичный поток запускает цикл событий. Однако этого было недостаточно. Метод longRunning() должен иметь возможность фактически обработать событие, исходящее из основного потока. С помощью этот SO-ответ Я понял, что простое добавление QApplication.processEvent() в метод longRunning() дало вторичному потоку такой шанс. Теперь я могу остановить обработку, выполняемую во вторичном потоке, хотя я не понял, как остановить сам поток.

Обернуть. Мой метод longRunning теперь выглядит так:

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() 

и в моем потоке графического интерфейса есть эти три строки, которые выполняют эту работу (в дополнение к подклассу QThread, указанному выше):

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

Комментарии приветствуются!

person stefano    schedule 29.04.2013
comment
У меня было много проблем с этим, затем я создал собственное решение, проверьте его: github .com / u2ros / python-qt-multithreading. - person U2ros; 11.11.2017

Вы можете остановить поток, вызвав exit () или quit (). В крайних случаях вы можете захотеть принудительно завершить () выполняющийся поток. Однако делать это опасно и не рекомендуется. Пожалуйста, прочтите документацию для terminate () и setTerminationEnabled () для получения подробной информации.

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

person AIT DOUKALI ZAKARIAE    schedule 31.07.2020