Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QMetaObject::invokeMethod doesn't find methods with parameters

This is a follow up of QMetaObject::invokeMethod doesn't find the method. Invoking a method without paramters works. But extending the previous question to methods with parameters brings me back to failure again.

See the following example script in 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'))

The output with PySide 1.2.1 and Python 3.3 on Windows 7 and Ubuntu 14.04 as well is:

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

This means that the invokeMethod calls to local methods failed silently. Only the call to Example:dup() gave the expected output. The two trials to get Example:beep(str) to work failed, although the failure messages give method signatures that actually should exist.

I posed an earlier version of this question on the PySide mailing list but it wasn't answered.

Question: How to make QMetaObject::invokeMethod calling local and class method with parameters in the Python Qt bindings (preferably in PySide)?

edit: By the way: if one knows what Signal:emit(...) or QtCore.QTimer.singleShot(0, ...) do under the hood, that could help here too. After all all these different approaches have very similar effects.


edit2:

With 'QString' as parameter name the warning messages disappear but Python fails as a whole with segfaults. It may be an implementation bug of PySide. The rule seems to be that one has to give the Qt-C++ types of the parameters in invokeMethod and the Python types in the Slots.

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_()
like image 735
Trilarion Avatar asked May 08 '14 10:05

Trilarion


1 Answers

For anybody still interested in this:

As of version 1.2.4, PySide is buggy in that it doesn't wrap the Q_ARG and Q_RETURN_ARG macros. Instead, it unwisely wraps the QGenericArgument and QGenericReturnArgument classes, which are internal helper classes and not meant to be instanciated directly. As a result of this, invoking slots with arguments invariably results in a segmentation fault.

By comparison, PyQt does wrap the macros rather than the classes, and does not suffer from the same problems.

like image 194
ekhumoro Avatar answered Sep 18 '22 23:09

ekhumoro