QThread完成信号在工作线程完成后未被发射。

3

I have the following code:

import time

from PyQt5.QtCore import QThread, QObject
from PyQt5.QtWidgets import QWidget, QApplication


class Worker(QObject):

    def run(self):
        time.sleep(1)
        print("Worker is finished")


class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.show()

        self.thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.finished.connect(self.on_thread_finished)
        self.thread.start()

    def on_thread_finished(self):
        print("Thread finished")
        self.thread.deleteLater()


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    app.exec_()

运行它,它会显示窗口,打印 Worker is finished,没有更多的内容。这很奇怪。在我看来,当工作线程结束时,线程也应该结束,这意味着应该调用 on_thread_finished 方法并打印 Thread finished。但事实并非如此。为什么?

2个回答

3
当你使用 moveToThread 而不是重新实现 QThread.run 时,该线程将启动自己的事件循环,等待显式调用 exit/quit。只有在事件循环停止(或者一旦 run 返回)后才会发出 finished 信号。处理此情况的通常方法是从工作线程发出自定义信号,并将其连接到线程的 quit 槽。(NB:跨线程信号槽连接保证是线程安全的)。
因此,只需进行以下更改,您的示例即可按预期工作。
class Worker(QObject):
    finished = QtCore.pyqtSignal()

    def run(self):
        time.sleep(1)
        print("Worker is finished")
        self.finished.emit()


class MainWindow(QWidget):    
    def __init__(self):
        ...
        self.worker.moveToThread(self.thread)
        self.worker.finished.connect(self.thread.quit)

2
当工作线程完成时,线程也应该结束。
事实并非如此。在使用通常的信号/槽机制时,您的Worker::run方法作为槽被调用后,QThread将继续正常处理事件。
如果您想在Worker::run完成后终止QThread,则需要明确告知它这样做...
import time

from PyQt5.QtCore import Qt, QThread, QObject
from PyQt5.QtWidgets import QWidget, QApplication


class Worker(QObject):

    # Constructor accepts the QThread as a parameter and stashes it
    # for later use.
    def __init__(self, thread):
        super(Worker, self).__init__()
        self.m_thread = thread
    def run(self):
        time.sleep(1)
        print("Worker is finished")

        # We're done so ask the `QThread` to terminate.
        if self.m_thread:
            self.m_thread.quit()


class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.show()

        self.thread = QThread()

        # Pass the QThread to the Worker's ctor.
        self.worker = Worker(self.thread)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.finished.connect(self.on_thread_finished)
        self.thread.start()

    def on_thread_finished(self):
        print("Thread finished")
        self.thread.deleteLater()


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    app.exec_()

这种方法不太优雅,但是可以传达思想。

一个更简单的替代方案是直接使用QThread::currentThread(),这样你的Worker::run方法变成了...

class Worker(QObject):
    def run(self):
        time.sleep(1)
        print("Worker is finished")

        # We're done so ask the `QThread` to terminate.
        QThread.currentThread().quit()

QThread.currentThread().quit() 给我返回致命的 Python 错误:Aborted。 - pippo1980
QThread:线程仍在运行时被销毁 我的IDE外出现了“Aborted (core dumped)”错误 - pippo1980
@pippo1980,我这边测试这段代码是正常的。如果你遇到特定的问题,请开一个新的问题来咨询。 - G.M.
我的示例在工作线程中使用了一个循环,所以有时我只是停止线程并关闭主窗口,但如果我不退出线程并关闭主窗口,就会出现错误,对我来说更好的方法是从工作线程向主线程发出完成信号。 - pippo1980
无法编辑您的答案,队列已满。对于像我这样的新手来说,指出QThread.currentThread()返回管理当前执行线程的QThread指针会很好。 - pippo1980

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接