6

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_()
Community
  • 1
  • 1
NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
  • 3
    Are you sure that the first argument to `QGenericArgument` should be the argument *name* instead of its *type*? In fact the error message is saying it cannot find a method with *type* `Example::beep(text)`, which is true since the type ought to be `Example::beep(QString)`. – Bakuriu May 08 '14 at 10:41
  • It's the usual Python way to name the parameter names not the types while I thought only for the Qt Signals one indicates the types. I tried with 'str' instead of 'text' with no change and 'QString' instead of 'text' with the change that no warnings are printed anymore but also not the expected texts. It doesn't solve the problem. – NoDataDumpNoContribution May 08 '14 at 10:47
  • Slots are searched by their *type* in order to determine the right overloading. C++ doesn't even have named parameters, and PySide is just a wrapper to the Qt library... how do you think PySide can handle named parameters if it's just a wrapper under something that doesn't support them? I know that it doesn't solve the problem (and that's why I didn't post it as an answer). By the way: on my machine I keep getting segmentation faults. It seems like the methods are found but something goes wrong when calling them. – Bakuriu May 08 '14 at 10:51
  • I added a small addendum to the question. I wonder how related `Signal:emit(paremeters)` and `invokeMethod(target, paremters)` are, after all they can have very similar effects. But I don't know enough what is going on under the hood of PySide to investigate one of them and learn about the other. – NoDataDumpNoContribution May 08 '14 at 11:02
  • You cannot have slots outside `QObject`s. Any slot must be a method of `QObject` derived class. You cannot use `QMetaObject.invokeMethod` to invoke functions outside `QObject`s. Calling `QtCore.QMetaObject.invokeMethod(t, 'beep', QtCore.QGenericArgument('QString', value))` seems to be correct, but PySide apparently cannot execute `invokeMethod` with arguments correctly. When I use `QString`, I get segfault. When I use `int` argument, I just get a wrong number. That is obviously a sign of a PySide implementation bug. – Pavel Strakhov May 08 '14 at 12:35
  • Now I get that too. I'll update the question accordingly. Should I maybe submit a bug report for it? – NoDataDumpNoContribution May 08 '14 at 13:03
  • I tried to submit a bug, but it doesn't seem to be easy. Their [development site](http://qt-project.org/wiki/PySideDevelopment) links to a [bug tracker](https://bugreports.qt-project.org/browse/PYSIDE) but there no indication is given how to submit a bug. Also I already unsubscribed from their mailing list after not getting an answer and won't subscribe again. A contact form or mail address I cannot find either. Without convenient ways to contact them I leave the issue as it is. – NoDataDumpNoContribution May 12 '14 at 09:27
  • @Trilarion Is there a work around of this issue? Im also stuck with the similar kind of problem. – zingy May 19 '15 at 10:28
  • @zingy Not that I know. I went back to signals/slots. – NoDataDumpNoContribution May 19 '15 at 11:45

1 Answers1

6

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.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • > As of version 1.2.4, PySide is buggy.....invoking slots with arguments invariably results in a segmentation fault. | That's good to know. I had other situations, where a downgrade from 1.2.4 to 1.2.1 would mean the difference between a stable and a broken app. Are any new (bugfix) releases of PySide in development? 1.2.4 is quite old for a latest release considering the bugs it seems to carry around. – timmwagener Dec 16 '16 at 18:05
  • @timmwagener. Qt4 is legacy code now (offical support ended last year), so I would imagine that the majority of future development will focus on [PySide2](http://wiki.qt.io/PySide2). – ekhumoro Dec 16 '16 at 18:12