QThread.finished 工作人员完成后不发出信号

QThread.finished signal isn't emitted after worker finishes

我有以下代码:

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

运行 它,它显示 window,打印 Worker is finished,仅此而已。那真是怪了。恕我直言,当工人完成时,线程也应该完成,这意味着应该调用 on_thread_finished 方法并打印 Thread finished 。但事实并非如此。为什么?

when the worker is finished, the thread should be finished too

事情不是这样的。您的 Worker::run 方法正在通过通常的 signal/slot 机制作为 slot 调用,之后 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_()

非常'inelegant'但它表达了这个想法。

想到的一个更简单的替代方法是简单地使用 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()

当您使用 moveToThread 而不是重新实现 QThread.run 时,线程将启动自己的 event-loop,它将等待 exit/quit 被显式调用。 finished 信号仅在 event-loop 停止后发出(and/or 一次 run returns)。处理这种情况的通常方法是从 worker 发出自定义信号,并将其连接到线程的 quit 插槽。 (注意:cross-thread signal-slot 连接保证为 thread-safe)。

因此,您的示例将按预期工作并进行以下更改:

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)