当前位置: 首页>>技术教程>>正文


python – 如何读取dbus-monitor输出?

, , ,

问题描述

我正在尝试 dbus-monitor 来尝试了解 dbus 在 Ubuntu 环境中是如何工作的。对此我有几个疑问:

  1. 您能告诉我如何正确阅读以下内容吗?我了解大意,但不了解细节。

    \n

    signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated\nint32 23\nstring "enabled"\nvariant boolean true\nmethod call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1399 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications;\nmember=GetCapabilities\n

    \n

    我知道第一个是信号,而第二个是方法。目的地是否意味着信号可以有特定的接收器/插槽?什么是会员?信号后面的列表项是信号中传递的参数吗?什么是发件人和序列号?

  2. 我注意到 volume-control 和通知之间的关系。从我从 dbus-monitor 输出中读到的内容

    \n

    method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1400 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify\nstring "gnome-settings-daemon"\nuint32 0\nstring "notification-audio-volume-medium"\nstring " "\nstring ""\narray [\n]\narray [\ndict entry(\nstring "value"\nvariant int32 38\n)\ndict entry(\nstring "x-canonical-private-synchronous"\nvariant string "volume"\n)\n]\nint32 -1\n

    \n

    看来通知是由它的方法触发的。我只是不太明白为什么会这样。在我看来,如果发出“notification-audio-volume-medium”信号,而通知会侦听该信号并做出相应反应,则更有意义。如果发送/接收是公开的而不是私有的,是否会带来更大的灵活性和效率?例如,如果有“notification-audio-volume-medium”的公共信号,则多个应用程序可以侦听该信号(这将允许竞争通知应用程序的存在),并且开发人员只需关心发送信号,同时接收信号处理信号将是通知应用程序的业务(或任何其他需要这些信号的程序)。

  3. 我是 Dbus 的新手,想了解更多,因为我正在 Python 上使用 Dbus,主要是为了开发一些小程序。我见过 the dbus-python tutorial,它教如何监听所有信号(通过既不指定接口也不指定路径等)。但是如何在调用方法时跟踪方法,就像 dbus-monitor 那样?

如果您有耐心教授其工作原理,我们不客气。

最佳方案

D-Bus介绍

  • D-Bus 提供服务之间通信的方式。服务可以是匿名的(仅通过总线地址标识,如 :1.6),并且服务可以获取 well-known 名称,如 org.freedesktop.Notificationsorg.freedesktop.NetworkManager 。您可以在日志中看到的发送者和目的地是服务。 “Null destination” 表示广播:传递到所有服务。

  • 一项服务可以将一个或多个对象导出到总线。对象被赋予对象路径,例如 /org/freedesktop/NetworkManager/ActiveConnection/1/org/ayatana/menu/DA00003 。对象路径使用斜杠作为分隔符,就像文件系统路径一样。

  • 每个对象可以支持一个或多个接口。接口只不过是一组方法和信号,通俗地称为成员(与 OOP 接口非常相似)。方法和信号具有固定的签名。成员始终在 well-known 接口名称中命名。

  • 一旦发布,well-known 名称就不会改变。

  • 任何服务都可以连接到另一个服务的信号并异步调用其方法。任何服务都可以发出信号。

Signals

现在回答您的具体问题。

\\n

signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated\\nint32 23\\nstring "enabled"\\nvariant boolean true\\n

\\n

是的,你没有看错,这是一个信号。它由服务 :1.1948 广播,而 “self” 对象是 /org/ayatana/menu/DA00003 。该信号的名称为 ItemPropertyUpdated,在接口 org.ayatana.dbusmenu 中定义(如 C++ 中的 org.ayatana.dbusmenu::ItemPropertyUpdated)。我猜想,序列号是总线上事件的一种唯一标识符。

然后我们看到信号参数。根据 interface documentation ,第一个 int32 参数是项目的 id,第二个字符串是其属性名称,第三个变体是属性值。因此,/org/ayatana/menu/DA00003 对象正在通知我们项目 id #23 将其 enabled 属性更改为 true。


关于信号的另一个例子:

signal sender=:1.1602 -> dest=(null destination) serial=20408 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=SendingChatMsg
   int32 47893
   string "test"
   uint32 1
signal sender=:1.1602 -> dest=(null destination) serial=20409 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=IrcSendingText
   int32 64170
   string "PRIVMSG #chat :test

我使用 Pidgin 向 IRC 频道发送了一条短信 “test”,而 /im/pidgin/purple/PurpleObjectim.pidgin.purple.PurpleInterface 接口下发出了两个信号:首先是通用的 SendingChatMsg ,然后是更具体的 IrcSendingText

Methods

现在方法。方法是要求 D-Bus 对象执行某些操作或执行某些查询并返回数据的方法。它们与经典的 OOP 方法非常相似,只是 D-Bus 方法是异步调用的。

让我们以编程方式调用 D-Bus 方法。

import dbus, dbus.proxies

#-- connect to the session bus (as opposed to the system bus)
session = dbus.SessionBus()

#-- create proxy object of D-Bus object
obj_proxy = dbus.proxies.ProxyObject(conn=session,
         bus_name="org.freedesktop.Notifications",     #-- name of the service we are retrieving object from
         object_path="/org/freedesktop/Notifications") #-- the object path

#-- create proxy object of the D-Bus object wrapped into specific interface
intf_proxy = dbus.proxies.Interface(obj_proxy, "org.freedesktop.Notifications")

#-- lastly, create proxy object of the D-Bus method
method_proxy = intf_proxy.get_dbus_method("Notify")

#-- ... and call the method
method_proxy("test from python",
             dbus.UInt32(0),
             "bluetooth",     #-- icon name
             "Notification summary",
             "Here goes notification body",
             [], {},
             5) #-- timeout

请注意参数,尤其是图标名称。在您的示例中,"notification-audio-volume-medium" 是 medium-powered 音量扬声器的图标。

定制服务

完全可以运行您自己的 D-Bus 服务、导出您自己的 D-Bus 对象并使用您自己的方法和信号定义您自己的 D-Bus 接口。一旦您掌握了总体概念并阅读了 dbus 模块文档,这一切都可以在 Python 中轻松完成。 :)

次佳方案

我还在寻找使用 python 脚本通过 dbus 收集桌面通知的解决方案。这个问题是我通过谷歌搜索得到的最接近的问题,但编写 notify-osd 的替代品似乎有点矫枉过正:)

查看 recent-notifications 小程序源代码,我得到了一些如何监视 dbus 消息的提示,这是我提出的 python 实现:

import gtk
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def filter_cb(bus, message):
    # the NameAcquired message comes through before match string gets applied
    if message.get_member() != "Notify":
        return
    args = message.get_args_list()
    # args are
    # (app_name, notification_id, icon, summary, body, actions, hints, timeout)
    print("Notification from app '%s'" % args[0])
    print("Summary: %s" % args[3])
    print("Body: %s", args[4])


DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
bus.add_match_string(
    "type='method_call',interface='org.freedesktop.Notifications',member='Notify'")
bus.add_message_filter(filter_cb)
gtk.main()

希望这对某人有帮助,因为似乎没有很多与监视 dbus 消息相关的简单 python 示例。

参考资料

本文由Ubuntu问答整理, 博文地址: https://ubuntuqa.com/article/14003.html,未经允许,请勿转载。