在 `moveToThread` 之后,工人的槽中没有收到 PySide2 信号

PySide2 signals aren't received in slots of a worker after `moveToThread`

在工人 class moveToThread 之后,我无法让插槽接收信号。 目标是 worker 内部的槽将在另一个线程上执行。

这是我的测试人员class:

class DbWorker(QObject):
    def __init__(self):
        super().__init__()
        logger.info(f"{threading.get_ident()}: DbWorker instantiated...")

    @Slot(str)
    def test_slot_str(self, test_text: str):
        logger.info(f"{threading.get_ident()}: test_slot_str fired. Got Message '{test_text}'")

    @Slot()
    def test_slot_empty(self):
        logger.info(f"{threading.get_ident()}: test_slot_empty fired ")

    @Slot()
    def process(self):
        logger.info(f"{threading.get_ident()}: Process slot activated!")

当我执行这个时:

# Signals must inherit QObject
class Communicate(QObject):
    signal_str = Signal(str)
    signal_empty = Signal()


def moveToThreadTest():
    logger.info(f"{threading.get_ident()}: main started...")
    worker = DbWorker()
    communicate = Communicate()
    my_thread = QThread()
    worker.moveToThread(my_thread)

    communicate.signal_str.connect(worker.test_slot_str)
    communicate.signal_empty.connect(worker.test_slot_empty)
    my_thread.started.connect(worker.process)
    my_thread.start()

    communicate.signal_str.emit("test")
    communicate.signal_empty.emit()
    logger.info(f"{threading.get_ident()}: main thread end...")

我得到的输出是:

 - INFO     [db_worker.py:35 -     moveToThreadTest() ]   13824: main started... 
 - INFO     [db_worker.py:12 -             __init__() ]   13824: DbWorker instantiated... 
 - INFO     [db_worker.py:48 -     moveToThreadTest() ]   13824: main thread end... 
 - INFO     [db_worker.py:25 -              process() ]   20776: Process slot activated! 

所以连接到 my_thread.started 的插槽设法从另一个线程执行。但是 none o 其他插槽也是如此。此外,值得指出的是,只有当我 运行 这段代码没有调试时,才会打印最后一行。如果我 运行 在调试模式下,我得到这个:

 - INFO     [db_worker.py:35 -     moveToThreadTest() ]   25128: main started... 
 - INFO     [db_worker.py:12 -             __init__() ]   25128: DbWorker instantiated... 
 - INFO     [db_worker.py:48 -     moveToThreadTest() ]   25128: main thread end... 

我试着看看我是否以正确的方式将信号连接到插槽,所以我 运行 相同的示例没有将工作线程移动到线程,如下所示:

def withoutMoveToThread():
    logger.info(f"{threading.get_ident()}: main started...")
    communicate = Communicate()
    worker = DbWorker()
    communicate.signal_str.connect(worker.test_slot_str)
    communicate.signal_empty.connect(worker.test_slot_empty)
    communicate.signal_str.emit("Signal str")
    communicate.signal_empty.emit()
    logger.info(f"{threading.get_ident()}: main finished...")

它似乎运行良好:

 - INFO     [db_worker.py:52 -  withoutMoveToThread() ]   6052: main started... 
 - INFO     [db_worker.py:12 -             __init__() ]   6052: DbWorker instantiated... 
 - INFO     [db_worker.py:16 -        test_slot_str() ]   6052: test_slot_str fired. Got Message 'Signal str' 
 - INFO     [db_worker.py:20 -      test_slot_empty() ]   6052: test_slot_empty fired  
 - INFO     [db_worker.py:59 -  withoutMoveToThread() ]   6052: main finished... 

我不明白我做错了什么,为什么我的插槽没有收到连接的信号。请帮助我了解我做错了什么。

这是完整的示例文件:

import threading

from PySide2 import QtCore
from PySide2.QtCore import QObject, Slot, Signal, QThread

from logger import logger


class DbWorker(QObject):
    def __init__(self):
        super().__init__()
        logger.info(f"{threading.get_ident()}: DbWorker instantiated...")

    @Slot(str)
    def test_slot_str(self, test_text: str):
        logger.info(f"{threading.get_ident()}: test_slot_str fired. Got Message '{test_text}'")

    @Slot()
    def test_slot_empty(self):
        logger.info(f"{threading.get_ident()}: test_slot_empty fired ")

    @Slot()
    def process(self):
        # print("Process Activated!")
        logger.info(f"{threading.get_ident()}: Process slot activated!")


# Signals must inherit QObject
class Communicate(QObject):
    signal_str = Signal(str)
    signal_empty = Signal()


def moveToThreadTest():
    logger.info(f"{threading.get_ident()}: main started...")
    worker = DbWorker()
    communicate = Communicate()
    my_thread = QThread()
    worker.moveToThread(my_thread)

    communicate.signal_str.connect(worker.test_slot_str)
    communicate.signal_empty.connect(worker.test_slot_empty)
    my_thread.started.connect(worker.process)
    my_thread.start()

    communicate.signal_str.emit("test")
    communicate.signal_empty.emit()
    logger.info(f"{threading.get_ident()}: main thread end...")


def withoutMoveToThread():
    logger.info(f"{threading.get_ident()}: main started...")
    communicate = Communicate()
    worker = DbWorker()
    communicate.signal_str.connect(worker.test_slot_str)
    communicate.signal_empty.connect(worker.test_slot_empty)
    communicate.signal_str.emit("Signal str")
    communicate.signal_empty.emit()
    logger.info(f"{threading.get_ident()}: main finished...")


if __name__ == '__main__':
    withoutMoveToThread()

信号需要事件循环才能工作,在您的情况下,您必须创建一个 QCoreApplication:

def moveToThreadTest():
    app = QCoreApplication.instance()
    if app is None:
        app = QCoreApplication()
    logger.info(f"{threading.get_ident()}: main started...")
    worker = DbWorker()
    communicate = Communicate()
    my_thread = QThread()
    worker.moveToThread(my_thread)

    communicate.signal_str.connect(worker.test_slot_str)
    communicate.signal_empty.connect(worker.test_slot_empty)
    my_thread.started.connect(worker.process)
    my_thread.start()

    communicate.signal_str.emit("test")
    communicate.signal_empty.emit()
    logger.info(f"{threading.get_ident()}: main thread end...")

    app.exec_()

然后将 threading.get_ident() 更改为 threading.current_thread() 你会得到:

INFO:140406642902848: main started...
INFO:<_MainThread(MainThread, started 140406642902848)>: DbWorker instantiated...
INFO:140406642902848: main thread end...
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: Process slot activated!
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: test_slot_str fired. Got Message 'test'
INFO:<_DummyThread(Dummy-1, started daemon 140406565365312)>: test_slot_empty fire

在您的初始情况下(部分有效)碰巧​​有时间创建线程,但由于函数立即终止,因此对象也被销毁。