dbus y la serpiente - linux magazine

4
DBus abre un nuevo mundo de posibilidades en el escritorio DBUS Y LA SERPIENTE información. Tiene lo mejor de Corba y DCOP, pero no pretende ser la solución final al problema del intercambio de infor- mació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. 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ácil- mente empotrable en cualquier pro- grama. Pero ¿para qué queremos un sis- tema 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 porta- papeles o la impresión. La función del escri- torio 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 tra- vés de DCOP, pero GNOME los ofrece a tra- vés de Corba. Lo mismo ocurriría en la situa- ció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. Conceptos Básicos La arquitectura de DBus se compone de 3 par- tes: • 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 rea- lizada 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 enganchar- nos 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 posi- bles 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 C uando los creadores de KDE y Gnome comenzaron a diseñarlos vieron la necesidad de crear un sistema de obje- tos. 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 pro- blemas. 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 aplica- ciones el protocolo DCOP de creación pro- pia (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 DESARROLLO • Python 44 Número 23 WWW.LINUX - MAGAZINE.ES 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

Upload: alexanderae

Post on 30-Jun-2015

238 views

Category:

Documents


3 download

DESCRIPTION

Artìculo de Linux Magazine sobre DBus y Python.

TRANSCRIPT

Page 1: DBus y La Serpiente - LInux Magazine

DBus abre un nuevo mundo de posibilidades en el escritorio

DBUS Y LASERPIENTE

información. Tiene lo mejor de Corba yDCOP, pero no pretende ser la soluciónfinal al problema del intercambio de infor-mación entre procesos. Su objetivo es claro:comunicar a las aplicaciones dentro de unamisma sesión de escritorio.

Al no pertenecer a ninguno de los dosescritorios, ha sido adoptado junto a unpaquete de otras compatibilidades, comopor ejemplo, a la dichosa papelera dereciclaje común.

El protocolo DBusDBus es un protocolo de comunicaciónentre procesos que emplea mensajes. Hasido diseñado para que sea ligero y fácil-mente empotrable en cualquier pro-grama. Pero ¿para qué queremos un sis-tema como DBus?

Las aplicaciones de nuestro escritoriohacen uso de servicios. Podemos pensar entodo aquello que es común encontrar en lasaplicaciones: un sistema de avisos, el porta-papeles o la impresión. La función del escri-torio es proveer estos servicios de forma quela creación de aplicaciones sea mucho mássencilla para los desarrolladores.

Gnome y KDE ofrecen estos servicios,pero de forma incompatible. Digamos quetienes Gnome arrancado y que ejecutas unprograma para KDE. El programa de KDEespera una serie de servicios ofrecidos a tra-vés de DCOP, pero GNOME los ofrece a tra-vés de Corba. Lo mismo ocurriría en la situa-ción inversa.

Tanto Gnome como KDE están adoptandoDBus como su sistema de comunicaciones.La interacción entre las aplicaciones de losdos grandes escritorios de Linux será unarealidad en un futuro próximo.

Conceptos BásicosLa arquitectura de DBus se compone de 3 par-tes:• La librería libdbus• Un daemon que sirve como repetidor de los

mensajes• Un conjunto de envolturas sobre la libreríaEn nuestro caso, usaremos una envoltura rea-lizada en Python. Ésta se encarga de mantenertodos los detalles ocultos. De esta formapodremos trabajar de manera más sencilla. Larelación entre estos tres componentes semuestra en la Figura [1].

libdbus crea conexiones o canales queconectan dos aplicaciones. Lo que hacemoses usar esa única conexión para enganchar-nos al daemon de DBus, que se comportacomo un repetidor. De esta forma todas lasaplicaciones que se conecten al daemonpodrán contactar entre sí.

La información se transmite en forma demensajes. Los hay de dos tipos:• Métodos• SeñalesLos métodos sirven para decirle a un objetoque realice una operación. Pueden requerirparámetros o no. Mientras, las señales sirvenpara notificar un suceso de interés general.

DBus es independiente del lenguaje queusemos para acceder a él. El acceso puedehacerse desde Python o C#, dos de sus posi-bles lenguajes. ¿A qué nos referimos conobjeto?, ¿a uno de Python o a uno de C#?DBus soluciona este problema haciendo quelos objetos sean unas entidades no asociadasa ningún lenguaje.

Un objeto en DBus es una ruta. En DBus losobjetos son direccionados a través de una rutaque equivale a su nombre. Un programapublicará «objetos-rutas» a las que podremos

Cuando los creadores de KDE y Gnomecomenzaron a diseñarlos vieron lanecesidad de crear un sistema de obje-

tos. Cada ventana, cada botón y cada etiquetaestán conectados y se mandan mensajes entreellos. KDE decidió programar sus sistemas enC++. Gnome decidió usar C.

Gnome, KDE,…Desde un primer momento aparecieron pro-blemas. La papelera de reciclaje de Gnome noera la misma que la de KDE, los escritorios nose guardaban en el mismo directorio. Lo peorde todo era que las aplicaciones de ambosescritorios no podían comunicarse entre ellas.

KDE usaba para comunicar a sus aplica-ciones el protocolo DCOP de creación pro-pia (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é», tanambicioso que jamás se ha llegado a usarcompletamente. Imagina que Corba es algoequivalente al proyecto de llevar al hombre ala Luna, pero en software.

KDE se reía del sistema de Gnomediciendo que era demasiado grande ydemasiado lento. La gente de Gnome sereía del sistema de KDE diciendo que erademasiado pequeño y no era compatiblecon Corba, que es un estándar mundial.

Esta situación se ha prolongado durante losaños. Y como hizo Alejandro Magno con elnudo gordiano, la solución ha consistido eneliminar el problema en lugar de solucionarlo.

Una entidad independiente, FreeDesktop(ver Recurso [3]), comenzó el diseño de DBusde forma autónoma (ver Recurso [4]). DBuspermitiría a los programas intercambiar

DESARROLLO • Python

44 Número 23 W W W . L I N U X - M A G A Z I N E . E S

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

044-047_PythonLM23 8/12/06 10:09 pm Página 44

Page 2: DBus y La Serpiente - LInux Magazine

acceder. Las rutas tienen un formato que nosdebe resultar familiar:

/a/b/c.../n

Son rutas como las que se emplean en el sis-tema de ficheros de Linux.

Cada aplicación debe tener una ruta única.Es común que se emplee la dirección URL dela página web del proyecto que mantiene ydesarrolla esa aplicación, así no es complicadoencontrar rutas como:• /net/sf/gaim• /org/freedesktop/DBusA cada objeto le corresponden unos métodos.Es igual que en Python: un objeto guarda unacantidad de información y unos métodos.Estos métodos cambiarán el estado del objetoo nos permitirán recabar información sobre él.

Si queremos ver métodos y señales enfuncionamiento sólo tenemos que ejecu-tar el comando dbus-monitor en unaconsola de texto y ver cómo se sucedenlas acciones (Figura [2]).

Los interfacesSi cada objeto publicase los métodos quequisiera sería muy complicado hacer algogenérico con DBus. ¿Cómo sabría nuestraaplicación genérica el modo de contactarcon los programas en ejecución? Estemismo problema surge también en eldiseño orientado a objetos, y ambos se hansolucionado de la misma manera.

Los interfaces son conjuntos de métodoscon nombres predefinidos y acciones acor-dadas que son conceptualmente cercanos.Un interfaz puede contener todo lo necesa-rio para reproducir música o buscar texto.

Los interfaces pueden tener la mismaruta que los objetos. Para poder diferenciar-los se llegó a un acuerdo:• Los objetos tienen rutas con / como sepa-

rador• Los interfaces tienen rutas con . como sepa-

rador

Por ejemplo, /org/freedesktop/DBus es un rutay org.freedesktop.DBus es un interfaz.

Imaginemos que queremos que todos nues-tros programas se puedan apagar remota-mente. Podríamos crear un interfaz que sóloofreciese el método apagar. Podríamos llamara este interfaz, es.linux.magazine.apagar. Losprogramas podrían implementar dentro desus objetos DBus este interfaz y de esa maneranuestro programa podría apagar todos los pro-gramas del sistema, dando igual en qué len-guaje estuviesen programados.

Cualquiera que haya estado un poco altanto de las noticias sobre los últimos sistemasoperativos de Apple o Microsoft habrá notadola importancia de la búsqueda en el escritorio.

El software libre no se ha quedado de bra-zos cruzados y también ha desarrollado tec-nología de este tipo. De hecho, DBus es la pie-dra angular para ello. Todos los programaspueden implementar un interfaz a través delcual se pueda realizar una búsqueda usandouna cadena de texto.

Con sólo invocar al método busca con unacadena, cada programa te ofrecería los resulta-dos encontrados de una forma establecida.Así es posible crear un programa que buscaraen todos ellos. Ese programa tiene nombre, sellama beagle (ver Recurso [5]).

Interacción con DBusUn programa que quiera trabajar con DBusdebe seguir unos pasos. El primero consisteen conseguir un bus, un canal, para comu-nicarse con el daemon de DBus. ¡Si el dae-mon no está ejecutándose esto será imposi-ble! Por tanto hay que asegurarse de queestá funcionando. También debemos tenerinstalada la envoltura para Python deDBus, por lo que deberemos comprobarlocon nuestro sistema de paquetes.

Una vez que tengamos nuestro bus hemosde conectar con el objeto. Para ellos debemosusar su ruta. Pero este objeto no existe real-mente en Python: es un objeto DBus. ¿Cómopodemos trabajar con él desde Python? La

solución a este problema consiste en emplearun objeto proxy.

Un proxy es un intermediario. Su misiónconsiste en hacer de traductor o embajadordel objeto DBus dentro de nuestra sesión dePython. Este proxy es en realidad una clasePython que enmascara los detalles de la inter-acción con DBus. El proxy se comporta comosi fuese el objeto remoto, pero con sintaxis dePython. De esta forma su uso será natural.

Comencemos con un ejemplo simple.Vamos a conectar con uno de los objetosdel propio DBus y conseguir un listado delos objetos ofertados. No olvidemos impor-tar 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’,Uu’:1.4’, u’net.sf.gaim.UGaimService’,u’:1.2’]>>>

El ejemplo sigue los pasos que hemosestado describiendo. Primero consegui-mos un bus, que es un canal de comunica-ción. Con el bus en nuestras manos pode-mos invocar a un objeto mediante elmétodo get_object(). Este método requieredos parámetros, el primero es un interfazy el segundo un objeto DBus. Como puedeapreciarse, la ruta se corresponde con ladirección que aparece en el Recurso [3],pero invertida. Freedesktop sabe que sólo

Python • DESARROLLO

45Número 23W W W . L I N U X - M A G A Z I N E . E S

Figura 1: Esquema de la arquitectura DBus.

Figura 2: dbus-monitor mostrando mensajes en DBus.

044-047_PythonLM23 8/12/06 10:09 pm Página 45

Page 3: DBus y La Serpiente - LInux Magazine

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

En esta ocasión la cantidad de informa-ción ha sido enorme. No se puede com-prender tal cual, así que es preciso pro-cesarla de alguna manera. Necesitamosnavegar datos XML. Python cuenta conlibrería de procesado de XML.

01 >>> import xml.parsers.expat02 >>> def comienzo(nombre,attrs):03 ... print “Comienzo de “,nombre,attrs04 ...05 >>> p = xml.parsers.expat.ParserCreate()06 >>>07 >>> p.StartElementHandler =comienzo08 >>> 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’:

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 ...

El formato de XML que se emplea con-tiene interfaces, y dentro de ellos haymétodos. De esta forma podemos ir reco-pilando la información que necesitamospara interactuar con los objetos.

Desgraciadamente, y a día de escritura deeste artículo, aún no existen navegadores grá-ficos de DBus establecidos. Sólo los desarrolla-dores de QT poseen uno: dbus-viewer.

¿Qué estoy escuchando?Vamos a emplear DBus en un script originalde Melissa Saffron. Nos dirá qué está sonandoen ese momento en el programa Rhythmbox.Con otros programas que soporten DBus elprocedimiento será muy parecido. El pro-grama aparece en el Listado [1].

Lo primero que hacemos, con la inestima-ble ayuda de la librería OS, es comprobar queel programa Rhythmbox se está ejecutando. Elscript lo hace de forma… grosera pero efectiva:)

Ejecuta el comando ps que devuelve el lis-tado de programas en ejecución y lo filtra congrep 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 cre-amos el bus, y conectamos con el interfazorg.gnome.Rhythmbox accediendo al objeto/org/gnome/Rhythmbox/Player. Generamos

ellos poseen esa URL, y que por tanto esúnica.

La envoltura alrededor de DBus se encargade realizar las conversiones pertinentes.Python puede usar sus propios tipos de datos,en este caso una lista, para representar los querecibe por parte de DBus.

IntrospecciónPara poder usar cualquier objeto debemoscomenzar por conseguir su información.DBus obliga a todos los objetos a implementarel interfaz org.freedesktop.DBus.Introspectableque nos permite preguntarle al objeto por losmétodos e interfaces que implementa. Vamosa ver cuáles implementa el programa de repro-ducció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.UIntrospectable’)>>> proxy.Introspect()u’<!DOCTYPE node PUBLICU“-//freedesktop//DTD D-BUSUObjectIntrospection1.0//EN”\n”http://Uwww.freedesktop.org/standards/Udbus/1.0/introspect.dtd”>\nU<node>\n<node name=”Player”/>\n <nodeUname=”PlaylistManager”/>\nU<node name=”Shell”/>\n</node>\n’>>>

¿Pero qué nos ha devuelto? Pues un textoXML que describe el contenido de ese Objeto.Resulta que Rhythmbox se compone de:• Player• PlaylistManager• ShellAhora podemos proceder a preguntar porcada 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 Object06 Introspection

DESARROLLO • Python

46 Número 23 W W W . L I N U X - M A G A Z I N E . E S

Figura 3: Rhythmbox.

044-047_PythonLM23 8/12/06 10:09 pm Página 46

Page 4: DBus y La Serpiente - LInux Magazine

Python • DESARROLLO

47Número 23W W W . L I N U X - M A G A Z I N E . E S

lista con toda la información de la canción queestamos escuchando.

Procedemos entonces a extraer la infor-mación de la lista de datos. Cogemos eltítulo, el artista, el álbum y la duración;procesamos la duración de la canciónpara convertirla a minutos y segundoscon unas cuantas operaciones.

El método player.getElapsed() nos dice elnúmero de segundos transcurridos desde quecomenzó la canción, debemos convertirlo aminutos y segundos también para que poda-mos imprimirlo correctamente.

Ahora podemos usar este script cuandoqueramos y el resultado será algo así como:

Sonando “Right Here, RightUNow” de Fatboy Slim (0:26 deU6:28)Se ha tocado 3 veces.

ConclusiónDBus es engorroso, aún falta tiempopara que tanto KDE como Gnome pasena emplear DBus como único mecanismode comunicación entre procesos. Pero elfuturo es brillante.

Dentro de unos años Gnome y KDE

competirán en ofrecer el mejor servicio acualquier aplicación en lugar de dividirel mundo en dos universos completa-mente separados.

Ni Firefox ni OpenOffice están aúnintegrados con DBus, ya que ambosposeen sus propios sistemas de compo-nentes, XPCOM y UNO respectivamente,que compiten con DBus . En cambioGaim, Xchat, Rhythmbox, Liferea, asícomo gran parte de Gnome, lo usan yade forma habitual. DBus es aún muyjoven, pero está ganando fuerzas entrelas pequeñas aplicaciones de escritorio.

Esperemos que DBus se vaya inte-grando poco a poco y que cada vez másaplicaciones lo usen. �

un proxy a partir del interfaz del mismo nom-bre y ya podemos emplear el objeto DBus quetenemos guardado en la variable player.

Necesitamos la canción que estásonando. Para ello invocamos elmétodo getPlaying() que nos devuelveun valor booleano, True si canción queestá sonando y False en caso contrario.

Si está sonando una canción volve-mos a realizar otra vez el proceso. ¿Porqué? Rhythmbox usa 3 objetos distin-tos, como vimos más arriba, el quealmacena los datos de la canción queestá sonando es Shell.

Creamos el objeto a partir de/org/gnome/Rhythmbox/Shell, y unproxy para él. Este objeto posee elmétodo getSongProperties() que nece-sita una dirección especial, que pode-mos conseguir de:

>>> player.getPlayingUri()u’file:///mnt/datos/ant/musica/U(FatboySlim)_right_here_rightU_now.OGG’

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

01 #!/usr/bin/env python02 import dbus03 import os04 if os.system(“ps -A|grep

rhythmbox >/dev/null”) != 256:05 bus = dbus.SessionBus()06 rb =

bus.get_object(‘org.gnome.Rhythmbox’,‘/org/gnome/Rhythmbox/Player’)

07 player = dbus.Interface(rb,‘org.gnome.Rhythmbox.Player’)

08 playing = player.getPlaying()09 if playing == True:10 rbshell =

bus.get_object(‘org.gnome.Rhythmbox’,‘/org/gnome/Rhythmbox/Shell’)

11 shell =dbus.Interface(rbshell,‘org.gnome.Rhythmbox.Shell’)

12 data =shell.getSongProperties(player.getPlayingUri())

1314 titulo = data[‘title’]

15 artista = data[‘artist’]16 album = data[‘album’]17 duracion = data[‘duration’]18 minutos = 019 while duracion >= 60:20 minutos += 121 duracion -= 602223 duracion_show = ‘%d:’ %

minutos24 if duracion < 10:25 duracion_show += ‘0’ +

str(duracion)26 else:27 duracion_show +=

str(duracion)2829 pasado = player.getElapsed()30 minutos = 031 while pasado >= 60:32 minutos += 133 pasado -= 603435 pasado_show = ‘%d:’ %

minutos36 if pasado < 10:

37 pasado_show += ‘0’ +str(pasado)

38 else:39 pasado_show += str(pasado)404142 output = ‘Sonando \”’ +

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

4344 if data[‘play-count’] + 1 >=

2:45 veces_sonado =

str(data[‘play-count’] + 1)46 output += ‘\n\tSe ha tocado

‘ + veces_sonado + ‘ veces.’4748 else:49 output = “\tRhythmbox no est

tocando ninguna canción”50 else:51 output = “\tRhythmbox no se

está ejecutando”52 print output.encode(“UTF-8”)

[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/

RECURSOS

044-047_PythonLM23 8/12/06 10:09 pm Página 47