首页 > 解决方案 > 在Qt(PySide2)的主线程上调用函数的简单方法

问题描述

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

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

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

标签: python-3.xqtqt5pyside2pyside6

解决方案


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

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

解决方案:

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

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)

这将在内部创建没有任何参数的 aSignal和 a 。Slot但是将Slot在主线程上调用,因为它已使用Qt.AutoConnection(默认)连接并使用moveToThread(...). 为了确保没有函数参数由于垃圾收集器而丢失,类的父级临时设置为QGuiApplication实例(如果您不依赖QGuiApplication. AnyQObject作为父级,则可能需要更改此设置)。

下面是一个关于如何使用这个类的例子:

InvokeMethod(lambda: print("hello"))

推荐阅读