【问题标题】:Use pydbus library to send signal over a Session Bus使用 pydbus 库通过会话总线发送信号
【发布时间】:2025-12-02 21:35:01
【问题描述】:

我正在使用pydbus,并且我已经成功地使用它来监听会话总线上的信号(在“客户端”)。我现在想编写服务器端,程序在每次触发动作时都会发送一个信号(例如,当它在 FS 上写入文件时)。我真的没有在他们的 GitHub 上得到任何这样的例子。他们只展示了如何编写一个基本的服务器,它有一堆客户端可以调用的方法(但这不是我想要的信号)。

仅供参考,客户端非常简单,看起来像这样:

# Subscribe to DBus dump signal
session_bus = SessionBus()
session_bus.subscribe(iface='my.iface',
                      signal='my_signal_name',
                      object='/my/object',
                      signal_fired=my_callback)

# Create and run main loop
loop = GObject.MainLoop()
loop.run()

其中my_callback 是每次侦听事件弹出时调用的方法(在本例中为my_signal_name

谢谢帮助。

【问题讨论】:

    标签: python dbus


    【解决方案1】:

    下面的python3/pydbus程序会每秒发布一个随机整数到会话D-Bus上。

    #!/usr/bin/env python3
    #!
    from pydbus.generic import signal
    from pydbus import SessionBus
    from gi.repository import GLib
    import random
    loop = GLib.MainLoop()
    interface_name = "org.example.project_1.server_1"
    bus = SessionBus()
    
    
    class Server_XML(object):
        """
        Server_XML definition.
        Emit / Publish a signal that is a random integer every second 
        type='i' for integer. 
        """
        dbus = """
        <node>
            <interface name="org.example.project_1.server_1">
                <signal name="app_1_signal">
                    <arg type='i'/>
                </signal>
            </interface>
        </node>
        """
        app_1_signal = signal()
    
    def repeating_timer():
        """Generate random integer between 0 and 100 and emit over Session D-Bus
        return True to keep the GLib timer calling this function once a second."""
        random_integer = random.randint(0,100)
        print(random_integer)
        emit.app_1_signal(random_integer)
        return True
    
    
    if __name__=="__main__":
        # Setup server to emit signals over the DBus
        emit = Server_XML()
        bus.publish(interface_name, emit)
        # Setup repeating timer
        GLib.timeout_add_seconds(interval=1, function=repeating_timer) 
        # Run loop with graceful ctrl C exiting.
        try:
            loop.run()
        except KeyboardInterrupt as e:
            loop.quit()
            print("\nExit by Control C")
    

    打开另一个 linux 控制台终端并使用 gdbus 实用程序来验证整数是否正在会话总线上发布。比如……

    $ gdbus monitor --session --dest org.example.project_1.server_1
    Monitoring signals from all objects owned by org.example.project_1.server_1
    The name org.example.project_1.server_1 is owned by :1.618
    /org/example/project_1/server_1: org.example.project_1.server_1.app_1_signal (36,)
    /org/example/project_1/server_1: org.example.project_1.server_1.app_1_signal (37,)
    /org/example/project_1/server_1: org.example.project_1.server_1.app_1_signal (25,)
    

    以下python3 / pydbus客户端程序订阅正在会话D-Bus上发布的随机整数...

    #!/usr/bin/env python3
    #!
    from pydbus.generic import signal
    from pydbus import SessionBus
    from gi.repository import GLib
    loop = GLib.MainLoop()
    dbus_filter = "/org/example/project_1/server_1"
    bus = SessionBus()
    
    
    def cb_server_signal_emission(*args):
        """
        Callback on emitting signal from server
        """
        print("Message: ", args)
        print("Data: ", str(args[4][0]))
    
    if __name__=="__main__":
        # Subscribe to bus to monitor for server signal emissions
        bus.subscribe(object = dbus_filter, 
                      signal_fired = cb_server_signal_emission)    
        loop.run()
    

    此客户端程序的控制台输出将与此类似...

    $ python3 client_pydbus.py
    Message:  (':1.621', '/org/example/project_1/server_1', 'org.example.project_1.server_1', 'app_1_signal', (72,))
    Data:  72
    Message:  (':1.621', '/org/example/project_1/server_1', 'org.example.project_1.server_1', 'app_1_signal', (46,))
    Data:  46
    Message:  (':1.621', '/org/example/project_1/server_1', 'org.example.project_1.server_1', 'app_1_signal', (47,))
    Data:  47
    

    【讨论】:

      【解决方案2】:

      负责信号的类位于generic 模块中。它看起来记录得很好:

      Static signal object
      
      You're expected to set it as a class property::
      
          class A:
              SomethingHappened = signal()
      
      Declared this way, it can be used on class instances
      to connect signal observers::
      
          a = A()
          a.SomethingHappened.connect(func)
      
      and emit the signal::
      
          a.SomethingHappened()
      
      You may pass any parameters to the emiting function
      - they will be forwarded to all subscribed callbacks.
      

      还有一个使用信号的example in the tutorial。注意最后一行和属性SomeProperty。当 setter 中的 python 属性SomeProperty 发生变化时,信号会通过self.PropertiesChanged("net.lew21.pydbus.TutorialExample", {"SomeProperty": self.SomeProperty}, []) 发出。

      from pydbus.generic import signal
      
      class Example(object):
        """
          <node>
            <interface name='net.lew21.pydbus.TutorialExample'>
              <method name='EchoString'>
                <arg type='s' name='a' direction='in'/>
                <arg type='s' name='response' direction='out'/>
              </method>
              <property name="SomeProperty" type="s" access="readwrite">
                <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
              </property>
            </interface>
          </node>
        """
      
        def EchoString(self, s):
          """returns whatever is passed to it"""
          return s
      
        def __init__(self):
          self._someProperty = "initial value"
      
        @property
        def SomeProperty(self):
          return self._someProperty
      
        @SomeProperty.setter
        def SomeProperty(self, value):
          self._someProperty = value
          self.PropertiesChanged("net.lew21.pydbus.TutorialExample", {"SomeProperty": self.SomeProperty}, [])
      
        PropertiesChanged = signal()
      

      还有一个notification_server example 使用信号(但不调用)。

      【讨论】: