D-Bus 介绍

D-Bus 介绍

我正在玩dbus 监控尝试了解 dbus 在 Ubuntu 环境中的工作原理。在这方面我有几个问题:

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

    signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated
    int32 23
    string "enabled"
    variant boolean true
    method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1399 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications;
    member=GetCapabilities
    

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

  2. 我注意到音量控制和通知之间的关系。从我从 dbus-monitor 输出中读到的内容来看

    method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1400 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify
    string "gnome-settings-daemon"
    uint32 0
    string "notification-audio-volume-medium"
    string " "
    string ""
    array [
    ]
    array [
    dict entry(
    string "value"
    variant int32 38
    )
    dict entry(
    string "x-canonical-private-synchronous"
    variant string "volume"
    )
    ]
    int32 -1
    

    通知似乎是由其方法触发的。我只是不太明白它为什么这样工作。在我看来,如果发出信号会更有意义“通知音量适中”而通知会监听此信号并做出相应反应。如果发送/接收是公开的而不是私密的,难道不会提供更多的灵活性和效率吗?例如,如果有一个公开信号“通知音量适中”那么多个应用程序可以监听这个信号(这将允许竞争的通知应用程序出现)并且开发人员只需要关心发送信号,而接收和处理信号将是通知应用程序的业务(或任何其他需要这些信号的程序)。

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

如果您有耐心教导如何操作,欢迎您。

答案1

D-Bus 介绍

  • D-Bus 提供了以下通信方式:服务. 服务可以匿名的(仅通过总线地址识别,如:1.6),并且服务可以获取知名的名字,如org.freedesktop.Notificationsorg.freedesktop.NetworkManager。日志中可以看到发送者和目的地是服务。“空目的地”表示广播:传送到所有服务。

  • 服务可以导出一个或多个对象到公交车。物品被给予对象路径,如/org/freedesktop/NetworkManager/ActiveConnection/1/org/ayatana/menu/DA00003。对象路径使用斜杠作为分隔符,如文件系统路径。

  • 每个对象可以支持一个或多个接口。接口只不过是一组方法和信号,俗称成员(与 OOP 接口非常相似)。方法和信号具有固定签名。成员始终位于命名空间内知名接口名称。

  • 一旦出版,知名的名字从不改变

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

信号

现在来回答您的具体问题。

信号发送者=:1.1948 -> 目标=(空目标)序列=1829990 路径=/org/ayatana/menu/DA00003;接口=org.ayatana.dbusmenu;成员=ItemPropertyUpdated
int32 23
字符串“已启用”
变体布尔值 true

是的,你说得对,这是一个信号。它由服务广播:1.1948,并且“自身”对象是/org/ayatana/menu/DA00003。信号具有ItemPropertyUpdated在接口中定义的名称org.ayatana.dbusmenu(如org.ayatana.dbusmenu::ItemPropertyUpdated在 C++ 中)。我猜,序列是总线上事件的一种唯一标识符。

然后我们会看到信号参数。根据接口文档,第一个 int32 参数是项目的 ID,第二个字符串是其属性名称,第三个变量是属性值。因此,该/org/ayatana/menu/DA00003对象通知我们项目 ID #23 已将其enabled属性更改为 true。


关于信号的另一个例子:

信号发送者=:1.1602 -> 目标=(空目标)序列=20408 路径=/im/pidgin/purple/PurpleObject;接口=im.pidgin.purple.PurpleInterface;成员=SendingChatMsg
   int32 47893
   字符串“测试”
   uint32 1
信号发送者=:1.1602 -> 目标=(空目标)序列=20409 路径=/im/pidgin/purple/PurpleObject;接口=im.pidgin.purple.PurpleInterface;成员=IrcSendingText
   int32 64170
   字符串“PRIVMSG #chat:test

我使用 Pidgin 向 IRC 频道发送了一条文本消息“测试”,并在界面/im/pidgin/purple/PurpleObject下发出两个信号im.pidgin.purple.PurpleInterface:首先是一般的SendingChatMsg,然后是更具体的IrcSendingText

方法

现在介绍方法。方法是要求 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"是中等功率音量扬声器的图标。

定制服务

完全可以运行自己的 D-Bus 服务、导出自己的 D-Bus 对象并使用自己的方法和信号定义自己的 D-Bus 接口。一旦您掌握了整体概念并阅读了模块文档,所有这些都可以很容易地用 Python 完成dbus:)

答案2

我也在寻找使用 Python 脚本通过 dbus 收集桌面通知的解决方案。这个问题是我在谷歌上搜索到的最接近的,但编写一个替代的方法来替代notify-osd似乎有点过头了 :)

看看近期通知小程序源代码我得到了一些关于如何监控 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 示例。

相关内容