QMetaObject::invokeMethod 找不到带参数的方法

6 投票
1 回答
4944 浏览
提问于 2025-04-18 05:48

这是对QMetaObject::invokeMethod找不到方法这个问题的后续讨论。调用没有参数的方法是可以成功的。但如果把问题扩展到带参数的方法,就又出现了失败。

下面是一个用Python写的示例脚本:

from PySide import QtCore

class Example(QtCore.QObject):
    def __init__(self):
        super().__init__()

    @QtCore.Slot()
    def dup(self):
        beep('dup-class')

    @QtCore.Slot(str)
    def beep(self, text):
        print(text)

@QtCore.Slot()
def dup(self):
    beep('dup-local')

@QtCore.Slot(str)
def beep(text):
    print(text)

if __name__ == '__main__':
    QtCore.QMetaObject.invokeMethod(None, 'dup')
    QtCore.QMetaObject.invokeMethod(None, 'beep', QtCore.Qt.AutoConnection, QtCore.QGenericArgument('text', 'beep-local'))

    print('now some classy trials')
    t = Example()
    QtCore.QMetaObject.invokeMethod(t, 'dup')
    QtCore.QMetaObject.invokeMethod(t, 'beep', QtCore.Qt.AutoConnection, QtCore.QGenericArgument('text', 'beep-class'))
    QtCore.QMetaObject.invokeMethod(t, 'beep', QtCore.Qt.AutoConnection, QtCore.QGenericArgument('self', t), QtCore.QGenericArgument('text', 'beep-class-b'))

在Windows 7和Ubuntu 14.04上,使用PySide 1.2.1和Python 3.3的输出结果是:

now some classy trials
dup-class
QMetaObject::invokeMethod: No such method Example::beep(text)
QMetaObject::invokeMethod: No such method Example::beep(self,text)

这意味着调用本地方法的invokeMethod失败了,结果没有任何提示。只有调用Example:dup()得到了预期的输出。尝试调用Example:beep(str)却失败了,虽然错误信息中给出的函数签名实际上应该是存在的。

我在PySide邮件列表上提过这个问题的早期版本,但没有人回答。

问题:如何在Python的Qt绑定中(最好是在PySide中)使用QMetaObject::invokeMethod调用带参数的本地和类方法?

补充说明:如果知道Signal:emit(...)QtCore.QTimer.singleShot(0, ...)的底层实现,这也可能有帮助。毕竟这些不同的方法有着非常相似的效果。


补充2:

使用'QString'作为参数名时,警告信息消失了,但Python整体却因为段错误而失败。这可能是PySide的一个实现错误。规则似乎是,在invokeMethod中必须给出Qt-C++类型的参数,而在Slots中则使用Python类型。

from PySide import QtCore

class Example(QtCore.QObject):
    def __init__(self):
        super().__init__()

    @QtCore.Slot(str)
    def beep(self, text='default'):
        print(text)

if __name__ == '__main__':
    app = QtCore.QCoreApplication([])

    e = Example()
    QtCore.QMetaObject.invokeMethod(e, 'beep', QtCore.Qt.AutoConnection, QtCore.QGenericArgument('QString', 'beep'))

    QtCore.QTimer.singleShot(1000, app.quit)
    app.exec_()

1 个回答

6

对于仍然感兴趣的人来说:

从版本1.2.4开始,PySide有一些问题,它没有正确处理 Q_ARGQ_RETURN_ARG 这两个宏。相反,它错误地处理了 QGenericArgumentQGenericReturnArgument 这两个类,而这两个类其实是内部帮助类,不应该直接使用。因此,当你尝试用参数调用槽函数时,通常会导致程序崩溃。

相比之下,PyQt 正确地 处理了宏,而不是类,所以它没有遇到同样的问题。

撰写回答