Hello,
I try to connect the DBus PropertiesChanged Signal with Qt and PySide6/Python, the signal has the following signature:
org.freedesktop.DBus.Properties.PropertiesChanged (STRING interface_name,
ARRAY of DICT_ENTRY<STRING,VARIANT> changed_properties,
ARRAY<STRING> invalidated_properties);
org.freedesktop.DBus.Properties.PropertiesChanged (STRING interface_name,
ARRAY of DICT_ENTRY<STRING,VARIANT> changed_properties,
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
import sys
from PySide6 import QtDBus, QtCore
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
service = "org.freedesktop.NetworkManager"
#service="org.mpris.MediaPlayer2.spotifyd.instance7453"
path = "/org/freedesktop/NetworkManager"
#path="/org/mpris/MediaPlayer2"
iface = "org.freedesktop.DBus.Properties"
#without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
conn.registerObject('/', self)
#bus.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString,QVariantMap,QStringList)"))
#qt.dbus.integration: Could not connect "org.freedesktop.DBus.Properties" to dbuspropertieschanged(QString,QVariantMap,QStringList)
conn.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString, QVariantMap, QVariantList)"))
pass
@Slot(str, dict, list)
def dbuspropertieschanged(self, arg1:str, arg2:dict, arg3:list) -> None:
print(arg1)
print(arg2)
print(arg3)
pass
if __name__ == '__main__':
print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
sys.version_info[2], sys.platform))
window = MainWindow()
window.setWindowTitle(qVersion())
window.resize(800, 480)
window.show()
sys.exit(app.exec())
import sys
from PySide6.QtWidgets import QMainWindow
from PySide6 import QtDBus, QtCore
from PySide6.QtCore import QLibraryInfo, qVersion, Slot
from PySide6.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
service = "org.freedesktop.NetworkManager"
#service="org.mpris.MediaPlayer2.spotifyd.instance7453"
path = "/org/freedesktop/NetworkManager"
#path="/org/mpris/MediaPlayer2"
iface = "org.freedesktop.DBus.Properties"
conn = QtDBus.QDBusConnection.systemBus()
#without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
conn.registerObject('/', self)
#bus.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString,QVariantMap,QStringList)"))
#qt.dbus.integration: Could not connect "org.freedesktop.DBus.Properties" to dbuspropertieschanged(QString,QVariantMap,QStringList)
conn.connect(service, path, iface, "PropertiesChanged", self, QtCore.SLOT("dbuspropertieschanged(QString, QVariantMap, QVariantList)"))
pass
@Slot(str, dict, list)
def dbuspropertieschanged(self, arg1:str, arg2:dict, arg3:list) -> None:
print(arg1)
print(arg2)
print(arg3)
pass
if __name__ == '__main__':
print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
sys.version_info[2], sys.platform))
print(QLibraryInfo.build())
app = QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle(qVersion())
window.resize(800, 480)
window.show()
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
from sdbus import (DbusInterfaceCommonAsync, dbus_signal_async, sd_bus_open_system)
from asyncio import new_event_loop
service = "org.freedesktop.NetworkManager"
#service of qspotify, instance number changes on every launch of qspotify
#service="org.mpris.MediaPlayer2.spotifyd.instance7453"
path = "/org/freedesktop/NetworkManager"
#path="/org/mpris/MediaPlayer2"
iface = "org.freedesktop.DBus.Properties"
class DBusInterface(
DbusInterfaceCommonAsync,
interface_name=iface
):
@dbus_signal_async(
signal_signature='sa{sv}as'
)
def PropertiesChanged(self) -> (str, dict, list):
pass
example_object = DBusInterface.new_proxy(service, path, bus=sd_bus_open_system())
async def dbuspropertieschange() -> None:
# Use async for loop to print clock signals we receive
async for x in example_object.PropertiesChanged:
print(f'{x} ')
loop = new_event_loop()
# Always binds your tasks to a variable
task_clock = loop.create_task(dbuspropertieschange())
loop.run_forever()
from sdbus import (DbusInterfaceCommonAsync, dbus_signal_async, sd_bus_open_system)
from asyncio import new_event_loop
service = "org.freedesktop.NetworkManager"
#service of qspotify, instance number changes on every launch of qspotify
#service="org.mpris.MediaPlayer2.spotifyd.instance7453"
path = "/org/freedesktop/NetworkManager"
#path="/org/mpris/MediaPlayer2"
iface = "org.freedesktop.DBus.Properties"
class DBusInterface(
DbusInterfaceCommonAsync,
interface_name=iface
):
@dbus_signal_async(
signal_signature='sa{sv}as'
)
def PropertiesChanged(self) -> (str, dict, list):
pass
example_object = DBusInterface.new_proxy(service, path, bus=sd_bus_open_system())
async def dbuspropertieschange() -> None:
# Use async for loop to print clock signals we receive
async for x in example_object.PropertiesChanged:
print(f'{x} ')
loop = new_event_loop()
# Always binds your tasks to a variable
task_clock = loop.create_task(dbuspropertieschange())
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
import sys
from PySide6 import QtDBus, QtCore
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
service = "org.freedesktop.DBus"
path = "/org/freedesktop/DBus"
iface = "org.freedesktop.DBus"
#without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
conn.registerObject('/', self)
conn.connect(service, path, iface, "NameOwnerChanged", self, QtCore.SLOT("nameownerchanged(QString, QString, QString)"))
pass
@Slot(str, str, str)
def nameownerchanged(self, arg1:str, arg2:str, arg3:str) -> None:
print(arg1)
print(arg2)
print(arg3)
pass
if __name__ == '__main__':
print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
sys.version_info[2], sys.platform))
window = MainWindow()
window.setWindowTitle(qVersion())
window.resize(800, 480)
window.show()
sys.exit(app.exec())
import sys
from PySide6.QtWidgets import QMainWindow
from PySide6 import QtDBus, QtCore
from PySide6.QtCore import QLibraryInfo, qVersion, Slot
from PySide6.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
service = "org.freedesktop.DBus"
path = "/org/freedesktop/DBus"
iface = "org.freedesktop.DBus"
conn = QtDBus.QDBusConnection.systemBus()
#without this, the conn.connect call hangs, seems to be a bug, is already reported and fixed.
conn.registerObject('/', self)
conn.connect(service, path, iface, "NameOwnerChanged", self, QtCore.SLOT("nameownerchanged(QString, QString, QString)"))
pass
@Slot(str, str, str)
def nameownerchanged(self, arg1:str, arg2:str, arg3:str) -> None:
print(arg1)
print(arg2)
print(arg3)
pass
if __name__ == '__main__':
print('Python {}.{}.{} {}'.format(sys.version_info[0], sys.version_info[1],
sys.version_info[2], sys.platform))
print(QLibraryInfo.build())
app = QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle(qVersion())
window.resize(800, 480)
window.show()
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
Bookmarks