Hello,

I try to connect the DBus PropertiesChanged Signal with Qt and PySide6/Python, the signal has the following signature:

Qt Code:
  1. org.freedesktop.DBus.Properties.PropertiesChanged (STRING interface_name,
  2. ARRAY of DICT_ENTRY<STRING,VARIANT> changed_properties,
  3. ARRAY<STRING> invalidated_properties);
To copy to clipboard, switch view to plain text mode 

Concrete I need the signal and PropertiesChanged metadata of the MPRIS standard (https://specifications.freedesktop.o...s-spec/latest/, https://www.freedesktop.org/wiki/Spe...spec/metadata/), the meta data has a dictionary in the propertieschanged dictionary and the inner dictionary has arrays as values on some entries, I can see the data by setting the environment variable "QDBUS_DEBUG": "1" or on a cross check using sdbus:

('org.mpris.MediaPlayer2.Player',
{'Metadata': ('a{sv}', {'xesam:albumArtist': ('as', ['Daft Punk', 'Pharrell Williams', 'Nile Rodgers']),
'mpris:trackid': ('o', '/spotify/track/2Foc5Q5nqNiosCNqttzHof'),
'xesam:title': ('s', 'Get Lucky (Radio Edit) [feat. Pharrell Williams and Nile Rodgers]'),
'xesam:album': ('s', 'Get Lucky (Radio Edit) [feat. Pharrell Williams and Nile Rodgers]'),
'xesam:trackNumber': ('u', 1), 'xesam:discNumber': ('i', 1),
'mpris:artUrl': ('s', 'https://i.scdn.co/image/ab67616d0000b2731d5cf960a92bb8b03fc2be7f'),
'xesam:url': ('s', 'https://open.spotify.com/track/2Foc5Q5nqNiosCNqttzHof'),
'mpris:length': ('x', 248413000), 'xesam:autoRating': ('d', 0.82),
'xesam:artist': ('as', ['Daft Punk', 'Pharrell Williams', 'Nile Rodgers'])}),
'PlaybackStatus': ('s', 'Playing')},
[])

I test it currently with the DBus NetworkManager, it is simpler as it has no inner dictionary, but some arrays of objects as value and I can force the signal by enabling and disabling my network connection on Ubuntu, these are 2 example outputs:

('org.freedesktop.NetworkManager', {'ActiveConnections': ('ao', ['/org/freedesktop/NetworkManager/ActiveConnection/8'])}, [])
('org.freedesktop.NetworkManager', {'State': ('u', 40)}, [])

But for neither of the 2 I can't get the connected slot method to run, I also don't get any error.

I managed to connect to DBus signals with simple data types, but I can't get it to work with PropertiesChanged, whith the dictionaries and arrays.

I have uploaded demos on github with a Visual Studio Code project: https://github.com/Drexel2k/qdbus

This is the NetworkManager demo I want to get to work first: https://github.com/Drexel2k/qdbus/bl...tieschanged.py
Qt Code:
  1. import sys
  2. from PySide6.QtWidgets import QMainWindow
  3. from PySide6 import QtDBus, QtCore
  4. from PySide6.QtCore import QLibraryInfo, qVersion, Slot
  5. from PySide6.QtWidgets import QApplication, QMainWindow
  6.  
  7. class MainWindow(QMainWindow):
  8.  
  9. def __init__ (self, *args, **kwargs):
  10. super().__init__(*args, **kwargs)
  11. service = "org.freedesktop.NetworkManager"
  12. #service="org.mpris.MediaPlayer2.spotifyd.instance7453"
  13. path = "/org/freedesktop/NetworkManager"
  14. #path="/org/mpris/MediaPlayer2"
  15. iface = "org.freedesktop.DBus.Properties"
  16. conn = QtDBus.QDBusConnection.systemBus()
  17.  
  18. #without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
  19. conn.registerObject('/', self)
  20.  
  21. #bus.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString,QVariantMap,QStringList)"))
  22. #qt.dbus.integration: Could not connect "org.freedesktop.DBus.Properties" to dbuspropertieschanged(QString,QVariantMap,QStringList)
  23.  
  24. conn.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString, QVariantMap, QVariantList)"))
  25. pass
  26.  
  27. @Slot(str, dict, list)
  28. def dbuspropertieschanged(self, arg1:str, arg2:dict, arg3:list) -> None:
  29. print(arg1)
  30. print(arg2)
  31. print(arg3)
  32. pass
  33.  
  34. if __name__ == '__main__':
  35. print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
  36. sys.version_info[2], sys.platform))
  37. print(QLibraryInfo.build())
  38. app = QApplication(sys.argv)
  39. window = MainWindow()
  40. window.setWindowTitle(qVersion())
  41. window.resize(800, 480)
  42. window.show()
  43. sys.exit(app.exec())
To copy to clipboard, switch view to plain text mode 
This is a working cross check with sdbus: https://github.com/Drexel2k/qdbus/bl...n/sdbusdemo.py
Qt Code:
  1. from sdbus import (DbusInterfaceCommonAsync, dbus_signal_async, sd_bus_open_system)
  2.  
  3. from asyncio import new_event_loop
  4.  
  5.  
  6. service = "org.freedesktop.NetworkManager"
  7. #service of qspotify, instance number changes on every launch of qspotify
  8. #service="org.mpris.MediaPlayer2.spotifyd.instance7453"
  9. path = "/org/freedesktop/NetworkManager"
  10. #path="/org/mpris/MediaPlayer2"
  11. iface = "org.freedesktop.DBus.Properties"
  12.  
  13. class DBusInterface(
  14. DbusInterfaceCommonAsync,
  15. interface_name=iface
  16. ):
  17.  
  18. @dbus_signal_async(
  19. signal_signature='sa{sv}as'
  20. )
  21. def PropertiesChanged(self) -> (str, dict, list):
  22. pass
  23.  
  24.  
  25. example_object = DBusInterface.new_proxy(service, path, bus=sd_bus_open_system())
  26.  
  27.  
  28. async def dbuspropertieschange() -> None:
  29. # Use async for loop to print clock signals we receive
  30. async for x in example_object.PropertiesChanged:
  31. print(f'{x} ')
  32.  
  33. loop = new_event_loop()
  34.  
  35. # Always binds your tasks to a variable
  36. task_clock = loop.create_task(dbuspropertieschange())
  37.  
  38. loop.run_forever()
To copy to clipboard, switch view to plain text mode 
This is a working demo with the simple data types: https://github.com/Drexel2k/qdbus/bl...wnerchanged.py
Qt Code:
  1. import sys
  2. from PySide6.QtWidgets import QMainWindow
  3. from PySide6 import QtDBus, QtCore
  4. from PySide6.QtCore import QLibraryInfo, qVersion, Slot
  5. from PySide6.QtWidgets import QApplication, QMainWindow
  6.  
  7. class MainWindow(QMainWindow):
  8.  
  9. def __init__ (self, *args, **kwargs):
  10. super().__init__(*args, **kwargs)
  11. service = "org.freedesktop.DBus"
  12. path = "/org/freedesktop/DBus"
  13. iface = "org.freedesktop.DBus"
  14. conn = QtDBus.QDBusConnection.systemBus()
  15.  
  16. #without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
  17. conn.registerObject('/', self)
  18.  
  19. conn.connect(service, path, iface, "NameOwnerChanged", self, QtCore.SLOT("nameownerchanged(QString, QString, QString)"))
  20. pass
  21.  
  22. @Slot(str, str, str)
  23. def nameownerchanged(self, arg1:str, arg2:str, arg3:str) -> None:
  24. print(arg1)
  25. print(arg2)
  26. print(arg3)
  27. pass
  28.  
  29. if __name__ == '__main__':
  30. print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
  31. sys.version_info[2], sys.platform))
  32. print(QLibraryInfo.build())
  33. app = QApplication(sys.argv)
  34. window = MainWindow()
  35. window.setWindowTitle(qVersion())
  36. window.resize(800, 480)
  37. window.show()
  38. sys.exit(app.exec())
To copy to clipboard, switch view to plain text mode 
For all of the 3 a debug config exists, currently for the Qt modules the QDBUS_DEBUG environment variable is set to 0, but it can be easily enabled by setting it to 1.

The documentation (https://doc.qt.io/qt-6/qdbustypesystem.html) also says:
Extending the Type System
In order to use one's own type with Qt D-Bus, the type has to be declared as a Qt meta-type with the Q_DECLARE_METATYPE() macro and registered with the qDBusRegisterMetaType() function. The streaming operators operator>> and operator<< will be automatically found by the registration system.

and:

Warning: You may not use any type that is not on the list above, including typedefs to the types listed. This also includes QList<QVariant> and QMap<QString,QVariant>.

I saw some C++ examples also using QDBusArgument and overloading the streaming operators, I don't know if this would be the solution, but I also don't know how to do this in Python.

Any help is appreciated, thank you in advance!

Best regards
Matthias