【问题标题】:Qthread is still working when i close gui on python pyqt当我在 python pyqt 上关闭 gui 时,Qthread 仍在工作
【发布时间】:2017-01-29 00:23:33
【问题描述】:

我的代码有线程,但是当我关闭 gui 时,它仍然在后台运行。我怎样才能停止线程?有什么停止(),关闭()? 我不使用信号,插槽?我必须使用这个吗?

from PyQt4 import QtGui, QtCore
import sys
import time
import threading

class Main(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.kac_ders=QtGui.QComboBox()
        self.bilgi_cek=QtGui.QPushButton("Save")
        self.text=QtGui.QLineEdit()
        self.widgetlayout=QtGui.QFormLayout()
        self.widgetlar=QtGui.QWidget()
        self.widgetlar.setLayout(self.widgetlayout)
        self.bilgiler=QtGui.QTextBrowser()
        self.bilgi_cek.clicked.connect(self.on_testLoop)
        self.scrollArea = QtGui.QScrollArea()
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setWidget(self.widgetlar)
        self.analayout=QtGui.QVBoxLayout()
        self.analayout.addWidget(self.text)
        self.analayout.addWidget(self.bilgi_cek)
        self.analayout.addWidget(self.bilgiler)
        self.centralWidget=QtGui.QWidget()
        self.centralWidget.setLayout(self.analayout)
        self.setCentralWidget(self.centralWidget)

    def on_testLoop(self):
        self.c_thread=threading.Thread(target=self.kontenjan_ara)
        self.c_thread.start()

    def kontenjan_ara(self):

        while(1):
                self.bilgiler.append(self.text.text())
                time.sleep(10)


app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()

【问题讨论】:

  • 此代码不是线程安全的。不允许您从辅助线程访问 GUI 对象(如 self.text)。您的目标应该是重构您的代码以使用 QThread 和信号和插槽,或者只使用一个线程和一个 QTimer(因为大部分时间您的线程都在休眠)

标签: python python-3.x pyqt pyqt4 qthread


【解决方案1】:

一些事情:

  1. 您不应该从主线程外部调用 GUI 代码。 GUI 元素不是线程安全的。 self.kontenjan_ara 更新和读取 GUI 元素,它不应该是您的 thread 的目标。

  2. 在几乎所有情况下,您都应该使用QThreads 而不是 python 线程。它们与 Qt 中的事件和信号系统很好地集成。

如果您只想每隔几秒运行一次,可以使用QTimer

def __init__(self, parent=None):
    ...
    self.timer = QTimer(self)
    self.timer.timeout.connect(self.kontenjan_ara)
    self.timer.start(10000)

def kontenjan_ara(self):
    self.bilgiler.append(self.text.text())

如果您的线程操作在计算上更加复杂,您可以创建一个工作线程并使用信号在工作线程和主 GUI 线程之间传递数据。

class Worker(QObject):

    work_finished = QtCore.pyqtSignal(object)

    @QtCore.pyqtSlot()
    def do_work(self):
        data = 'Text'
        while True:
            # Do something with data and pass back to main thread
            data = data + 'text'
            self.work_finished.emit(data)
            time.sleep(10)


class MyWidget(QtGui.QWidget):

    def __init__(self, ...)
        ...
        self.worker = Worker()
        self.thread = QtCore.QThread(self)
        self.worker.work_finished.connect(self.on_finished)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.do_work)
        self.thread.start()

    @QtCore.pyqtSlot(object)
    def on_finished(self, data):
        self.bilgiler.append(data)
        ...

当主线程退出事件循环时,Qt会自动杀死所有子线程。

【讨论】:

  • 这比我的回答要好得多,也更正确,但我认为 OP 希望在每次单击按钮时启动一个工作人员,并希望该工作人员从 GUI 中读取,而不仅仅是异步通知它。
【解决方案2】:

我选择稍微改写一下这个答案,因为我未能正确看待问题的上下文。正如其他答案和 cmets 所说,您的代码缺乏线程安全性。

解决此问题的最佳方法是尝试真正“在线程中”思考,限制自己仅使用位于同一线程中的对象或称为“线程安全”的函数。

加入一些信号和槽会有所帮助,但也许您想回想一下您最初的问题。在您当前的代码中,每次按下按钮时,会启动一个新线程,它将每 10 秒做两件事: - 阅读self.text的一些文字 - 附加到self.bilgiler

这两个操作都是非线程安全的,必须从拥有这些对象的线程(主线程)中调用。您想让工作线程“调度和等待”读取和附加操作,而不是简单地“执行”它们。

我建议使用其他答案(线程停止问题通过使用与 Qt 事件循环良好集成的适当 QThreads 自动修复),这将使您使用更简洁的方法,与 Qt 更集成。

您可能还想重新考虑您的问题,因为也许有一种更简单的方法可以解决您的问题,例如:每次单击 bilgi_cek 时不生成线程,或者使用 Queue 对象,以便您的工作人员完全不可知论您的 GUI,并且仅使用线程安全对象与之交互。

祝你好运,如果我造成任何混乱,请见谅。我的原始答案仍然可用here。我认为将另一个答案标记为该问题的有效答案是明智的。

【讨论】:

  • 谢谢,它成功了,但我有一些问题。当我关闭 gui 时,self.close 是如何设置的? self.closed = threading.Event() 当我关闭 gui 时,该行是否理解。一定是信号,不是吗?
  • 这是通过覆盖closeEvent 并调用self.closed.set() 来处理的。请注意,另一个答案可能是解决您问题的更好方法,因为它直接使用 Qt 功能在循环停止时使线程死亡,并且还因为它还修复了代码中缺乏线程安全性的问题。
  • 其实我真的没有 pyqt 的经验和能力,你的解决方案解决了我的问题,但是现在,出现了一些新问题,我的代码不像那个例子那样简单,我的代码中有两个线程。现在,我什至不知道如何调用变量而不是其他类等。其他解决方案的第一部分有效,但我无法理解第二部分。我如何与我的代码同步,我只是想它。非常感谢您的关心,非常感谢您一次又一次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 2017-07-31
  • 2021-05-15
  • 1970-01-01
  • 1970-01-01
  • 2018-01-21
  • 2014-03-25
相关资源
最近更新 更多