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

Evade Flow evadeflow at gmail.com
Fri Aug 10 16:59:48 BST 2012


> It seems the trick is to specify QDBusMessage as the slot argument...
>
>         @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?

Indeed, it does! Thanks so much, Phil!!


On Fri, Aug 10, 2012 at 5:04 AM, Phil Thompson
<phil at riverbankcomputing.com> wrote:
> 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