在 Qt (PySide2) 的主线程上调用函数的简单方法

Easy way to call a function on the main thread in Qt (PySide2)

有没有简单的方法从任何其他线程或 QThread 调用主线程中的函数或方法?

我听说 Slots 和 Signals 可以用作线程和主线程之间的代理,但是每次都创建这样的代理感觉工作量太大我想将数据传输到我的主线程。

(我的回答描述了一个非常普遍的方法来实现这个所以我不会在这里提供一个“最小”的例子,你可以看看答案。)

Qt 有一个名为 invokeMethod (https://doc.qt.io/qt-5/qmetaobject.html#invokeMethod) 的函数,可用于通过使用 Qt.QueuedConnection 连接类型调用主线程上的方法。

但是在 PySide 中如果你想调用带参数的函数,这将不起作用!

解法:

因此,为了让 PySide 具有类似甚至更简单的功能,我编写了这个 class 每次您想在主线程上 运行 函数或方法时都可以使用它:

from typing import Callable

from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication


class InvokeMethod(QObject):
    def __init__(self, method: Callable):
        """
        Invokes a method on the main thread. Taking care of garbage collection "bugs".
        """
        super().__init__()

        main_thread = QGuiApplication.instance().thread()
        self.moveToThread(main_thread)
        self.setParent(QGuiApplication.instance())
        self.method = method
        self.called.connect(self.execute)
        self.called.emit()

    called = Signal()

    @Slot()
    def execute(self):
        self.method()
        # trigger garbage collector
        self.setParent(None)

这将在内部创建不带任何参数的 SignalSlot。然而,Slot 将在主线程上调用,因为它已使用 Qt.AutoConnection(默认)连接并使用 moveToThread(...) 移动到主线程。 为确保没有函数参数因垃圾收集器而丢失,class 的 parent 临时设置为 QGuiApplication 实例(如果您不这样做,则可能需要更改它依靠 QGuiApplication。任何 QObject 都可以,因为 parent).

这里有一个如何使用这个的例子class:

InvokeMethod(lambda: print("hello"))