P. 1
DBus y La Serpiente - LInux Magazine

DBus y La Serpiente - LInux Magazine

|Views: 237|Likes:
Published by alexanderae
Artìculo de Linux Magazine sobre DBus y Python.
Artìculo de Linux Magazine sobre DBus y Python.

More info:

Published by: alexanderae on Mar 01, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

11/08/2015

pdf

text

original

DESARROLLO • Python

DBus abre un nuevo mundo de posibilidades en el escritorio

DBUS Y LA SERPIENTE
Después de años de enfrentamientos teóricos, técnicos y «religiosos» los dos grandes escritorios se han puesto de acuerdo en algo. POR JOSÉ MARÍA RUIZ

C

uando los creadores de KDE y Gnome comenzaron a diseñarlos vieron la necesidad de crear un sistema de objetos. Cada ventana, cada botón y cada etiqueta están conectados y se mandan mensajes entre ellos. KDE decidió programar sus sistemas en C++. Gnome decidió usar C.

Gnome, KDE,…
Desde un primer momento aparecieron problemas. La papelera de reciclaje de Gnome no era la misma que la de KDE, los escritorios no se guardaban en el mismo directorio. Lo peor de todo era que las aplicaciones de ambos escritorios no podían comunicarse entre ellas. KDE usaba para comunicar a sus aplicaciones el protocolo DCOP de creación propia (ver Recurso [1]), un protocolo diseñado exclusivamente para él. Es pequeño, ligero y simple. Gnome empleaba Corba (ver Recurso [2]), un protocolo «diseñado por comité», tan ambicioso que jamás se ha llegado a usar completamente. Imagina que Corba es algo equivalente al proyecto de llevar al hombre a la Luna, pero en software. KDE se reía del sistema de Gnome diciendo que era demasiado grande y demasiado lento. La gente de Gnome se reía del sistema de KDE diciendo que era demasiado pequeño y no era compatible con Corba, que es un estándar mundial. Esta situación se ha prolongado durante los años. Y como hizo Alejandro Magno con el nudo gordiano, la solución ha consistido en eliminar el problema en lugar de solucionarlo. Una entidad independiente, FreeDesktop (ver Recurso [3]), comenzó el diseño de DBus de forma autónoma (ver Recurso [4]). DBus permitiría a los programas intercambiar

información. Tiene lo mejor de Corba y DCOP, pero no pretende ser la solución final al problema del intercambio de información entre procesos. Su objetivo es claro: comunicar a las aplicaciones dentro de una misma sesión de escritorio. Al no pertenecer a ninguno de los dos escritorios, ha sido adoptado junto a un paquete de otras compatibilidades, como por ejemplo, a la dichosa papelera de reciclaje común.

Conceptos Básicos
La arquitectura de DBus se compone de 3 partes: • La librería libdbus • Un daemon que sirve como repetidor de los mensajes • Un conjunto de envolturas sobre la librería En nuestro caso, usaremos una envoltura realizada en Python. Ésta se encarga de mantener todos los detalles ocultos. De esta forma podremos trabajar de manera más sencilla. La relación entre estos tres componentes se muestra en la Figura [1]. libdbus crea conexiones o canales que conectan dos aplicaciones. Lo que hacemos es usar esa única conexión para engancharnos al daemon de DBus, que se comporta como un repetidor. De esta forma todas las aplicaciones que se conecten al daemon podrán contactar entre sí. La información se transmite en forma de mensajes. Los hay de dos tipos: • Métodos • Señales Los métodos sirven para decirle a un objeto que realice una operación. Pueden requerir parámetros o no. Mientras, las señales sirven para notificar un suceso de interés general. DBus es independiente del lenguaje que usemos para acceder a él. El acceso puede hacerse desde Python o C#, dos de sus posibles lenguajes. ¿A qué nos referimos con objeto?, ¿a uno de Python o a uno de C#? DBus soluciona este problema haciendo que los objetos sean unas entidades no asociadas a ningún lenguaje. Un objeto en DBus es una ruta. En DBus los objetos son direccionados a través de una ruta que equivale a su nombre. Un programa publicará «objetos-rutas» a las que podremos

El protocolo DBus
DBus es un protocolo de comunicación entre procesos que emplea mensajes. Ha sido diseñado para que sea ligero y fácilmente empotrable en cualquier programa. Pero ¿para qué queremos un sistema como DBus? Las aplicaciones de nuestro escritorio hacen uso de servicios. Podemos pensar en todo aquello que es común encontrar en las aplicaciones: un sistema de avisos, el portapapeles o la impresión. La función del escritorio es proveer estos servicios de forma que la creación de aplicaciones sea mucho más sencilla para los desarrolladores. Gnome y KDE ofrecen estos servicios, pero de forma incompatible. Digamos que tienes Gnome arrancado y que ejecutas un programa para KDE. El programa de KDE espera una serie de servicios ofrecidos a través de DCOP, pero GNOME los ofrece a través de Corba. Lo mismo ocurriría en la situación inversa. Tanto Gnome como KDE están adoptando DBus como su sistema de comunicaciones. La interacción entre las aplicaciones de los dos grandes escritorios de Linux será una realidad en un futuro próximo.

44

Número 23

WWW.LINUX- MAGAZINE.ES

Python • DESARROLLO

acceder. Las rutas tienen un formato que nos Por ejemplo, /org/freedesktop/DBus es un ruta debe resultar familiar: y org.freedesktop.DBus es un interfaz. Imaginemos que queremos que todos nues/a/b/c.../n tros programas se puedan apagar remotamente. Podríamos crear un interfaz que sólo Son rutas como las que se emplean en el sis- ofreciese el método apagar. Podríamos llamar tema de ficheros de Linux. a este interfaz, es.linux.magazine.apagar. Los Cada aplicación debe tener una ruta única. programas podrían implementar dentro de Es común que se emplee la dirección URL de sus objetos DBus este interfaz y de esa manera la página web del proyecto que mantiene y nuestro programa podría apagar todos los prodesarrolla esa aplicación, así no es complicado gramas del sistema, dando igual en qué lenencontrar rutas como: guaje estuviesen programados. • /net/sf/gaim Cualquiera que haya estado un poco al • /org/freedesktop/DBus tanto de las noticias sobre los últimos sistemas A cada objeto le corresponden unos métodos. operativos de Apple o Microsoft habrá notado Es igual que en Python: un objeto guarda una la importancia de la búsqueda en el escritorio. cantidad de información y unos métodos. El software libre no se ha quedado de braEstos métodos cambiarán el estado del objeto zos cruzados y también ha desarrollado teco nos permitirán recabar información sobre él. nología de este tipo. De hecho, DBus es la pieSi queremos ver métodos y señales en dra angular para ello. Todos los programas funcionamiento sólo tenemos que ejecu- pueden implementar un interfaz a través del tar el comando dbus-monitor en una cual se pueda realizar una búsqueda usando consola de texto y ver cómo se suceden una cadena de texto. las acciones (Figura [2]). Con sólo invocar al método busca con una cadena, cada programa te ofrecería los resultaLos interfaces dos encontrados de una forma establecida. Si cada objeto publicase los métodos que Así es posible crear un programa que buscara quisiera sería muy complicado hacer algo en todos ellos. Ese programa tiene nombre, se genérico con DBus. ¿Cómo sabría nuestra llama beagle (ver Recurso [5]). aplicación genérica el modo de contactar con los programas en ejecución? Este Interacción con DBus mismo problema surge también en el Un programa que quiera trabajar con DBus diseño orientado a objetos, y ambos se han debe seguir unos pasos. El primero consiste solucionado de la misma manera. en conseguir un bus, un canal, para comuLos interfaces son conjuntos de métodos nicarse con el daemon de DBus. ¡Si el daecon nombres predefinidos y acciones acor- mon no está ejecutándose esto será imposidadas que son conceptualmente cercanos. ble! Por tanto hay que asegurarse de que Un interfaz puede contener todo lo necesa- está funcionando. También debemos tener rio para reproducir música o buscar texto. instalada la envoltura para Python de Los interfaces pueden tener la misma DBus, por lo que deberemos comprobarlo ruta que los objetos. Para poder diferenciar- con nuestro sistema de paquetes. los se llegó a un acuerdo: Una vez que tengamos nuestro bus hemos • Los objetos tienen rutas con / como sepa- de conectar con el objeto. Para ellos debemos rador usar su ruta. Pero este objeto no existe real• Los interfaces tienen rutas con . como sepa- mente en Python: es un objeto DBus. ¿Cómo rador podemos trabajar con él desde Python? La

Figura 1: Esquema de la arquitectura DBus.

solución a este problema consiste en emplear un objeto proxy. Un proxy es un intermediario. Su misión consiste en hacer de traductor o embajador del objeto DBus dentro de nuestra sesión de Python. Este proxy es en realidad una clase Python que enmascara los detalles de la interacción con DBus. El proxy se comporta como si fuese el objeto remoto, pero con sintaxis de Python. De esta forma su uso será natural. Comencemos con un ejemplo simple. Vamos a conectar con uno de los objetos del propio DBus y conseguir un listado de los objetos ofertados. No olvidemos importar la librería dbus.
>>> import dbus >>> >>> bus = dbus.SessionBus() >>> proxy_obj = bus.get_objectU (‘org.freedesktop.DBus’,’U /org/freedesktop/DBus’) >>> dbus_iface = dbus.InterfaceU (proxy_obj,U ‘org.freedesktop.DBus’) >>> print dbus_iface.ListNames() [u’org.freedesktop.DBus’,U u’:1.4’, u’net.sf.gaim.U GaimService’,u’:1.2’] >>>

Figura 2: dbus-monitor mostrando mensajes en DBus.

El ejemplo sigue los pasos que hemos estado describiendo. Primero conseguimos un bus, que es un canal de comunicación. Con el bus en nuestras manos podemos invocar a un objeto mediante el método get_object(). Este método requiere dos parámetros, el primero es un interfaz y el segundo un objeto DBus. Como puede apreciarse, la ruta se corresponde con la dirección que aparece en el Recurso [3], pero invertida. Freedesktop sabe que sólo

WWW.LINUX- MAGAZINE.ES

Número 23

45

DESARROLLO • Python

ellos poseen esa URL, y que por tanto es única. La envoltura alrededor de DBus se encarga de realizar las conversiones pertinentes. Python puede usar sus propios tipos de datos, en este caso una lista, para representar los que recibe por parte de DBus.

Introspección
Para poder usar cualquier objeto debemos comenzar por conseguir su información. DBus obliga a todos los objetos a implementar el interfaz org.freedesktop.DBus.Introspectable que nos permite preguntarle al objeto por los métodos e interfaces que implementa. Vamos a ver cuáles implementa el programa de reproducción de audio Rhythmbox (Figura [3]):
>>> bus = dbus.SessionBus() >>> r = bus.get_object(U ‘org.gnome.Rhythmbox’,U ‘/org/gnome/Rhythmbox’) >>> proxy = dbus.Interface(r,U ‘org.freedesktop.DBus.U Introspectable’) >>> proxy.Introspect() u’<!DOCTYPE node PUBLICU “-//freedesktop//DTD D-BUSU Object Introspection 1.0//EN”\n”http://U www.freedesktop.org/standards/U dbus/1.0/introspect.dtd”>\nU <node>\n <node name=”Player”/>\n <nodeU name=”PlaylistManager”/>\nU <node name=”Shell”/>\n</node>\n’ >>>

Figura 3: Rhythmbox.

07 1.0//EN”\n”http:// www.freedesktop.org/standards/ dbus/1.0/introspect.dtd”>\n <node>\n 08 <interface name= “org.freedesktop.DBus. Introspectable”>\n <method 09 name=”Introspect”>\n <arg name= “data” direction=”out”type=”s”/>\n 10 ....

u’org.freedesktop.DBus. Properties’} 14 Comienzo de method {u’name’: u’Get’} 15 Comienzo de arg {u’direction’: u’in’, u’type’: u’s’, u’name’: u’interface’} 16 Comienzo de arg {u’direction’: u’in’, u’type’: u’s’, u’name’: u’propname’} 17 ...

En esta ocasión la cantidad de información ha sido enorme. No se puede comprender tal cual, así que es preciso procesarla de alguna manera. Necesitamos navegar datos XML. Python cuenta con librería de procesado de XML.
01 >>> import xml.parsers.expat 02 >>> def comienzo(nombre, attrs): 03 ... print “Comienzo de “, nombre,attrs 04 ... 05 >>> p = xml.parsers.expat. ParserCreate() 06 >>> 07 >>> p.StartElementHandler = comienzo 08 >>> p.Parse( proxy.Introspect()) 09 Comienzo de node {} 10 Comienzo de interface {u’name’: u’org.freedesktop.DBus. Introspectable’} 11 Comienzo de method {u’name’: u’Introspect’} 12 Comienzo de arg {u’direction’: u’out’, u’type’: u’s’, u’name’: u’data’} 13 Comienzo de interface {u’name’:

El formato de XML que se emplea contiene interfaces, y dentro de ellos hay métodos. De esta forma podemos ir recopilando la información que necesitamos para interactuar con los objetos. Desgraciadamente, y a día de escritura de este artículo, aún no existen navegadores gráficos de DBus establecidos. Sólo los desarrolladores de QT poseen uno: dbus-viewer.

¿Qué estoy escuchando?
Vamos a emplear DBus en un script original de Melissa Saffron. Nos dirá qué está sonando en ese momento en el programa Rhythmbox. Con otros programas que soporten DBus el procedimiento será muy parecido. El programa aparece en el Listado [1]. Lo primero que hacemos, con la inestimable ayuda de la librería OS, es comprobar que el programa Rhythmbox se está ejecutando. El script lo hace de forma… grosera pero efectiva :) Ejecuta el comando ps que devuelve el listado de programas en ejecución y lo filtra con grep para poder localizar a nuestro programa. En caso de éxito la salida será 256. De acuerdo, Rhythmbox se está ejecutando. Ahora debemos contactar con él. Primero creamos el bus, y conectamos con el interfaz org.gnome.Rhythmbox accediendo al objeto /org/gnome/Rhythmbox/Player. Generamos

¿Pero qué nos ha devuelto? Pues un texto XML que describe el contenido de ese Objeto. Resulta que Rhythmbox se compone de: • Player • PlaylistManager • Shell Ahora podemos proceder a preguntar por cada uno de ellos:
01 >>> player = bus.get_object (‘org.gnome.Rhythmbox’,’/org/ gnome/Rhythmbox/Player’) 02 >>> proxy = dbus.Interface (player,’org.freedesktop.DBus. Introspectable’) 03 >>> proxy.Introspect() 04 >>> proxy.Introspect() 05 u’<!DOCTYPE node PUBLIC “-// freedesktop//DTD D-BUS Object 06 Introspection

46

Número 23

WWW.LINUX- MAGAZINE.ES

Python • DESARROLLO

01 #!/usr/bin/env python 02 import dbus 03 import os 04 if os.system(“ps -A|grep rhythmbox >/dev/null”) != 256: 05 06 bus = dbus.SessionBus() rb = bus.get_object(‘org.gnome.Rhyt hmbox’, ‘/org/gnome/Rhythmbox/Player’) 07 08 09 10 player = dbus.Interface(rb, ‘org.gnome.Rhythmbox.Player’) playing = player.getPlaying() if playing == True: rbshell = bus.get_object(‘org.gnome.Rhyt hmbox’, ‘/org/gnome/Rhythmbox/Shell’) 11 shell = dbus.Interface(rbshell, ‘org.gnome.Rhythmbox.Shell’) 12 data = shell.getSongProperties(player .getPlayingUri()) 13 14 titulo = data[‘title’]

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

artista = data[‘artist’] album = data[‘album’] duracion = data[‘duration’] minutos = 0 while duracion >= 60: minutos += 1 duracion -= 60 duracion_show = ‘%d:’ % minutos if duracion < 10: duracion_show += ‘0’ + str(duracion) else: duracion_show += str(duracion) pasado = player.getElapsed() minutos = 0 while pasado >= 60: minutos += 1 pasado -= 60 pasado_show = ‘%d:’ % minutos if pasado < 10:

37 38 39 40 41 42

pasado_show += ‘0’ + str(pasado) else: pasado_show += str(pasado)

output = ‘Sonando \”’ + titulo + ‘\” de ‘ + artista + ‘ (‘ + pasado_show + ‘ de ‘ + duracion_show + ‘)’

43 44 2: 45 46 47 48 49 else: output = “\tRhythmbox no est tocando ninguna canción” 50 else: 51 output = “\tRhythmbox no se está ejecutando” 52 print output.encode(“UTF-8”) veces_sonado = str(data[‘play-count’] + 1) output += ‘\n\tSe ha tocado ‘ + veces_sonado + ‘ veces.’ if data[‘play-count’] + 1 >=

un proxy a partir del interfaz del mismo nombre y ya podemos emplear el objeto DBus que tenemos guardado en la variable player. Necesitamos la canción que está sonando. Para ello invocamos el método getPlaying() que nos devuelve un valor booleano, True si canción que está sonando y False en caso contrario. Si está sonando una canción volvemos a realizar otra vez el proceso. ¿Por qué? Rhythmbox usa 3 objetos distintos, como vimos más arriba, el que almacena los datos de la canción que está sonando es Shell. Creamos el objeto a partir de /org/gnome/Rhythmbox/Shell, y un proxy para él. Este objeto posee el método getSongProperties() que necesita una dirección especial, que podemos conseguir de:
>>> player.getPlayingUri() u’file:///mnt/datos/ant/musica/U (FatboySlim)_right_here_rightU _now.OGG’

lista con toda la información de la canción que estamos escuchando. Procedemos entonces a extraer la información de la lista de datos. Cogemos el título, el artista, el álbum y la duración; procesamos la duración de la canción para convertirla a minutos y segundos con unas cuantas operaciones. El método player.getElapsed() nos dice el número de segundos transcurridos desde que comenzó la canción, debemos convertirlo a minutos y segundos también para que podamos imprimirlo correctamente. Ahora podemos usar este script cuando queramos y el resultado será algo así como:
Sonando “Right Here, RightU Now” de Fatboy Slim (0:26 deU 6:28) Se ha tocado 3 veces.

competirán en ofrecer el mejor servicio a cualquier aplicación en lugar de dividir el mundo en dos universos completamente separados. Ni Firefox ni OpenOffice están aún integrados con DBus, ya que ambos poseen sus propios sistemas de componentes, XPCOM y UNO respectivamente, que compiten con DBus . En cambio Gaim, Xchat, Rhythmbox, Liferea, así como gran parte de Gnome, lo usan ya de forma habitual. DBus es aún muy joven, pero está ganando fuerzas entre las pequeñas aplicaciones de escritorio. Esperemos que DBus se vaya integrando poco a poco y que cada vez más I aplicaciones lo usen.

RECURSOS
[1] http://developer.kde.org/ documentation/other/dcop.html [2] http://www.corba.org [3] http://www.freedesktop.org [4] http://dbus.freedesktop.org [5] http://beagle-project.org/Main_Page [6] http://developer.gnome.org/doc/API/ glib/

Conclusión
DBus es engorroso, aún falta tiempo para que tanto KDE como Gnome pasen a emplear DBus como único mecanismo de comunicación entre procesos. Pero el futuro es brillante. Dentro de unos años Gnome y KDE

player.getPlayingUri() devuelve una ruta URI. Con ambos métodos podemos obtener una

WWW.LINUX- MAGAZINE.ES

Número 23

47

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->