dbus-python 指南
- 2020 年 1 月 6 日
- 筆記
dbus-python 指南
This tutorial requires Python 2.4 or up, and dbus-python 0.80rc4 or up.
指南需要:Python 2.4 或更高的版本, dbus-python 0,80rc4或更高的版本
Connecting to the Bus (连接Bus) Making method calls (制造一个方法调用) Proxy objects (代理对像) interfaces and methods (接口和方法) See also Data types (数据类型) Basic types (基本类型) Basic type conversions (基本类型约定) Container types (容器类型) Return values, and the byte_arrays and utf8_strings options (返回值, byte_arrays 和 utf8_strings 选项) Making asynchronous method calls (使用异步方法调用) Setting up an event loop (设置事件循环) Backwards compatibility: dbus.glib (向后兼容: dbus.glib) The Qt main loop (Qt main loop) Making asynchronous calls (创建异步调用) See also Receiving signals (接收信号) Signal matching (匹配信号) Getting more information from a signal (从信号中获取更多信息) String argument matching (匹配字符串参数) Receiviing signals from a proxy object (接收来自 proxy 对象的信号) See also Claiming a bus name (声名一个 bus name) The unique-instance idiom Exporting objects (导出对象) Inheriting from dbus.service.Object (继承 dbus.service.Object) Exporting methods with dbus.service.method (使用 dbus.service.method 导出方法) Finding out the caller's bus name Asynchronous method implementations (异步方法的实现) Emitting signals with dbus.service.signal Example License for this document
Connecting to the Bus (连接Bus)
使用 D-Bus 的应用程序常常连接到一个 bus 服务上,这个服务在应用程序之间传递消息。想要使用 D-Bus ,你需要创建一个 Bus 对象来代表一个到 bus 服务的连接。
import dbus session_bus = dbus.SessionBus()
import dbus system_bus = dbus.SystemBus()
Of course, you can connect to both in the same application.
Making method calls (制造一个方法调用)
D-Bus 应用程序能够输出对象让其它应用使用。为了能够使用另一个应用程序提供的对象,你需要知道:
- The bus name. This identifies which application you want to communicate with. You'll usually identify applications by a well-known name, which is a dot-separated string starting with a reversed domain name, such as org.freedesktop.NetworkManager or com.example.WordProcessor. bus名称(bus name)。它标识着你想与哪个应用程序进行通讯。你会常常通过众所周知的名称(well-known name)来标识一个应用程序,它是反转域名后用 `.' 分割的字符串,例如: org.freedesktop.NetworkManager 或 com.example.WordProcessor。
- The object path. Applications can export many object – for instance, example.com's word processor might provide an object representing the word processor application itself and an object for each document window opened, or it might also provide an object for each paragraph within a document. 对象路径(object path)。应用程序可以输出很多对象,例如, example.com 的文字处理进程会提供一个代表文字处理进程自身对象,还会为每一个打开的文档窗口都提供一个对象。或者它还可以为一个文档的每一段都提供一个对象。 To identify which one you want to interact with, you use an object path, a slash-separated string resembling a filename. For instance, example.com's word processor might provide an object at / representing the word processor itself, and objects at /documents /123 and /documents/345 representing opened document windows. 为了标识你想与谁通讯,你需要使用对象路径(object path),它是一个用 `/' 分割的字符串,就像文件名一样。例如, example.com 的文字处理进程会提供一个对像 `/' ,它代表文字进程自身, 和代其它已打开文档窗口的对象,/documents/123 和 /document/345 。
就像你期望的,你可以通过远程对象做的最主要的事情之一就是调用它们的方法。就像在 Python 中,方法有一些参数,它们会返回一个或多个值。
Proxy objects (代理对像)
import dbus bus = dbus.SystemBus() proxy = bus.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager/Devices/eth0') # proxy is a dbus.proxies.ProxyObject
interfaces and methods (接口和方法)
为了调用一个方法,在 proxy object 上调用同样名称的方法,通过 dbus_interface 关键词参数传递接口的名称。
import dbus bus = dbus.SystemBus() eth0 = bus.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager/Devices/eth0') props = eth0.getProperties(dbus_interface='org.freedesktop.NetworkManager.Devices') # props is a tuple of properties, the first of which is the object path
如果你要用同样的接口调用很多的方法,作为一个快捷的办法,你可构造一个 dbus.Interface 对像,然后在它上面调用方法,而不需要再次指定接口:
import dbus bus = dbus.SystemBus() eth0 = bus.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager/Devices/eth0') eth0_dev_iface = dbus.Interface(eth0, dbus_interface='org.freedesktop.NetworkManager.Devices') props = eth0_dev_iface.getProperties() # props is the same as before
See also
参考 examples/example-client.py 中的例子。在运行它之前,你需要在后台或在另一个 shell 中运行 examples/example-service.py。
Data types (数据类型)
不像 Python , D-Bus 是静态类型 (statically typed) – 每一个方法都有一个代表它们参数类型的标识,并且不接受其它类型的参数。
Basic types (基本类型)
The following basic data types are supported.
Python type |
converted to D-Bus type |
notes |
---|---|---|
unicode or subclass |
string ('s') |
|
D-Bus proxy object |
ObjectPath (signature 'o') |
(+) |
dbus.Interface |
ObjectPath (signature 'o') |
(+) |
dbus.service.Object |
ObjectPath (signature 'o') |
(+) |
dbus.Boolean |
Boolean (signature 'b') |
a subcalss of int |
dbus.Byte |
byte (signature 'y') |
a subclass of int |
dbus.Int16 |
16-bit signed integer ('n') |
a subclass of int |
dbus.Int32 |
32-bit signed integer ('i') |
a subclass of int |
dbus.Int64 |
64-bit signed integer ('x') |
(*) |
dbus.Uint16 |
16-bit unsigned integer ('q') |
a subclass of int |
dbus.Uint32 |
32-bit unsigned integer ('u') |
(*) |
dbus.Uint64 |
64-bit unsigned integer ('t') |
(*) |
dbus.Double |
double-precision float ('d') |
a subclass of float |
dbus.ObjectPath |
object path ('o') |
a subclass of str |
dbus.Signature |
signature ('g') |
a subclass of str |
dbus.String |
string ('s') |
a subclass of unicode |
dbus.UTF*String |
string ('s') |
a subclass of str |
bool |
Boolean ('b') |
|
int or subclass |
32-bit signed inter ('i') |
|
long or subclass |
64-bit signed integer ('x') |
|
float or subclass |
double-precision float ('d') |
|
str or subclass |
string ('s') |
must be valid UTF-8 |
Types marked (*
) may be a subclass of either int or long, depending on platform.
标记为(*
)的类型是 int 或 long 的子类,平台相关。
Basic type conversions (基本类型约定)
If introspection succeeded, dbus-python will also accept:
如果 introspection 成功, dbus-python 将接受:
- for Boolean parameters, any object (converted as if via int(bool(…))) 对于 Boolean 参数,任何 object (被转换通过 int(bool(…)))
- for byte parameters, a single-character string (converted as if via ord()) 对于 byte 参数,一个单字符串 (single-character string) (被转换通过 ord())
- for byte and integer parameters, any integer (must be in the correct range) 对于 byte 和 integer 参数,任何 integer (必须在正确的范围内)
- for object-path and signature parameters, any str or unicode subclass (the value must follow the appropriate syntax) 对于 object-path 和 signature 参数,任何 str 或 unicode subclass (必须有适当的语义)
Container types (容器类型)
一个数组的标识是 'ax' ,这里的 'x' 代表一个元素的标识。例如,你可以使用 'as' (字符串数组) 或 'a(ii)' (包含两个 32-bit 整数的结构体数组)
dbus.ByteArray是一个字符字符串的子类,被用开更有效率的代表 D-Bus的字节数组 (标识 'ay')。
由内容的标识的构成的结构体的标识在括号里 – 例如, '(is)' 是一个包含一个 32-bit 整数和字符串的标识。
变体 (variants) … (不会翻)。如果 non-variant 作为一个参数被传递,但 introspection 指示期望一个 variant ,它将会自动的包在一个变体中。
The signature of a variant is 'v'.
Return values, and the byte_arrays and utf8_strings options (返回值, byte_arrays 和 utf8_strings 选项)
If a D-Bus method returns no value, the Python proxy method will return None.
如果 D-Bus 方法没有返回值,那么 Python 代理方法将会返回 None.
如果 D-Bus 方法返回多个值,那么 Python 代理方法会返回一个元组包含这些值。
如果你想要字符串作为 dbus.UTF8String (str 的子类) 传递关键词参数 utf8_strings=True 到代理方法。
如果你要字节数组作为 dbus.ByteArray (也是 str 的子类 – 实践中,它常常是你想要的)传递关键词参数 byte_arrays=True 到代理方法。
Making asynchronous method calls (使用异步方法调用)
异步(非阻塞)方法调用允许同时有多个方法正在调用,允许你的应用在等待结果的时候可以做一些其它的工作。为了使用异步调用,你首先需要一个事件循环或 "main loop"。
Setting up an event loop (设置事件循环)
Currently, the only main loop supported by dbus-python is GLib.
目前,dbus-python 唯一支持的 main loop 是 GLib 。
dbus-python 有一个全局默认的 main loop ,它是使用这个功能最容易的方法。把 GLib main loop 设置为默认,使用:
from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True)
You must do this before connecting to the bus.
Actually starting the main loop is as usual for pygobject:
实际上常常为 pygobject 启动 main loop。
import gobject loop = gobject.MainLoop() loop.run()
当 loop.run() 在运行, GLib 将会在适当的时候运行你的回调函数。调用 loop.quit() 停止。
你可以在每一个连接的基础上设置一个 main loop,通过传递一个 main loop 到 Bus 构造函数。
import dbus from dbus.mainloop.glib import DBusGMainLoop dbus_loop = DBusGMainLoop() bus = dbus.SessionBus(mainloop=dbus_loop)
This isn't very useful until we support more than one main loop, though.
Backwards compatibility: dbus.glib (向后兼容: dbus.glib)
In versions of dbus-python prior to 0.80, the way to set GLib as the default main loop was:
在 dbus-python 0.80 之前的版本里,设置 GLib 作为默认 main loop 的方法是:
执行那个 import 语句将会自动的加载 GLib main loop 并设置它为默认。不赞成使用它,因为它是高不可见的,但是如果你想写或者理解向后兼容的代码是有用的。
The Qt main loop (Qt main loop)
Making asynchronous calls (创建异步调用)
- the reply_handler will be called with the method's return values as arguments; or reply_handler 将会被调用,它的参数是上面方法的返回值。
- the error_handler will be called with one argument, an instance of DBusException representing a remote exception. error_handler 将被调用,它的参数是一个代表远程异常的 DBusException 的一个实例。
See also
Receiving signals (接收信号)
为了收到一个信号,Bus 需要被连接到一个事件循环 – 参考设置事件循环那章。信号只在事件循环运行的时候才能收到。
Signal matching (匹配信号)
为了对信息产生响应,你要在 Bus object 上使用 add_signal_receiver 方法。当一个匹配的信号收到后,安排好的回调函数将被调用,传入以后参数:
- a callable (the handler_function) which will be called by the event loop when the signal is received – its parameters will be the arguments of the signal 当收到信号时,一个可调函数 (callable) (the handler_fucntion) 将会被事件循环调用 – 它的参数是信息的参数。
- the signal name, signal_name: here None (the default) matches all names 信号名称,signal_name: 这里为 None (默认) 则匹配所有名称。
- the D-Bus interface, dbus_interface: again None is the default, and matches all interfaces D-Bus 接口, dbus_interface: None 是默认的,匹配所有接口。
- a sender bus name (well-known or unique), bus_name: None is again the default, and matches all senders. Well-known names match signals from whatever application is currently the primary owner of that well-known name. 发送者的 bus 名称 (well-known 或 unique),bus_name: None是默认的,匹配所有发送者。Well-known 名称匹配来自当前拥有那个 well-known 名称的应用程序的信号,无论应用程序是谁。
- a sender object path, path: once again None is the default and matches all object paths 一个发送者的 object path,path: None 是默认的,匹配所有 object paths,
add_signal_receiver 返回一个 SignalMatch 对像。目前,它唯一有用的公共 API 是一个没有参数移除方法,它用来移除从连接上匹配的连接。
Getting more information from a signal (从信号中获取更多信息)
def handler(sender=None): print "got signal from %r" % sender iface.connect_to_signal("Hello", handler, sender_keyword='sender')
一个来自 com.example.Foo 的没有参数的 Hello 信号将会被收到,处理函数将会被调用,它的参数 sender='com.example.Foo'。
String argument matching (匹配字符串参数)
如果有关键词的格式是 argn,其中 n 是一个小的非负数,它们的值必须是 unicode 对象 或 UTF-8 字符串。(后面理不清)
Receiviing signals from a proxy object (接收来自 proxy 对象的信号)
Proxy 对象有一个特殊的方法 connect_to_signal ,当收到一个来自相应远程对象的信号时, connect_to_signal 将安排一个回调函数被调用。
- the name of the signal 信号名称
- a callable (the handler function) which will be called by the event loop when the signal is received – its parameters will be the arguments of the signal 当收到信号时,一个可调函数 (callable) (the handler_fucntion) 将会被事件循环调用 – 它的参数是信息的参数。
- the handler function, a callable: the same as for add_signal_receiver 处理函数 (callable): 与 add_signal_receiver 相同
- the keyword argument dbus_interface qualifies the name with its interface 关键词参数 dbus_interface 限定接口的名称。
dbus.Interface 对象有一个相似的 connect_to_signal 方法,但这种情况下,你不需要 dbus_interface 关键词参数,因为使用的接口已经知道了。
对于 add_signal_receiver 而言,同样的关键词参数也是可用的,就像 add_signal_receiver , 它返回一个 SignalMatch 。
你不应该只用 proxy 对象来监听信号,因为当他们创建的时候也许会激活相关的服务,但如果你为了调用方法已经有一个 proxy 对象,使用它添加信号匹配常常很方便。
See also
Claiming a bus name (声名一个 bus name)
FIXME describe BusName – perhaps fix its API first?
The unique-instance idiom
FIXME provide exemplary code, put it in examples
Exporting objects (导出对象)
在 D-Bus 上让另外一个应用程序可用的对像称作导出 (exported)。所有的 dbus.service.Object 的子类是自动被 exported。
为了 export 对象, Bus 需要连接到事件循环 – 参考设置事件循环那章。只有在事件循环运行时,导出方法也才会被调用,队列中的信号才会被传递。
Inheriting from dbus.service.Object (继承 dbus.service.Object)
class Example(dbus.service.Object): def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), path)
这个对象会自动支持 introspection ,但不做任何特别的事情。为了修正它,你需要导出一些方法和信号。
FIXME also mention dbus.gobject.ExportedGObject once I've written it
Exporting methods with dbus.service.method (使用 dbus.service.method 导出方法)
To export a method, use the decorator dbus.service.method. For example:
为了导出方法,使用 dbus.service.method 操作,例如:
class Example(dbus.service.Object): def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), path) @dbus.service.method(dbus_interface='com.example.Sample', in_signature='v', out_signature='s') def StringifyVariant(self, variant): return str(variant)
The in_signature and out_signature are D-Bus signature strings as described in Data Types.
in_signature 和 out_signature 是 D-Bus 的标识字符串,描述数据的类型。
你可以找到一个简单的例子 examples/example-service.py ,我们先前用来它来示范 examples/example-client.py 。
Finding out the caller's bus name
方法描述接受 sender_keyword 关键词参数。如果你把它设置为一个字符串,发送者的唯一 bus 名称将作为一个关键词参数传递到描述方法。
class Example(dbus.service.Object): def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), path) @dbus.service.method(dbus_interface='com.example.Sample', in_signature='', out_signature='s', sender_keyword='sender') def SayHello(self, sender=None): return 'Hello, %s!' % sender # -> something like 'Hello, :1.1!'
Asynchronous method implementations (异步方法的实现)
FIXME and also add an example, perhaps examples/example-async-service.py
Emitting signals with dbus.service.signal
class Example(dbus.service.Object): def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), path) @dbus.service.signal(dbus_interface='com.example.Sample', signature='us') def NumberOfBottlesChanged(self, number, contents): print "%d bottles of %s on the wall" % (number, contents) e = Example('/bottle-counter') e.NumberOfBottlesChanged(100, 'beer') # -> emits com.example.Sample.NumberOfBottlesChanged(100, 'beer') # and prints "100 bottles of beer on the wall"
Example
当发送信号的方法之一被调用, examples/example-signal-emitter.py 会按要求发送一些信号。