dbus-python指南

仿照李大神翻译,主要是练英语!查看英文原版请点这里

dbus-python 指南

作者:Simon McVittie, Collabora Ltd.
日期:2006-06-14

本教程需要 Python 2.4版本或更高版本,dbus-python 0.80rc4 版本或更高版本.

目录

  • 连接总线
  • 创建方法
    • 代理对象
    • 接口和方法
    • 数据类型
      • 基本类型
      • 基本类型转换
      • 容器类型
      • 返回值,字节数组和utf8-字符串选项。
  • 异步方法调用
    • 建立一个事件循环
      • 向后兼容:dbus.glib
      • Qt的主循环
    • 异步调用
  • 接收信号
    • 信号匹配
    • 获取一个信号的更多信息
    • 匹配参数
    • 从代理对象接收信号
  • 声明总线名称
    • 独特的实例成语
  • 导出对象

连接总线(Connecting to the Bus

使用D-Bus的应用程序通常连接到一个总线服务上,这个服务用于程序间传递信息。要使用D-Bus,您需要创建一个总线对象来代表到总线服务的连接

一般来说,您感兴趣的总线服务有两种:会话总线,系统总线。每个用户登录会话应该有一个会话总线,它是局部的,在本会话内有效,用于桌面程序间的通讯。通过创建一个SessionBus对象连接一个会话总线:

import dbus
session_bus = dbus.SessionBus()

系统总线是全局的,通常在系统启动的时候启动,它常用于系统服务间的通信,像 udevNetworkManager, 和 the Hardware Abstraction Layer daemon (hald). 要使用系统总线,要创建一个SystemBus对象:

import dbus
system_bus = dbus.SystemBus()

当然您可以在一个程序里同时使用这两种总线。

对于特殊用途,您或许使用非默认的总线,或者根本连接不到总线上。这些可以通过dbus-python 0.81.0新添加的API实现. 这里不做介绍,有专门的介绍它们的指南.

方法调用(Making method calls

D-Bus应用程序可以导出对象给其他应用程序使用,要在其他应用程序里使用对象工作,需要知道:

  • 总线名称. 它标识与之通信的应用程序,我们通常用一个众所周知(well-known)的名字标识应用程序。这个众说周知的名字使用反向域名的点分字符串,例如:org.freedesktop.NetworkManager 或者 com.example.WordProcessor.

  • 对象路径.应用程序可以导出许多对象,例如:example.com的文档处理程序可能提供一个对象代表文档处理程序自身,并且为每个打开的文档窗口创建一个对象,还可能为文档中的每一个段落创建一个对象。     

    我们使用对象路径标识要作用哪一个对象,对象路径是一个由反斜线作为分隔的字符串文件名,例如example.com的文档处理程序或许提供一个对象路径为/的对象代表它自己,还提供对象路径为 /documents/123 和 /documents/345 的对象代表打开的窗口。

如你所愿,对于远程对象你能做的最主要的事就是调用它们的方法。在Python中,方法可能有参数,也可能有一个或多个返回值。

代理对象(Proxy objects

为了和远端的对象通信,我们使用代理对象,代理对象是一个Python对象,它是远端对象的一个代理或替身-当调用代理对象的一个方法时,会导致dbus-python对远端对象的方法调用,并将远端对象的返回值作为代理对象方法调用的返回值。 

可以对Bus调用get_object方法来获取代理对象。例如,NetworkManager有一个众所周知的名称org.freedesktop.NetworkManager, NetworkManager export一个对象路径为/org/freedesktop/NetworkManager的对象。NetworkManager为每一个网络接口(network interface)都在对象路径下创建一个对象,例如,/org/freedesktop/NetworkManager/Devices/eth0。你可以像这样获取一个代理对象来代理eth0:

import dbus
bus = dbus.SystemBus()
proxy = bus.get_object('org.freedesktop.NetworkManager',
                       '/org/freedesktop/NetworkManager/Devices/eth0')
# proxy 是一个dbus.proxies.ProxyObject类型的对象

接口和方法(Interfaces and methods

D-Bus使用接口(interfaces)给方法(methods)提供命名空间的机制。一个接口(interface)就是一组相关的方法和信号(后面再介绍信号),接口名字是一个反域名的点分式字符串。例如,每一个代表一个网络接口的NetworkManager对象都实现了接口org.freedesktop.NetworkManager.Devices,在这个接口中有一个方法是gerProperties。 
要调用一个方法,就在代理对象上调用相同的方法,并通过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 是一个属性元组,元组的第一项是对象路径。

简便起见,如果你要在同一个接口上调用多个方法时,可以先构造一个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()
#  同样是一个属性元组,元组的第一项是对象路径。

更多参见

可以参考examples/example-client.py中的例子。在运行它之前,你需要在后台或者另一个shell上运行examples/example-service.py

数据类型(Data types

与Python不同,D-Bus使用静态类型-每个方法都有一个标识来代表其参数的类型,其他类型的参数是不被接受的。 
D-Bus有一个内省机制(introspection mechanism),dbus-python通过这个机制来尝试发现正确的参数类型。如果尝试成功,Python类型会被自动转化为正确的D-Bus数据类型,如果可以;如果类型不恰当,会报TypeError错误。 
如果内省失败(或者参数类型可变-详见下文),你必须提供正确的参数类型。dbus-python根据D-Bus的数据类型提供了相应的Python类型,一些Python类型可以自动的转化为D-Bus数据类型。如果你使用这些类型之外的类型时,会TypError出错,提示你dbus-python不能猜出D-Bus的标识。

基本类型(Basic types

以下是支持的基本数据类型。

Python type converted to D-Bus type notes
D-Bus proxy objectObjectPath (signature 'o')(+)
dbus.InterfaceObjectPath (signature 'o')(+)
dbus.service.ObjectObjectPath (signature 'o')(+)
dbus.BooleanBoolean (signature 'b')a subclass of int
dbus.Bytebyte (signature 'y')a subclass of int
dbus.Int1616-bit signed integer ('n')a subclass of int
dbus.Int3232-bit signed integer ('i')a subclass of int
dbus.Int6464-bit signed integer ('x')(*)
dbus.UInt1616-bit unsigned integer ('q')a subclass of int
dbus.UInt3232-bit unsigned integer ('u')(*)_
dbus.UInt6464-bit unsigned integer ('t')(*)_
dbus.Doubledouble-precision float ('d')a subclass of float
dbus.ObjectPathobject path ('o')a subclass of str
dbus.Signaturesignature ('g')a subclass of str
dbus.Stringstring ('s')a subclass of unicode
dbus.UTF8Stringstring ('s')a subclass of str
boolBoolean ('b') 
int or subclass32-bit signed integer ('i') 
long or subclass64-bit signed integer ('x') 
float or subclassdouble-precision float ('d') 
str or subclassstring ('s')must be valid UTF-8
unicode or subclassstring ('s') 

标记(*)的类型,在不同的平台上可能是int的子类或者long的子类。 
(+):D-Bus proxy objects, exported D-Bus service objects 以及其他任何拥有 dbus_object_path 属性(必须为字符串,可以转化为对象路径)的对象。这在你使用dbus-python编写面向对象的API时,可能对你有帮助。

基本类型转换(Basic type conversions

如果内省成功,dbus-python还接受:

  • 对于Boolean参数,任何对象(经由int(bool(…))转化)
  • 对于byte参数,a single-character string,即,单字字符串(经由ord()转化)
  • 对于byte和integer参数,任何integer(必须在正确的区间)
  • 对于object-path和signature参数,任何str或者unicode的子类(值必须遵循正确的语法)。

容器类型(Container types

D-Bus支持4种容器类型:array(包含同种类型数据的可变长度队列), struct(一个固定长度队列,它的成员可能是不同的数据类型),dictionary(从同一基本类型到同一类型的映射),variant(一种可以存储任何D-Bus数据类型的容器,包含另一variant在内)。

Array的代表是Python的list或者dbus.Array(list的一个子类)。发送一个array时,如果内省标识(introspected signature)可用,则使用内省标识;否则,如果传递了signature关键字参数给array的构造函数,则使用传递的signature来作为array内容的signature;如果内省失败,也没有给array的构造函数传递signature关键字参数,则dbus-python将根据array的第一个元素来猜测其signature.

一个array的signature格式为’ax’,其中的’x’代表array元素的signature。例如,你可以用’as’表示字符串array(array of strings),或者用’a(ii)’表示结构体数组(array of structs each containing two 32-bit integer),这个array的元素是有2个32-bit整数组成的结构体。

还有一种类型dbus.ByteArray,它是str的一个子类。这种类型被广泛用于代表一个D-Bus的字节数组(signature ‘ay’)。

Struct的代表是Python的tuple或者dbus.Struct(tuple的一个子类)。当发送一个struct时,如果内省的signature可用,则使用内省的signature;否则,如果传递signature关键字参数给Struct(指南中使用的的Array,个人感觉是写错了,不是是否理解有误)的构造函数,则使用此传递的signature作为struct内容的signature;否则dbus-python将通过struct(同样,指南原文使用的是Array)的第一个元素来猜测signature(这里个人有一点小疑问,struct的成员数据类型是不一定相同的,这个要怎么根据第一个元素猜?)。

一个struct的signature是用一个元括号将其成员的signature括起来。例如’(is)’是一个struct的signature,这个struct包含了一个32-bit的integer成员和一个string类型成员。

Dictionary的代表是Python的Dictionary或者dbus.Dictionary(一个dict的子类)。当发送一个dictionary时,如果内省的signature可用,则使用内省signature;否则,如果给Dictionary的构造函数传递了signature关键字参数,则使用传递的signature作为dictionary内部的key和value的signature;否则,dbus-python会根据dict中随意的一个item来猜测其signature。

dictionary的signature的格式为’a{xy}’,其中,’x’是dictionary中key的signature(它不能是容器的类型),’y‘是dictionary中的value的signature。例如,’a{s(ii)}’是一个dictionary的signature,这个dictionary的key是一个字符串,value是一个由2个32-bit的整数组成的结构体。

variant的代表是在D-Bus的任意一个数据类型上,通过其构造函数将关键字参数variant_level的值设置为一个大于0的数。(variant_level 1表示一个包含一些其他数据类型(不能是variant类型)的成员variant。variant_level 2 表示一个包含另一个variant(被包含的variant是一个包含除了variant类型之外的其他数据成员的variant)成员的variant,等等)。如果将一个非variant(non-variant)数据传递给一个内省要求为variant的参数,则,这个非variant数据会被自动封装为variant类型。

variant的signature是’v’。


返回值,字节数组和utf8-strings选项(Return values, and the byte_arrays and utf8_strings options

如果一个D-Bus方法没有返回值,则Python的代理方法返回None。

如果一个D-Bus方法有一个返回值,则Python代理方法会根据dbus.Types的相应类型返回那个数值-默认情况下,如果方法返回dbus.String(Unicode的一个子类)类型,则代理方法返回string。如果方法返回值是由dbus.Byte组成的dbus.Array,则Python代理方法返回array。

如果一个D-Bus方法返回多个返回值,则Python代理方法返回一个包含这些数值的tuple。

如果你想让返回的string类型是dbus.UTF8String(str的一个子类),需要给python的代理方法传递关键字参数utf8_string=True。

如果你想让byte array数值以dbus.ByteArray(同样也是str的子类,在实践中,这个类型经常是你最需要的)的类型返回,需要给python代理方法传递关键字参数byte_arrays=True。

异步方法调用(Making asynchronous method calls

异步(非阻塞)方法调用允许多个方法调用同时进行,并且允许你在等待返回结果之前做其他事情。要使用异步方法调用,你首先需要一个事件循环或者“主循环”(main loop)。

设置一个事件循环(Setting up an event loop

当前,dbus-python唯一支持的主循环(main loop)就是GLib。 
dbus-python有一个全局默认的主循环,这是使用这个功能的最简便方法。要把GLib设置为默认主循环可以通过:

from dbus.mainloop.glib import DBusGMainLoop

DBusGMainLoop(set_as_default=True)

这些操作必须要在连接到bus服务之前完成。 
可以通过pygi开始运行主循环:

from gi.repository import GLib

loop = GLib.MainLoop()
loop.run()

当loop.run()正在执行时,GLib会在适当的时候调用你的回调函数。要停止事件循环,调用loop.quit()。 
你也可以通过在连接Bus服务时,通过传递一个main loop给Bus的构造函数,这样也可以设置主循环:

import dbus
from dbus.mainloop.glib import DBusGMainLoop

dbus_loop = DBusGMainLoop()

bus = dbus.SessionBus(mainloop=dbus_loop)

这个功能不是很有用,除非我们能够支持多个主循环。

向后兼容:dbus.glib

在dbus-python 0.80以前的版本, 设置GLib作为默认主循环的方式是:

import dbus.glib

执行以上import语句会自动加载GLib的主循环并将其作为默认设置。这中做法由于太不明显已经被抛弃。但是知道这个事情对你要编写或者理解以前版本的代码还是有用的。

Qt主循环(The Qt main loop

PyQt V4.2和其以后版本支持将dbus-python整合到Qt事件循环中。要把D-Bus连接到Qt的主循环中,掉用dbus.mainloop.qt.DBusQtMainLoop 
代替dbus.mainloop.glib.DBusGMainLoop即可。除此以外,Qt循环和GLib循环的使用方式一样。

异步调用(Making asynchronous calls

要做异步调用,需要传递2个可调用的关键字参数reply_handler和error_handler给代理方法(代理对象的方法)。代理方法会理解返回None。一段时间以后,在事件循环的运行过程中,会发生以下两种情况之一:

  • reply_handler以method的返回值作为参数被调用;或者
  • error_handler以一个代表远端异常的DBusException实例为参数被调用。

更多参见

examples/example-async-client.py异步调用了examples/example-service.py提供的服务,这些服务会返回一个值或者异常。与examples/example-client.py一样,你也需要先在后台或者另一个shell运行examples/example-service.py

接收信号(Receiving signals

To receive signals, the Bus needs to be connected to an event loop - see section Setting up an event loop. Signals will only be received while the event loop is running.

信号匹配(Signal matching

To respond to signals, you can use the add_signal_receiver method on Bus objects. This arranges for a callback to be called when a matching signal is received, and has the following arguments:

  • 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
  • the signal name, signal_name: here None (the default) matches all names
  • the D-Bus interface, dbus_interface: again None is the default, and matches all interfaces
  • 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.
  • a sender object path, path: once again None is the default and matches all object paths

add_signal_receiver also has keyword arguments utf8_strings and byte_arrays which influence the types used when calling the handler function, in the same way as thebyte_arrays and utf8_strings options on proxy methods.

add_signal_receiver returns a SignalMatch object. Its only useful public API at the moment is a remove method with no arguments, which removes the signal match from the connection.

从一个信号获取更多信息(Getting more information from a signal

You can also arrange for more information to be passed to the handler function. If you pass the keyword arguments sender_keyworddestination_keywordinterface_keyword,member_keyword or path_keyword to the connect_to_signal method, the appropriate part of the signal message will be passed to the handler function as a keyword argument: for instance if you use

def handler(sender=None):
    print "got signal from %r" % sender

iface.connect_to_signal("Hello", handler, sender_keyword='sender')

and a signal Hello with no arguments is received from com.example.Foo, the handler function will be called with sender='com.example.Foo'.

字符串参数匹配(String argument matching

If there are keyword parameters for the form argn where n is a small non-negative number, their values must be unicode objects or UTF-8 strings. The handler will only be called if that argument of the signal (numbered from zero) is a D-Bus string (in particular, not an object-path or a signature) with that value.

从一个代理对象接收信号(Receiving signals from a proxy object

Proxy objects have a special method connect_to_signal which arranges for a callback to be called when a signal is received from the corresponding remote object. The parameters are:

  • 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
  • the handler function, a callable: the same as for add_signal_receiver
  • the keyword argument dbus_interface qualifies the name with its interface

dbus.Interface objects have a similar connect_to_signal method, but in this case you don't need the dbus_interface keyword argument since the interface to use is already known.

The same extra keyword arguments as for add_signal_receiver are also available, and just like add_signal_receiver, it returns a SignalMatch.

You shouldn't use proxy objects just to listen to signals, since they might activate the relevant service when created, but if you already have a proxy object in order to call methods, it's often convenient to use it to add signal matches too.

更多参见

examples/signal-recipient.py receives signals - it demonstrates general signal matching as well as connect_to_signal. Before running it, you'll need to runexamples/signal-emitter.py in the background or in another shell.

声明总线名称(Claiming a bus name

FIXME describe BusName - perhaps fix its API first?

独特的实例成语(The unique-instance idiom

FIXME provide exemplary code, put it in examples

导出对象(Exporting objects

Objects made available to other applications over D-Bus are said to be exported. All subclasses of dbus.service.Object are automatically exported.

To export objects, the Bus needs to be connected to an event loop - see section Setting up an event loop. Exported methods will only be called, and queued signals will only be sent, while the event loop is running.

dbus.service.Object继承

To export an object onto the Bus, just subclass dbus.service.Object. Object expects either a BusName or a Bus object, and an object-path, to be passed to its constructor: arrange for this information to be available. For example:

class Example(dbus.service.Object):
    def __init__(self, object_path):
        dbus.service.Object.__init__(self, dbus.SessionBus(), path)

This object will automatically support introspection, but won't do anything particularly interesting. To fix that, you'll need to export some methods and signals too.

FIXME also mention dbus.gobject.ExportedGObject once I've written it

从dbus.service.method导出方法(Exporting methods with dbus.service.method)

To export a method, use the decorator dbus.service.method. For example:

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.

As well as the keywords shown, you can pass utf8_strings and byte_arrays keyword arguments, which influence the types which will be passed to the decorated method when it's called via D-Bus, in the same way that the byte_arrays and utf8_strings options affect the return value of a proxy method.

You can find a simple example in examples/example-service.py, which we used earlier to demonstrate examples/example-client.py.

找出调用者的总线名称(Finding out the caller's bus name

The method decorator accepts a sender_keyword keyword argument. If you set that to a string, the unique bus name of the sender will be passed to the decorated method as a keyword argument of that name:

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

用dbus.service.signal发射信号(Emitting signals with dbus.service.signal

To export a signal, use the decorator dbus.service.signal; to emit that signal, call the decorated method. The decorated method can also contain code which will be run when called, as usual. For example:

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"

The signal will be queued for sending when the decorated method returns - you can prevent the signal from being sent by raising an exception from the decorated method (for instance, if the parameters are inappropriate). The signal will only actually be sent when the event loop next runs.

例如:

examples/example-signal-emitter.py emits some signals on demand when one of its methods is called. (In reality, you'd emit a signal when some sort of internal state changed, which may or may not be triggered by a D-Bus method call.)

  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值