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)
我有以下代码:
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)