使用dbus调用Inkscape失败(Gio方式可正常运行)
问题:使用Python dbus库调用Inkscape DBus接口报错
尝试通过DBus脚本驱动Inkscape时,基于Gio的代码可正常运行,但改用Python dbus库时出现参数不匹配的错误。
正常运行的Gio版本代码
# Start after inkscape is running. print("DBus test") import gi gi.require_version("Gio", "2.0") from gi.repository import Gio, GLib try: bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) except BaseException: print("No DBus bus") exit() print("Got DBus bus") proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', None) names_list = proxy.call_sync('ListNames', None, Gio.DBusCallFlags.NO_AUTO_START, 500, None) # names_list is a GVariant, must unpack names = names_list.unpack()[0] # Look for Inkscape; names is a tuple. for name in names: if ('org.inkscape.Inkscape' in name): print("Found: " + name) break print("Name: " + name) appGroupName = "/org/inkscape/Inkscape" winGroupName = appGroupName + "/window/1" docGroupName = appGroupName + "/document/1" applicationGroup = Gio.DBusActionGroup.get( bus, name, appGroupName) windowGroup = Gio.DBusActionGroup.get(bus, name, winGroupName) documentGroup = Gio.DBusActionGroup.get(bus, name, docGroupName) # Activate actions. Draw a few objects first. applicationGroup.activate_action('select-all', GLib.Variant.new_string('all')) applicationGroup.activate_action('object-rotate-90-cw', None) windowGroup.activate_action('tool-switch', GLib.Variant.new_string('Arc')) windowGroup.activate_action('canvas-zoom-page', None)
报错的dbus库版本代码
#!/usr/bin/env python # Start after Inkscape is running. import dbus from xml.etree import ElementTree as ET print("DBus test") try: # Connect to the session bus bus = dbus.SessionBus() except dbus.exceptions.DBusException as e: print(f"No DBus bus: {e}") exit() print("Got DBus bus") # Get the list of names on the bus proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') interface = dbus.Interface(proxy, 'org.freedesktop.DBus') names = interface.ListNames() # Look for Inkscape name = None for n in names: if 'org.inkscape.Inkscape' in n: name = n print(f"Found: {name}") break if not name: print("Inkscape not found") exit() print(f"Name: {name}") appGroupName = "/org/inkscape/Inkscape" winGroupName = appGroupName + "/window/1" docGroupName = appGroupName + "/document/1" def activate_action(group, action, parameter=None): try: group.Activate(action, parameter, dbus_interface='org.gtk.Actions') print(f"Activated action: {action} with parameter: {parameter}") except dbus.exceptions.DBusException as e: print(f"Failed to activate action {action}: {e}") applicationGroup = bus.get_object(name, appGroupName) windowGroup = bus.get_object(name, winGroupName) documentGroup = bus.get_object(name, docGroupName) # Activate actions. Draw a few objects first. activate_action(applicationGroup, 'select-all', 'all') activate_action(applicationGroup, 'object-rotate-90-cw')
报错信息
ERROR:dbus.connection:Unable to set arguments ('select-all', 'all') according to signature 'sava{sv}': <class 'TypeError'>: More items found in D-Bus signature than in Python arguments Traceback (most recent call last): File "/home/simone/inkscape_experiments/dbus_05.py", line 54, in <module> activate_action(applicationGroup, 'select-all', 'all') File "/home/simone/inkscape_experiments/dbus_05.py", line 44, in activate_action group.Activate(action, parameter, dbus_interface='org.gtk.Actions') File "/usr/lib/python3/dist-packages/dbus/proxies.py", line 72, in __call__ return self._proxy_method(*args, **keywords) File "/usr/lib/python3/dist-packages/dbus/proxies.py", line 141, in __call__ return self._connection.call_blocking(self._named_service, File "/usr/lib/python3/dist-packages/dbus/connection.py", line 643, in call_blocking message.append(signature=signature, *args) TypeError: More items found in D-Bus signature than in Python arguments
问题原因
错误提示中的签名sava{sv}明确了org.gtk.Actions接口的Activate方法需要3个参数:
- 动作名称(字符串类型,
s) - 参数数组(变体类型数组,
av) - 附加属性字典(字符串-变体键值对的字典,
a{sv})
你调用时只传了2个参数,且第二个参数直接传了字符串,没有包装成变体数组,导致参数数量和类型都不匹配。
对比Gio代码,activate_action的第二个参数是GLib.Variant对象,Gio会自动将其包装成符合要求的数组;但dbus库需要手动处理参数格式。
解决方法
修改activate_action函数,按照DBus签名要求包装参数:
def activate_action(group, action, parameter=None): try: # 处理参数:有参数则包装为dbus.Variant数组,无参数则传空变体数组 if parameter is not None: param_array = dbus.Array([dbus.Variant('s', parameter)], signature='v') else: param_array = dbus.Array(signature='v') # 附加属性传空字典 group.Activate(action, param_array, {}, dbus_interface='org.gtk.Actions') print(f"Activated action: {action} with parameter: {parameter}") except dbus.exceptions.DBusException as e: print(f"Failed to activate action {action}: {e}")
查看DBus签名的方法
方法1:使用gdbus命令行工具
直接查询Inkscape的org.gtk.Actions接口信息:
gdbus introspect --session --dest org.inkscape.Inkscape --object-path /org/inkscape/Inkscape --interface org.gtk.Actions
方法2:在Python中通过Introspect接口获取
通过DBus的org.freedesktop.DBus.Introspectable接口获取XML描述,解析后查看方法签名:
# 添加到你的代码中,获取接口签名 introspect_proxy = bus.get_object(name, appGroupName) introspect_interface = dbus.Interface(introspect_proxy, 'org.freedesktop.DBus.Introspectable') xml_data = introspect_interface.Introspect() root = ET.fromstring(xml_data) for interface in root.findall('interface'): if interface.get('name') == 'org.gtk.Actions': print("org.gtk.Actions接口方法签名:") for method in interface.findall('method'): print(f" 方法名: {method.get('name')}") for arg in method.findall('arg'): print(f" 参数: {arg.get('name')} 类型: {arg.get('type')}")
内容的提问来源于stack exchange,提问作者simone
相关产品推荐
相关产品推荐

