Search code examples
pythonqtpyqt5dbus

PyQt5 dbus: Force type signature of signal argument to be array of string


I'm writing an MPRIS player, which communicates with clients over dbus. I need to emit a signal when my playback state changes. However, the signal requires a format of (sa{sv}as), and my code is producing (sa{sv}av). Here's the important part:

self.signal = QDBusMessage.createSignal(
   "/org/mpris/MediaPlayer2",
   "org.freedesktop.DBus.Properties",
   "PropertiesChanged"
)
self.signal.setArguments(
    [interface, {property: values}, ['']]
)

The problem is the third item in the list given to setArguments. It is an empty string in a list because I need to produce a type of 'array of string' (as) but pyqt5 translates that into 'array of variant' (av).

I never need to put any actual data in that list, I just need the type signature to be correct.

Is there any way to do this in PyQt5? Perhaps using QDBusArgument?


Solution

  • I... I got it working. Wow. That was a campaign.

    I don't know what is going wrong exactly, I wasn't able to dig out of the PyQt5 source where exactly the conversion happens. However, I did look into QDbusArgument(). There is no documentation for this in python, and the C++ docs are worthless due to major differences, so I took to the source code. in sip/QtDbus/qdbusargument.sip, I discovered a completely undocumented new method called qdbusargument_add. This maps to QDbusArgument().add() in python. It is used to add arguments with an explicit type id to a QDbusArgument. And it has a special case for QStringList!

    From then I just bruteforced every possibility I could think of with arguments to QDbusArgument(), and finally got the following:

    def PropertiesChanged(self, interface, property, values):
        """Sends PropertiesChanged signal through sessionBus.
        Args:
            interface: interface name
            property: property name
            values: current property value(s)
        """
        emptyStringListArg = QDBusArgument()
        emptyStringListArg.add([""], QMetaType.QStringList)
        self.signal.setArguments([interface, {property: values}, emptyStringListArg])
        QDBusConnection.sessionBus().send(self.signal)
    

    It'd be great if the add() function could be documented, but I can't seem to send messages to the PyQt5 mailing list. Do I have to register first?