[PyQt] How to expose function accepting array of struct to D-Bus?

Phil Thompson phil at riverbankcomputing.com
Fri Aug 10 10:04:07 BST 2012


On Tue, 7 Aug 2012 15:45:43 -0400, Evade Flow <evadeflow at gmail.com> wrote:
>> In theory you should be able to use QDBusArgument but I've not tested
>> it - I've always struggled to find test cases.
> 
> Maybe the little server app appended below will help. Using dbus-send, I
> can exercise the 'name' property and 'echo' method, as well as the
> introspection interface:
> 
>   # Call 'echo' method
>   dbus-send --print-reply --dest=com.example.dbus /com/example/dbus \
>       com.example.dbus.echo string:Hello
> 
>   # Exercise introspection interface
>   dbus-send --print-reply --dest=com.example.dbus /com/example/dbus \
>       org.freedesktop.DBus.Introspectable.Introspect
> 
>   # Get the 'name' property
>   dbus-send --print-reply --dest=com.example.dbus /com/example/dbus \
>       org.freedesktop.DBus.Properties.Get string:com.example.dbus
>       string:name
> 
>   # Set 'name'
>   dbus-send --print-reply --dest=com.example.dbus /com/example/dbus \
>       org.freedesktop.DBus.Properties.Set string:com.example.dbus \
>       string:name variant:string:MyNewName
> 
>   # Call 'setPosition' method
>   dbus-send --print-reply --dest=com.example.dbus /com/example/dbus \
>       com.example.dbus.setPosition double:1.0 double:2.0 double:3.0
> 
> Unfortunately for me, attempts to call setPosition() result in:
> 
>   Error org.freedesktop.DBus.Error.UnknownMethod: No such method
>   'setPosition' in interface 'com.example.dbus' at object path
>   '/com/example/dbus' (signature 'ddd')
> 
> I suppose this makes sense, but I was hoping there might be a
> workaround. It would be really great to be able to write a PyQt-based
> implementation of *any* D-Bus specification. That way, I could mock out
> any arbitrary server's functionality for testing.
> 
> For now, it looks like structs aren't supported?  I'd be willing to help
> add that support if it seems doable, but I'm not sure where to start, or
> what the effort level might be. (I can probably spend about 20 hours on
> it without getting into too much trouble...)
> 
> ----------
> 
> from PyQt4 import QtDBus
> from PyQt4.QtCore import (QCoreApplication, QObject, Q_CLASSINFO,
pyqtSlot,
>                           pyqtProperty)
> from PyQt4.QtDBus import QDBusArgument, QDBusConnection,
> QDBusAbstractAdaptor
> 
> class MyServer(QObject):
> 
>     def __init__(self):
>         QObject.__init__(self)
>         self.__dbusAdaptor = ServerAdaptor(self)
>         self.__name = 'myname'
> 
>     def echo(self, value):
>         return'Received: {0}'.format(value)
> 
>     @property
>     def name(self):
>         return self.__name
> 
>     @name.setter
>     def name(self, value):
>         self.__name = value
> 
> 
> class ServerAdaptor(QDBusAbstractAdaptor):
>     Q_CLASSINFO("D-Bus Interface", "com.example.dbus")
>     Q_CLASSINFO("D-Bus Introspection",
>     '  <interface name="com.example.dbus">\n'
>     '    <property name="name" type="s" access="readwrite"/>\n'
>     '    <method name="echo">\n'
>     '      <arg direction="in" type="s" name="phrase"/>\n'
>     '      <arg direction="out" type="s" name="echoed"/>\n'
>     '    </method>\n'
>     '    <method name="setPosition">\n'
>     '      <arg direction="in" type="(ddd)" name="pos"/>\n'
>     '    </method>\n'
>     '  </interface>\n')
> 
>     def __init__(self, parent):
>         super().__init__(parent)
> 
>     @pyqtSlot(str, result=str)
>     def echo(self, phrase):
>         return self.parent().echo(phrase)
> 
>     @pyqtSlot(QDBusArgument)
>     def setPosition(self, pos):
>         print("How can I call this function?")
> 
> 
>     @pyqtProperty(str)
>     def name(self):
>         return self.parent().name
> 
>     @name.setter
>     def name(self, value):
>         self.parent().name = value
> 
> def start():
>     app = QCoreApplication([])
>     bus = QDBusConnection.sessionBus()
>     server = MyServer()
>     bus.registerObject('/com/example/dbus', server)
>     bus.registerService('com.example.dbus')
>     app.exec()
> 
> if __name__ == '__main__':
>     start()

It seems the trick is to specify QDBusMessage as the slot argument and use
its arguments() method to get at the actual arguments.

For example, if you have a method setColors() that takes a single argument
that is an array of structs of three ints (i.e. an array of RGB values).
This would have a DBus signature of "a(iii)". The Python implementation
would be...

        @pyqtSlot(QDBusMessage)
        def setColors(self, msg):
            for red, green, blue in msg.arguments()[0]:
                print(red, green, blue)

Does this technique solve all your issues?

I'll update the docs.

Phil


More information about the PyQt mailing list