From 5284ed3979d7c27f84bd0496682fb63b5e60ed5f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 25 Jan 2007 16:42:54 +0000 Subject: * doc/dbus-tutorial.xml: Replace Python section of tutorial with a pointer to the tutorial maintained as part of dbus-python --- doc/dbus-tutorial.xml | 548 +------------------------------------------------- 1 file changed, 5 insertions(+), 543 deletions(-) (limited to 'doc') diff --git a/doc/dbus-tutorial.xml b/doc/dbus-tutorial.xml index add59e1a..5c385f0e 100644 --- a/doc/dbus-tutorial.xml +++ b/doc/dbus-tutorial.xml @@ -1640,551 +1640,13 @@ my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error) - Python API: Using Remote Objects + Python API - The Python bindings provide a simple to use interface for talking over D-Bus. - Where possible much of the inner-workings of D-Bus are hidden behind what looks - like normal Python objects. + The Python API, dbus-python, is now documented separately in + the dbus-python tutorial (also available in doc/tutorial.txt, + and doc/tutorial.html if built with python-docutils, in the dbus-python + source distribution). - - D-Bus - Python type mappings - - While python itself is a largely untyped language D-Bus provides a simple type system - for talking with other languages which may be strongly typed. Python for the most part - tries automatically map python objects to types on the bus. It is none the less good to - know what the type mappings are so one can better utilize services over the bus. - - - Basic type mappings - - Below is a list of the basic types, along with their associated - mapping to a Python object. - - - - - D-Bus basic type - Python wrapper - Notes - - - - - BYTE - dbus.Byte - - - BOOLEAN - dbus.Boolean - Any variable assigned a True or False boolean value will automatically be converted into a BOOLEAN over the bus - - INT16 - dbus.Int16 - - - UINT16 - dbus.UInt16 - - - INT32 - dbus.Int32 - This is the default mapping for Python integers - - UINT32 - dbus.UInt32 - - - INT64 - dbus.Int64 - - - UINT64 - dbus.UInt64 - - - DOUBLE - dbus.Double - Any variable assigned a floating point number will automatically be converted into a DOUBLE over the bus - - STRING - dbus.String - Any variable assigned a quoted string will automatically be converted into a STRING over the bus - - OBJECT_PATH - dbus.ObjectPath - - - - - - - - - Container type mappings - - The D-Bus type system also has a number of "container" - types, such as DBUS_TYPE_ARRAY and - DBUS_TYPE_STRUCT. The D-Bus type system - is fully recursive, so one can for example have an array of - array of strings (i.e. type signature - aas). - - - D-Bus container types have native corresponding built-in Python types - so it is easy to use them. - - - - - D-Bus type - Python type - Python wrapper - Notes - - - - - ARRAY - Python lists - dbus.Array - Python lists, denoted by square brackets [], are converted into arrays and visa versa. - The one restriction is that when sending a Python list each element of the list must be of the same - type. This is because D-Bus arrays can contain only one element type. Use Python tuples for mixed types. - - When using the wrapper you may also specify a type or signature of the elements contained in the Array. - This is manditory when passing an empty Array to a method on the bus because Python can not guess at the - contents of an empty array. For example if a method is expecting an Array of int32's and you need to pass - it an empty Array you would do it as such: - - emptyint32array = dbus.Array([], type=dbus.Int32) - - or - - emptyint32array = dbus.Array([], signature="i") - - Note that dbus.Array derives from list so it acts just like a python list. - - - - STRUCT - Python tuple - dbus.Struct - Python tuples, denoted by parentheses (,), are converted into structs and visa versa. - Tuples can have mixed types. - - - DICTIONARY - Python dictionary - dbus.Dictionary - D-Bus doesn't have an explicit dictionary type. Instead it uses LISTS of DICT_ENTRIES to - represent a dictionary. A DICT_ENTRY is simply a two element struct containing a key/value pair. - Python dictionaries are automatically converted to a LIST of DICT_ENTRIES and visa versa. - - Since dictonaries are described as lists of dict_entries we also need the signature in order - to pass empty dictionaries. The wrapper provides a way of specifying this through the key_type/value_type - type parameters or the signature parameters. To send an empty Dictionary where the key is a string - and the value is a string you would do it as such: - - emptystringstringdict = dbus.Dictionary({}, key_type=dbus.String, value_type=dbus.Value) - - or - - emptystringstringdict = dbus.Dictionary({}, signature="ss") - - Note that dbus.Dictionary derives from dict so it acts just like a python dictionary. - - - - VARIANT - any type - dbus.Variant - A variant is a container for any type. Python exports its methods to accept only variants - since we are an untyped language and can demarshal into any Python type. - - To send a variant you must first wrap it in adbus.Variant. If no type or signiture is - given to the variant the marshaler will get the type from the contents. - - - - - - - - - - Invoking Methods - Here is a D-Bus program using the Python bindings to get a listing of all names on the session bus. - -import dbus - -bus = dbus.SessionBus() -proxy_obj = bus.bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') -dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus') - -print dbus_iface.ListNames() - - - - Notice I get an interface on the proxy object and use that to make the call. While the specifications - state that you do not need to specify an interface if the call is unambiguous (i.e. only one method implements - that name) due to a bug on the bus that drops messages which don't have an interface field you need to specify - interfaces at this time. In any event it is always good practice to specify the interface of the method you - wish to call to avoid any side effects should a method of the same name be implemented on another interface. - - - You can specify the interface for a single call using the dbus_interface keyword. - -proxy_obj.ListNames(dbus_interface = 'org.freedesktop.DBus') - - - - This is all fine and good if all you want to do is call methods on the bus and then exit. In order to - do more complex things such as use a GUI or make asynchronous calls you will need a mainloop. You would use - asynchronous calls because in GUI applications it is very bad to block for any long period of time. This cause - the GUI to seem to freeze. Since replies to D-Bus messages can take an indeterminate amount of time using async - calls allows you to return control to the GUI while you wait for the reply. This is exceedingly easy to do in - Python. Here is an example using the GLib/GTK+ mainloop. - -import gobject -import dbus -if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - -def print_list_names_reply(list): - print str(list) - -def print_error(e): - print str(e) - -bus = dbus.SessionBus() -proxy_obj = bus.bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') -dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus') - -dbus_iface.ListNames(reply_handler=print_list_names_reply, error_handler=print_error) - -mainloop = gobject.MainLoop() -mainloop.run() - - - - In the above listing you will notice the reply_handler and error_handler keywords. These tell the method that - it should be called async and to call print_list_names_reply or print_error depending if you get a reply or an error. - The signature for replys depends on the number of arguments being sent back. Error handlers always take one parameter - which is the error object returned. - - - You will also notice that I check the version of the dbus bindings before importing dbus.glib. In older versions - glib was the only available mainloop. As of version 0.41.0 we split out the glib dependency to allow for other mainloops - to be implemented. Notice also the python binding version does not match up with the D-Bus version. Once we reach 1.0 - this should change with Python changes simply tracking the D-Bus changes. - While the glib mainloop is the only mainloop currently implemented, integrating other mainloops should - be very easy to do. There are plans for creating a a generic mainloop to be the default for non gui programs. - - - - Listening for Signals - - Signals are emitted by objects on the bus to notify listening programs that an event has occurred. There are a couple of ways - to register a signal handler on the bus. One way is to attach to an already created proxy using the connect_to_signal method - which takes a signal name and handler as arguments. Let us look at an example of connecting to the HAL service to receive - signals when devices are added and removed and when devices register a capability. This example assumes you have HAL already running. - -import gobject -import dbus -if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - -def device_added_callback(udi): - print 'Device with udi %s was added' % (udi) - -def device_removed_callback(udi): - print 'Device with udi %s was added' % (udi) - -def device_capability_callback(udi, capability): - print 'Device with udi %s added capability %s' % (udi, capability) - -bus = dbus.SystemBus() -hal_manager_obj = bus.get_object('org.freedesktop.Hal', - '/org/freedesktop/Hal/Manager') -hal_manager = dbus.Interface(hal_manager_obj, - 'org.freedesktop.Hal.Manager') - -hal_manager.connect_to_signal('DeviceAdded', device_added_callback) -hal_manager.connect_to_signal('DeviceRemoved', device_removed_callback) -hal_manager.connect_to_signal('NewCapability', device_capability_callback) - -mainloop = gobject.MainLoop() -mainloop.run() - - - - The drawback of using this method is that the service that you are connecting to has to be around when you register - your signal handler. While HAL is guaranteed to be around on systems that use it this is not always the case for every - service on the bus. Say our program started up before HAL, we could connect to the signal by adding a signal receiver - directly to the bus. - -bus.add_signal_receiver(device_added_callback, - 'DeviceAdded', - 'org.freedesktop.Hal.Manager', - 'org.freedesktop.Hal', - '/org/freedesktop/Hal/Manager') - -bus.add_signal_receiver(device_removed_callback, - 'DeviceRemoved', - 'org.freedesktop.Hal.Manager', - 'org.freedesktop.Hal', - '/org/freedesktop/Hal/Manager') - -bus.add_signal_receiver(device_capability_callback, - 'DeviceAdded', - 'org.freedesktop.Hal.Manager', - 'org.freedesktop.Hal', - '/org/freedesktop/Hal/Manager') - - - - All this can be done without creating the proxy object if one wanted to but in most cases you would want to have - a reference to the object so once a signal was received operations could be executed on the object. - - - Signal matching on arguments - - Starting with D-Bus 0.36 and the (0, 43, 0) version of the python - bindings you can now add a match on arguments being sent in a signal. - This is useful for instance for only getting NameOwnerChanged - signals for your service. Lets say we create a name on the bus called - 'org.foo.MyName' we could also add a match to just get - NameOwnerChanges for that name as such: - -bus.add_signal_receiver(myname_changed, - 'NameOwnerChanged', - 'org.freedesktop.DBus', - 'org.freedesktop.DBus', - '/org/freedesktop/DBus', - arg0='org.foo.MyName') - - - It is as simple as that. To match the second arg you would use arg1=, - the third arg2=, etc. - - - - Cost of Creating a Proxy Object - - Note that creating proxy objects can have an associated processing cost. When introspection is implemented - a proxy may wait for introspection data before processing any requests. It is generally good practice to - create proxies once and reuse the proxy when calling into the object. Constantly creating the same proxy - over and over again can become a bottleneck for your program. - - - - TODO: example of getting information about devices from HAL - - - - - - Python API: Implementing Objects - - Implementing object on the bus is just as easy as invoking methods or listening for signals on the bus. - - - Version Alert - - The Python D-Bus bindings require version 2.4 or greater of Python when creating D-Bus objects. - - - - - Inheriting From dbus.service.Object - - In order to export a Python object over the bus one must first get a bus name and then create - a Python object that inherits from dbus.service.Object. The following is the start of an example - HelloWorld object that we want to export over the session bus. - -import gobject -import dbus -import dbus.service -if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - -class HelloWorldObject(dbus.service.Object): - def __init__(self, bus_name, object_path='/org/freedesktop/HelloWorldObject'): - dbus.service.Object.__init__(self, bus_name, object_path) - -session_bus = dbus.SessionBus() -bus_name = dbus.service.BusName('org.freedesktop.HelloWorld', bus=session_bus) -object = HelloWorldObject(bus_name) - -mainloop = gobject.MainLoop() -mainloop.run() - - - - Here we got the session bus, then created a BusName object which requests a name on the bus. - We pass that bus name to the HelloWorldObject object which inherits from dbus.service.Object. - We now have an object on the bus but it is pretty useless. - - - - Exporting Methods Over The Bus - - Let's make this object do something and export a method over the bus. - -import gobject -import dbus -import dbus.service -if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - -class HelloWorldObject(dbus.service.Object): - def __init__(self, bus_name, object_path='/org/freedesktop/HelloWorldObject'): - dbus.service.Object.__init__(self, bus_name, object_path) - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def hello(self): - return 'Hello from the HelloWorldObject' - -session_bus = dbus.SessionBus() -bus_name = dbus.service.BusName('org.freedesktop.HelloWorld', bus=session_bus) -object = HelloWorldObject(bus_name) - -mainloop = gobject.MainLoop() -mainloop.run() - - - - Python Decorators - - Notice the @ symbol on the line before the hello method. This is a new directive introduced in - Python 2.4. It is called a decorator and it "decorates" methods. All you have to know is that - it provides metadata that can then be used to alter the behavior of the method being decorated. - In this case we are telling the bindings that the hello method should be exported as a D-Bus method - over the bus. - - - - As you can see we exported the hello method as part of the org.freedesktop.HelloWorldIFace interface. - It takes no arguments and returns a string to the calling program. Let's create a proxy and invoke this - method. - -import dbus - -bus = dbus.SessionBus() -proxy_obj = bus.bus.get_object('org.freedesktop.HelloWorld', '/org/freedesktop/HelloWorldObject') -iface = dbus.Interface(proxy_obj, 'org.freedesktop.HelloWorldIFace') - -print iface.hello() - - - - When invoking methods exported over the bus the bindings automatically know how many parameters - the method exports. You can even make a method that exports an arbitrary number of parameters. - Also, whatever you return will automatically be transfered as a reply over the bus. Some examples. - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def one_arg(self, first_arg): - return 'I got arg %s' % first_arg - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def two_args(self, first_arg, second_arg): - return ('I got 2 args', first_arg, second_arg) - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def return_list(self): - return [1, 2, 3, 4, 5, 6] - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def return_dict(self): - return {one: '1ne', two: '2wo', three: '3ree'} - - - - - Emitting Signals - - Setting up signals to emit is just as easy as exporting methods. It uses the same syntax as methods. - -import gobject -import dbus -import dbus.service -if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - -class HelloWorldObject(dbus.service.Object): - def __init__(self, bus_name, object_path='/org/freedesktop/HelloWorldObject'): - dbus.service.Object.__init__(self, bus_name, object_path) - - @dbus.service.method('org.freedesktop.HelloWorldIFace') - def hello(self): - return 'Hello from the HelloWorldObject' - - @dbus.service.signal('org.freedesktop.HelloWorldIFace') - def hello_signal(self, message): - pass - -session_bus = dbus.SessionBus() -bus_name = dbus.service.BusName('org.freedesktop.HelloWorld', bus=session_bus) -object = HelloWorldObject(bus_name) - -object.hello_signal('I sent a hello signal') - -mainloop = gobject.MainLoop() -mainloop.run() - - - - Adding a @dbus.service.signal decorator to a method turns it into a signal emitter. You can put code - in this method to do things like keep track of how many times you call the emitter or to print out debug - messages but for the most part a pass noop will do. Whenever you call the emitter a signal will be emitted - with the parameters you passed in as arguments. In the above example we send the message 'I sent a hello signal' - with the signal. - - - - Inheriting from HelloWorldObject - - One of the cool things you can do in Python is inherit from another D-Bus object. We use this trick in - the bindings to provide a default implementation for the org.freedesktop.DBus.Introspectable interface. - Let's inherit from the HelloWorldObject example above and overide the hello method to say goodbye. - -class HelloWorldGoodbyeObject(HelloWorldObject): - def __init__(self, bus_name, object_path='/org/freedesktop/HelloWorldGoodbyeObject'): - HelloWorldObject.__init__(self, bus_name, object_path) - - @dbus.service.method('org.freedesktop.HelloWorldGoodbyeIFace') - def hello(self): - return 'Goodbye' - -goodbye_object = HelloWorldGoodbyeObject(bus_name) - - - - Let's now call both methods with a little help from interfaces. - -import dbus - -bus = dbus.SessionBus() -proxy_obj = bus.bus.get_object('org.freedesktop.HelloWorld', '/org/freedesktop/HelloWorldGoodbyeObject') - -print proxy_obj.hello(dbus_interface='org.freedesktop.HelloWorldIFace') -print proxy_obj.hello(dbus_interface='org.freedesktop.HelloWorldGoodbyeIFace') - - - - This should print out 'Hello from the HelloWorldObject' followed by a 'Goodbye'. - - - - Conclusion - - As you can see, using D-Bus from Python is an extremely easy proposition. Hopefully - the tutorial has been helpful in getting you started. If you need anymore help please - feel free to post on the mailing list. - The Python bindings are still in a state of flux and there may be API changes in the future. - This tutorial will be updated if such changes occur. - - -- cgit