Python - PyQt:重新启动已完成的线程

Posted

技术标签:

【中文标题】Python - PyQt:重新启动已完成的线程【英文标题】:Python - PyQt: restarting a thread which is finished 【发布时间】:2018-10-09 08:12:46 【问题描述】:

我有 pyqt 界面,如果按下按钮,它会启动 QThread。线程运行,完成后我可以再次启动它,到这里没有问题。 我现在为线程的连续操作添加了一个复选框。如果选中复选框,则按钮保持按下状态,并且必须再次按下按钮以停止线程。

显然在停止后,线程已正确完成(也使用isRunning() 方法检查)但如果我尝试在连续操作中再次启动它,程序将崩溃。如果我在关闭连续操作的情况下重新启动它,则不会发生这种情况,但在这种情况下,线程会启动和停止两次。

如何解释这种行为?有没有办法让它按预期工作?

一个例子:

import sys
from PyQt5 import QtCore
import PyQt5.QtWidgets as QtW
from PyQt5.QtCore import QThread, pyqtSlot, pyqtSignal
import time

class MyWindow(QtW.QMainWindow):

  def __init__(self):
    super().__init__()
    self.setWindowTitle('MyWindow')
    self._main = QtW.QWidget()
    self.setCentralWidget(self._main) 
    self.button = QtW.QPushButton('Do it', self)
    self.button.clicked.connect(self.do)
    self.contincheck = QtW.QCheckBox("Continuous")
    self.contincheck.clicked.connect(self.continuous_doing)
    self.continuous = False
    self.layout = QtW.QGridLayout(self._main)
    self.layout.addWidget(self.button,0,0)
    self.layout.addWidget(self.contincheck,1,0)
    self.setLayout(self.layout)
    self.show()

  def continuous_doing(self):
    if self.contincheck.isChecked():
      self.button.setCheckable(True)
      self.continuous = True
    else:
      self.button.setCheckable(False)
      self.continuous = False

  def do(self):
    if self.button.isCheckable() and not self.button.isChecked():
        self.button.setText('Do it')
        self.button.clicked.connect(self.do)
        self.contincheck.setEnabled(True)
    else:
        self.mythread = MyThread(self.continuous)
        if self.button.isCheckable() and self.button.isChecked():
            self.button.setText('Stop doing that')
            self.contincheck.setDisabled(True)
            self.button.clicked.connect(self.mythread.stop)
        self.mythread.finished.connect(self.thread_finished)
        self.mythread.signal.connect(self.done)
        self.mythread.start()

  @pyqtSlot(int)
  def done(self, i):
      print('done it', i)

  @pyqtSlot()
  def thread_finished(self):
      print('thread finished')



class MyThread(QThread):
    signal = pyqtSignal(int)

    def __init__(self, continuous):
        QThread.__init__(self)
        self._stopped = True
        self.continuous = continuous
        self.i = 0

    def __del__(self):
        self.wait()

    def stop(self):
        self._stopped = True

    def run(self):
        self._stopped = False
        while True:
            self.signal.emit(self.i)
            if self._stopped:
                break
            if self.continuous: time.sleep(2)
            else: break


if __name__ == '__main__':
    app = QtCore.QCoreApplication.instance()
    if app is None:
        app = QtW.QApplication(sys.argv)
    mainGui = MyWindow()
    mainGui.show()
    app.aboutToQuit.connect(app.deleteLater)
    app.exec_()

【问题讨论】:

【参考方案1】:

问题是您正在创建一个新线程而不是重用现有线程,在下面的示例中,我将向您展示如何正确执行此操作:

import sys
from PyQt5 import QtCore, QtWidgets

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('MyWindow')
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main) 
        self.button = QtWidgets.QPushButton('Do it')
        self.button.clicked.connect(self.do)

        self.contincheck = QtWidgets.QCheckBox("Continuous")
        self.contincheck.clicked.connect(self.continuous_doing)
        self.continuous = False
        layout = QtWidgets.QGridLayout(self._main)
        layout.addWidget(self.button, 0, 0)
        layout.addWidget(self.contincheck, 1, 0)

        self.mythread = MyThread(self.continuous, self)
        self.mythread.finished.connect(self.thread_finished)
        self.button.clicked.connect(self.mythread.stop)
        self.mythread.signal.connect(self.done)

    def continuous_doing(self):
        self.button.setCheckable(self.contincheck.isChecked())
        self.continuous = self.contincheck.isChecked()

    def do(self):
        if self.button.isCheckable() and not self.button.isChecked():
            self.button.setText('Do it')
            self.contincheck.setEnabled(True)
        else:
            self.mythread.continuous = self.continuous
            if self.button.isCheckable() and self.button.isChecked():
                self.button.setText('Stop doing that')
                self.contincheck.setDisabled(True)

            self.mythread.start()

    @QtCore.pyqtSlot(int)
    def done(self, i):
        print('done it', i)

    @QtCore.pyqtSlot()
    def thread_finished(self):
        print('thread finished')


class MyThread(QtCore.QThread):
    signal = QtCore.pyqtSignal(int)

    def __init__(self, continuous=False, parent=None):
        super(MyThread, self).__init__(parent)
        self._stopped = True
        self.continuous = continuous
        self.i = 0

    def __del__(self):
        self.wait()

    def stop(self):
        self._stopped = True

    def run(self):
        self._stopped = False
        while True:
            self.signal.emit(self.i)
            if self._stopped:
                break
            if self.continuous: 
                QtCore.QThread.sleep(2)
            else: 
                break


if __name__ == '__main__':
    app = QtCore.QCoreApplication.instance()
    if app is None:
        app = QtWidgets.QApplication(sys.argv)
    mainGui = MyWindow()
    mainGui.show()
    app.aboutToQuit.connect(app.deleteLater)
    app.exec_()

【讨论】:

太棒了!感谢您的宝贵时间!

以上是关于Python - PyQt:重新启动已完成的线程的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中使用 PyQT 应用程序的主线程中的回调方法启动一个新线程

PyQt5 QDialog 在后续线程中

后续线程中的PyQt5 QDialog

PyQt5中如何与线程通信并等待结果

PyQt:定时器不能从另一个线程启动

完成后如何自动退出PyQT QThread?