Joaquin Sorianello: TSXCUSB485 en GNU/Linux!

Arranque esta semana testeando un dispositivo MODBUS, sobre un enlace RS485. Para ello decidi utilizar un lindo conversor usb a RS485, de la firma Schneider Electrics: el TSXCUSB 485.El aparatito en Cuestión

Probe en Win, lo detecto como un puerto com, usando un driver privativo. Probe en GNU/Linux y.... nada... me mostraba el vendor id y el product id del dispositivo pero no me lo montaba como un "ttyUSBx".

Las opciones: resignarse y usarlo bajo windows, o buscar, escribir, parchar lo que fuera necesario.

Obviamente, elejí la opción 2, ya que si lo sacaba funcionando tenia resuelto un punto importante en materia de comunicaciones industriales, rubro al que me dedico.

Empeze leyendo un poco de guias sobre como son los drivers usb en linux, y googleando si alguien lo habia hecho andar, pero, se ve que a los que trabajan de esto usan solo windows.

El primer paso para hacer ingeniería inversa, era ver el hardware que componia la placa:



Detalle de la Plaqueta
Ya, de un golpe de vista nos encontramos con el corazón de la conversión usb-rs232, el ft232bl, un chip bastante estandar.
El lugar mas ovio para encontrar información era la pagina del fabricante, y ¡Oh Sorpresa! estaba el driver para nuestro querido pinguÑu, incluso una guia para agregar vendor y product ids pero:
La edición del driver, para que reconozca el dispositivo, en si, es bastante trivial, pero el problema es compilarlo: hay muchas versiones del driver y muy pocas de ellas son compilables, porque, según entiendo, el soporte de usb del kernel se fue modificando con cada versión.

Finalmente, termine buceando en el git de www.kernel.org y encontré uno que compilo de maravillas.

La verdad que me sorprende cuan poderoso es el software libre: en el sistema de la ventana, ante un problema así ¡estas fregado!
En balance de tiempo tarde unas 3 horas en resolverlo, pero ya esta!!! cuando consiga comittear mi pach para el driver, en futuros kernels, el dispositivo va a tener soporte, ¡para mucha gente!

Pablo Alejandro Costesich: Experimentos con DBus en Python: Nombre Conocido, Exportando Objetos y Lanzando Señales

Nombre Conocido

En el tutorial oficial no está documentado, pero tampoco es complejo. Sólo se necesita usar el método dbus.service.BusName para reclamar un nombre y pasarle el bus al que estará asociado.

import gobject
from dbus.service import BusName
from dbus import SessionBus
from dbus.mainloop.glib import DBusGMainLoop

dbus_loop = DBusGMainLoop()
session = SessionBus(mainloop=dbus_loop)

name = BusName('com.blogger.pcostesi.Test', session)

loop = gobject.MainLoop()
gobject.threads_init()
loop.run()

Lo anterior es inseguro, ya que no realizamos ninguna verificación sobre la existencia previa de un servicio del mismo modo. Lo correcto sería envolver el llamado en un bloque try/except, como hace Roberto Alsina.

Una vez que tenemos el BusName reservado para nuestro servicio, podemos exponer objetos.


Exportando Objetos

Para publicar un servicio, es necesario encapsularlo en una clase que herede de dbus.service.Object, y los métodos que quisiéramos que sean accesibles desde el bus deben ser decorados con dbus.service.method. Object tiene que ser inicializado en __init__ para que funcione, pasándole un Nombre Conocido o un Bus y el path del objeto.

Un servicio ejemplo sería:


import gobject
from dbus.service import BusName, Object, method
from dbus import SessionBus
from dbus.mainloop.glib import DBusGMainLoop

dbus_loop = DBusGMainLoop()
session = SessionBus(mainloop=dbus_loop)

class Ejemplo(Object):
instancias = 0
def __init__(self, bus, loop):
# Nombre Conocido de la aplicación
name = BusName('com.blogger.pcostesi.Test', bus)
Ejemplo.instancias += 1
self.loop = loop
self.path = '/com/blogger/pcostesi/Ejemplo/%s' % Ejemplo.instancias
super(Ejemplo, self).__init__(name, self.path)
print self.path, "listo."

@method(dbus_interface='com.blogger.pcostesi.Ejemplo',
in_signature='s', out_signature='', sender_keyword='sender',
destination_keyword='dest')
def saludo(self, saludo, sender='alguien', dest='mi'):
print "%s dijo a %s: %s" % (sender, dest, saludo)

@method(dbus_interface='com.blogger.pcostesi.CerrarEjemplo')
def salir(self):
self.loop.quit()

loop = gobject.MainLoop()
gobject.threads_init()

ejemplo1 = Ejemplo(session, loop)
ejemplo2 = Ejemplo(session, loop)

loop.run()

Y un cliente sólo necesitaría ser así:

import dbus
session = dbus.SessionBus()

ejemplo1 = session.get_object('com.blogger.pcostesi.Test',
'/com/blogger/pcostesi/Ejemplo/1')
ejemplo2 = session.get_object('com.blogger.pcostesi.Test',
'/com/blogger/pcostesi/Ejemplo/2')

ej1_saludo = dbus.Interface(ejemplo1,
dbus_interface='com.blogger.pcostesi.Ejemplo')

ej2_saludo = dbus.Interface(ejemplo2,
dbus_interface='com.blogger.pcostesi.Ejemplo')

ej1_cerrar = dbus.Interface(ejemplo1,
dbus_interface='com.blogger.pcostesi.CerrarEjemplo')

ej2_cerrar = dbus.Interface(ejemplo2,
dbus_interface='com.blogger.pcostesi.CerrarEjemplo')

ej1_saludo.saludo('Hola')
ej2_saludo.saludo('Hello')
ej1_cerrar.salir()

Notarán la gran cantidad de parámetros que recibe el decorador. No todos son necesarios, sólo la interfaz. Recomiendo llenar in_signature y out_signature para despejar dudas.

Los keywords que admite son:
  • dbus_interface: la interfaz a la que el método pertenecerá
  • in_signature: la firma de entrada de la función, según los Tipos de Datos.
  • out_signature: la firma de salida de la función, según los Tipos de Datos.
  • async_callbacks: una tupla que contiene dos strings, cada uno con los nombres de keyword en el método que es decorado (de éxito y de error respectivamente) para cuando son llamados asincrónicamente.
    • sender_keyword
    • path_keyword: keyword a la dirección del objeto
    • rel_path_keyword: similar al anterior, pero el objeto recibirá un path relativo.
    • destination_keyword
    • message_keyword
    • connection_keyword: keyword para pasar un objeto dbus.connection.Connection. Útil por si este objeto está en más de una conexión.
    • utf8_strings
    • byte_arrays

    Lanzando Señales


    A diferencia de los métodos exportados, las señales trabajan sólo con los parámetros de entrada y tienen el mismo nombre que la función a la que se llama. Para crear una señal, lo único que hay que hacer es aplicar el decorador dbus.service.signal.

    Agreguemos a nuestro ejemplo anterior una señal para cuando llamemos al método saludo:

    import gobject
    from dbus.service import BusName, Object, method, signal
    from dbus import SessionBus
    from dbus.mainloop.glib import DBusGMainLoop

    dbus_loop = DBusGMainLoop()
    session = SessionBus(mainloop=dbus_loop)

    class Ejemplo(Object):
    instancias = 0
    def __init__(self, bus, loop):
    # Nombre Conocido de la aplicación
    name = BusName('com.blogger.pcostesi.Test', bus)
    Ejemplo.instancias += 1
    self.loop = loop
    self.path = '/com/blogger/pcostesi/Ejemplo/%s' % Ejemplo.instancias
    super(Ejemplo, self).__init__(name, self.path)
    print self.path, "listo."

    @method(dbus_interface='com.blogger.pcostesi.Ejemplo',
    in_signature='s', out_signature='', sender_keyword='sender',
    destination_keyword='dest')
    def saludo(self, saludo, sender='alguien', dest='mi'):
    print u"%s dijo a %s: %s" % (sender, dest, saludo)
    self.dicho(sender, dest, saludo)

    @method(dbus_interface='com.blogger.pcostesi.CerrarEjemplo')
    def salir(self):
    self.loop.quit()

    @signal(dbus_interface='com.blogger.pcostesi.Ejemplo', signature='sss')
    def dicho(self, s, d, saludo):
    print 'Notificando: dicho(%s, %s, %s)' % (s, d, saludo)

    loop = gobject.MainLoop()
    gobject.threads_init()

    ejemplo3 = Ejemplo(session, loop)
    loop.run()

    dbus.service.signal recibe como parámetros:
    • dbus_interface: interfaz que lanzará la señal
    • signature: firma que tiene la señal
    • path_keyword (no puede pasarse como posicional, sólo como keyword)
    • rel_path_keyword


    Y eso es básicamente todo lo necesario de dbus.

    Pablo Alejandro Costesich: Experimentos con DBus en Python: Escuchando Señales

    Señales

    Si corren el último ejemplo del post anterior con dbus-monitor --session (para ver los mensajes en el bus), verán que se envían mensajes que no son "llamados" a ninguna función, sino que son emitidas en eventos. Esos mensajes se llaman señales.

    Existen dos formas de capturar señales: una es escuchando un bus, mientras que la otra es escuchando una interfaz de un objeto. En un principio pueden sonar similares, pero el propósito de las mismas es radicalmente distinto. Es necesario en ambos casos disponer de un event loop.

    En el caso de escuchar un bus tenemos el método add_signal_receiver. Como parámetros, admite:
    • handler_function, una función (callable) a la que se llamará cuando se reciba la señal. Recibirá como parámetros los argumentos de la señal.
    • signal_name, el nombre de la señal.
    • dbus_interface, la interfaz asociada al emisor de la señal. Escucha interfaces y no objetos.
    • bus_name, el Nombre Conocido de la aplicación.
    • path, la dirección de un Objeto.
    • keywords (ver más adelante).

    En todas (excepto handler_function) el default es None, que escucha todas. Devuelve un objeto SignalMatch, que tiene un método remove() para eliminar la conexión. Los keywords utf8_strings (booleana) y byte_arrays (booleana) afectan los tipos al llamar el handler.

    Un ejemplo de una función catch-all sería:

    from dbus import SystemBus
    from dbus.mainloop.glib import DBusGMainLoop
    import gobject

    def handler(*args, **kwargs):
    print '='*10
    print 'args:', args
    print 'kwargs', kwargs

    dbus_loop = DBusGMainLoop()
    session = SystemBus(mainloop=dbus_loop)

    session.add_signal_receiver(handler)

    loop = gobject.MainLoop()
    gobject.threads_init()
    loop.run()

    Si tienen un teclado multimedia, prueben presionando play o subiendo y bajando el volumen. Podríamos refinar lo anterior para escuchar sólo elementos con la interfaz org.freedesktop.Hal.Device de este modo:

    session.add_signal_receiver(handler,
    dbus_interface='org.freedesktop.Hal.Device',
    sender_keyword='sender')

    Para escuchar señales de una interfaz asociada a un objeto, disponemos de connect_to_signal. Los parámetros son:
    • handler_function, una función (callable) a la que se llamará cuando se reciba la señal. Recibirá como parámetros los argumentos de la señal.
    • dbus_interface, la interfaz asociada al emisor de la señal.
    • keywords (ver más adelante).

    También cuenta con utf8_strings y byte_arrays, como add_signal_receiver.

    Un ejemplo trivial:

    from dbus import Interface

    class Notifier(object):

    def __init__(self, bus, replace=False, app_name='', loop=None):
    self.proxy = bus.get_object('org.freedesktop.Notifications',
    '/org/freedesktop/Notifications')
    self.interface = Interface(self.proxy,
    dbus_interface='org.freedesktop.Notifications')
    self.app_name = app_name
    self.prev = 0
    self.loop = loop
    self.replace = replace
    self.r = {1: 'expiró', 2: 'fue cerrada por el usuario',
    3: 'se llamó a CloseNotification',
    4: 'pasó algo no esperado'}
    # Conectamos la señal
    self.interface.connect_to_signal('NotificationClosed',
    self.available)

    def notify(self, message, timeout=-1):
    self.prev = self.interface.Notify('System', self.prev, '',
    self.app_name, message, [], {}, timeout)

    # La señal envía como parámetros id y reason.
    def available(self, id, reason):
    print "La notificación %i se cerró debido a que %s." % (id,
    self.r[reason])
    self.loop.quit()


    Y se implementa así:

    from dbus import SessionBus
    from dbus.mainloop.glib import DBusGMainLoop
    import gobject, dbus

    dbus_loop = DBusGMainLoop()
    session = dbus.SessionBus(mainloop=dbus_loop)

    loop = gobject.MainLoop()
    gobject.threads_init()

    notifier = Notifier(session, app_name='Prueba', loop=loop)
    def notify():
    notifier.notify('Hola', 3*1000)

    gobject.timeout_add(1000, notify)
    loop.run()


    Keywords

    Tanto add_signal_receiver como su contraparte connect_to_signal en una interfaz o proxy admiten keywords que son utilizadas para pasar información extra a nuestros handlers bajo el nombre que le asignemos. Las más usadas son:
    • sender_keyword: El nombre único del emisor de la señal.
    • destination_keyword: El destino de la señal.
    • interface_keyword: La interfaz asociada que lanza la señal.
    • member_keyword: El nombre de la señal.
    • path_keyword: El path o dirección del objeto emisor.
    • message_keyword: el mensaje, como dbus.lowlevel.SignalMessage
    Si no son especificadas, no se pasan.

    Pablo Alejandro Costesich: Experimentos con DBus en Python: Loop de Eventos y Llamados Asincrónicos

    Llamadas Asincrónicas

    Ahora, nuestros programas cuentan con la capacidad de realizar llamados por el bus a otros objetos, pero éstos son bloqueantes -- el programa se quedará esperando una respuesta antes de seguir. A menudo, esto es el comportamiento que deseamos y muchas veces nos es suficiente, pero dado que algunos servicios pueden tardar un tiempo largo (como descubrir aparatos bluetooth en el entorno), a veces queremos procesar otras cosas.

    Primero, y para que esto sea posible, necesitamos crear un Event Loop. Es un método al que llamaremos y se encargará de esperar eventos para despacharlos a nuestras funciones.

    Hay dos loops disponibles: uno para Qt y otro para Gtk. Yo prefiero el de Gtk, principalmente porque tengo varias aplicaciones que usan pygtk y ninguna de pyqt4. Eso es cuestión del lector.

    Nota importante: se debe instanciar el loop antes de conectar el bus.

    En gtk, haríamos:

    from dbus import SessionBus
    from dbus import SessionBus
    from dbus.mainloop.glib import DBusGMainLoop
    import gobject

    dbus_loop = DBusGMainLoop()
    session = dbus.SessionBus(mainloop=dbus_loop)

    # Colocar señales y llamados asincrónicos aquí

    loop = gobject.MainLoop()
    gobject.threads_init()
    loop.run()

    O sino, también se puede definir un dbus_loop global de este modo:

    from dbus import SessionBus
    from dbus import SessionBus
    from dbus.mainloop.glib import DBusGMainLoop
    import gobject

    DBusGMainLoop(set_as_default=True)
    session = dbus.SessionBus()

    # Colocar señales y llamados asincrónicos aquí

    loop = gobject.MainLoop()
    # threads_init activa/desactiva threads cuando llama métodos en c
    gobject.threads_init()
    loop.run()

    Prefiero el primero, por una cuestión de ser explícito el dbus_loop que elijo.

    Nótese, sin embargo, que ese no es el loop general. Para ello usamos gobject.MainLoop y su método run.

    Para PyQT4, tenemos:

    # Basado en listing4.py de Roberto Alsina
    import sys
    import dbus
    from dbus import SessionBus
    from dbus.mainloop.qt import DBusQtMainLoop
    from PyQt4.QtCore import QCoreApplication

    app = QCoreApplication(sys.argv)
    mainloop = DBusQtMainLoop(set_as_default=True)
    dbus.set_default_main_loop(mainloop)

    # Colocar señales y llamados asincrónicos aquí

    app.exec_()

    El código extra es para iniciar QT (framework que todavía no usé en python).


    Una vez que sabemos crear un main loop y usarlo, podemos llamar funciones asincrónicamente. Para ello, sólo tenemos que llamar al método que queremos con dos keywords:
    • reply_handler - Función (callable) a la que se pasa como parámetros los resultados del método remoto
    • error_handler - Función (callable) que recibe un solo argumento de la clase DBusException

    A modo ilustrativo (porque es bastante fácil), una versión modificada del Notifier del post pasado:

    from dbus import Interface

    class Notifier(object):

    def __init__(self, bus, replace=False, app_name=''):
    self.proxy = bus.get_object('org.freedesktop.Notifications',
    '/org/freedesktop/Notifications')
    self.interface = Interface(self.proxy,
    dbus_interface='org.freedesktop.Notifications')
    self.app_name = app_name
    self.prev = 0
    self.replace = replace

    def notify(self, message, timeout=-1):
    self.interface.Notify('System', self.prev, '',
    self.app_name, message, [], {}, timeout,
    reply_handler=self.reply, error_handler=self.error)

    def reply(self, n):
    self.prev = n

    def error(self, exception):
    print exception

    Y para implementarlo:

    from dbus import SessionBus
    from dbus.mainloop.glib import DBusGMainLoop
    import gobject, dbus

    # Creamos el loop de dbus y la sesión
    dbus_loop = DBusGMainLoop()
    session = dbus.SessionBus(mainloop=dbus_loop)

    # Creamos el loop de eventos
    loop = gobject.MainLoop()
    gobject.threads_init()

    notifier = Notifier(session, app_name='Prueba')

    def notify():
    notifier.notify('Hola', 15*1000)
    loop.quit()

    # Llama a los cinco segundos el método notify. Esto lo hacemos
    # porque no estamos dentro de una aplicación de pygtk, por lo
    # que gobject no puede "escuchar" nuestros eventos sin modificar
    # el loop.
    gobject.timeout_add(5000, notify)

    loop.run()

    Probablemente, si la aplicación es sólo-consola, quieran usar threads para manejar el event-loop sin bloquear otras tareas. Una posible solución puede encontrarse aquí, aunque prefiero usar threads.

    Pablo Alejandro Costesich: Syntax Highlighting & LaTeX en Blogger

    Como estudiante de Ing. en Informática, a veces siento la necesidad de publicar código y fórmulas... El problema es que por los medios habituales se complica, y en Blogger no hay ninguna herramienta por defecto para solucionarlo. ¿Qué hacer? O nos pasamos a Wordpress, o usamos un poco de magia en javascript.

    La instalación de SyntaxHighlighter es realmente sencilla, sólo cortar y pegar.

    ¿Y para LaTeX? Todavía más sencillo, ya que es integrable con el sistema de blogger para gadgets.

    La potencia de estos sistemas es realmente interesante. Uno puede escribir cosas como:

    import this

    for i in xrange(10):
    print i


    class Test(object):

    def __init__(self):
    self.message = 'hello!'

    def say_hello(self):
    print self.message
    return self.message

    def less_than_five(self, n):
    if 5 < n:
    print '%i is greater than five'


    O...


    $\setlength{\unitlength}{1mm}\begin{picture}(60, 40)\put(30, 20){\vector(1, 0){30}}\put(30, 20){\vector(4, 1){20}}\put(30, 20){\vector(3, 1){25}}\put(30, 20){\vector(2, 1){30}}\put(30, 20){\vector(1, 2){10}}\thicklines\put(30, 20){\vector(-4, 1){30}}\put(30, 20){\vector(-1, 4){5}}\thinlines\put(30, 20){\vector(-1, -1){5}}\put(30, 20){\vector(-1, -4){5}}\end{picture}$

    (Ejemplo de Watchmath)

    Pablo Alejandro Costesich: Experimentos con DBus en Python: Buses, Proxies, Interfaces y Tipos de datos

    Voy a seguir el orden del tutorial oficial, por si algo no queda claro.

    Buses

    En DBus existen dos clases de buses de comunicación: el bus de sistema (dbus.SystemBus) y el bus de sesión (dbus.SessionBus). Ambos se manejan más o menos del mismo modo, pero sus roles son radicalmente distintos.

    El bus de sistema sirve, principalmente, para la comunicación entre daemons y otros servicios centrales. El bus de sesión existe por cada usuario, y es responsable de comunicar servicios de modo más "local" y no system-wide.

    import dbus

    system = dbus.SystemBus()
    session = dbus.SessionBus()

    En DBus, cada aplicación puede referirse a sí misma con un "Nombre Conocido". Generalmente, tiene la forma de una URI invertida, como puede ser org.freedesktop.Hal. Adicionalmente, cada aplicación puede exponer varios objetos, tales como un Manager (org.freedesktop.Hal.Manager) o, si hablamos de un procesador de texto, podría exponer los archivos abiertos.

    Proxies

    Entonces, por ahora sólo tenemos los buses. ¿Qué podemos hacer con ellos? Obtener proxies del bus. DBus trabaja con un sistema exponiendo una interfaz y traduciendo los llamados, y para ello se debe crear una clase de objeto que se llama proxy. Éste actúa como intermediario al que se le pueden aplicar métodos, y que automáticamente ejecutará el envío del mensaje, y es el equivalente a un objeto de una aplicación.

    Interfaces

    Una vez hecho esto, podemos llamar algunos métodos sobre nuestro proxy. Para hacer esto, vamos a adquirir una Interfaz. Una interfaz, como seguramente saben, es un conjunto de funciones que se asocian a un objeto y le dan una capacidad determinada. Lo bueno de este sistema es que un objeto puede tener más de una interfaz disponible, con lo que podríamos aplicar sólo el subconjunto de acciones que necesitamos.

    Vamos a ver un ejemplo simple de llamar al sistema de Notificaciones en Linux:

    import dbus

    # No es necesario el SystemBus, sólo el SessionBus
    system = dbus.SystemBus()
    session = dbus.SessionBus()

    # Obtenemos el proxy con get_object
    # NOTA: get_object admite como parámetros:
    # * El Nombre Conocido de la aplicación
    # * El nombre del Objeto al que queremos acceder

    proxy = session.get_object('org.freedesktop.Notifications',
    '/org/freedesktop/Notifications')

    # Y le asociamos una interfaz. En nuestro caso, la interfaz
    # de Notify daemon se llama igual que el Nombre Conocido
    # de la aplicación que expone el servicio.

    interface = dbus.Interface(proxy,
    dbus_interface='org.freedesktop.Notifications')

    # Llamamos al método Notify en la interfaz para mostrar un
    # cartel con un saludo

    interface.Notify('System', 0, '', 'Title', 'Message', [], {}, -1)

    # Los detalles del método Notify pueden encontrarse en
    # http://www.galago-project.org/specs/notification/0.9/x408.html
    # Pero lo anterior cubre el 99% de los usos


    Notarán, sin embargo, que los métodos exportados de DBus en los proxies no son lo más Pythónico que existe. Podemos arreglar eso y sumar un par de features aprovechando la programación orientada a objetos:

    from dbus import Interface

    class Notifier(object):

    def __init__(self, bus, replace=False, app_name=''):
    self.proxy = bus.get_object('org.freedesktop.Notifications',
    '/org/freedesktop/Notifications')
    self.interface = Interface(self.proxy,
    dbus_interface='org.freedesktop.Notifications')
    self.app_name = app_name
    self.prev = 0
    self.replace = replace

    def notify(self, message, timeout=-1):
    self.prev = self.interface.Notify('System',
    self.prev, '', self.app_name, message, [], {}, timeout)
    if self.replace:
    self.prev = 0

    Y si queremos implementarla, simplemente haríamos:

    from dbus import SessionBus

    session = SessionBus()
    notifier = Notifier(session, app_name='Prueba')
    notifier.notify('Hola', 15*1000) # Tiempo en ms.

    Entonces, generalmente haremos lo siguiente:
    Instanciar un bus --> Obtener un proxy --> Obtener una interfaz asociada --> Llamar métodos en la interfaz.


    Tipos de Datos

    Si ya estuvieron metiendo mano a los ejemplos anteriores, notarán que no pueden enviar cualquier clase de datos. Esto pasa porque DBus es un sistema strongly typed (como Python) y static typed. Los parámetros tienen un tipo específico de datos, y cada método tiene una firma. No me voy a detener mucho en esto, ya que la documentación oficial es clara en ello.

    Pablo Alejandro Costesich: Experimentos con DBus en Python: Intro

    Hace unos días, Roberto Alsina expuso en esta entrada la necesidad que tenemos muchos de generar una respuesta visual ante ciertas acciones de nuestros equipos, más concretamente, eventos del teclado y ACPI. En particular, esta era una necesidad evidente para mí, que uso un entorno de escritorio poco común y muy minimalista (Openbox + LXDE). Fue por eso que decidí hacer un fork a su proyecto en github y probar las posibilidades en mi rama.

    Considero mi trabajo en el código del mismo un borrador, no más que una mera investigación antes del trabajo real. Como la API de DBus en Python es nada menos que caótica y áspera, trataré de documentar mis progresos a modo de anotación.

    Publiqué Voy a ir publicando (si el tiempo me lo permite) una serie de posts (parte 1, parte 2, parte 3 y parte 4), posiblemente breves, sobre dbus y su uso en Python.

    import dbus

    San Cayetano: arrancamos con pygame !!!

    
    
    # -*- coding: utf8 -*-
    #!/usr/bin/env python
    
    import pygame
    from pygame.locals import *
    from sys import exit
    
    pygame.init()
    
    imagen = 'linux.png'
    fondo = 'linux_2.jpg'
    
    pantalla = pygame.display.set_mode((500,400), 0 ,32)
    pygame.display.set_caption("Aparición")
    fondo = pygame.image.load(fondo).convert()
    pingui = pygame.image.load(imagen).convert_alpha()
    
    x, y = 200, 150
    
    
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                exit()
    
        keys = pygame.key.get_pressed()
    
        if keys[pygame.K_LEFT]:
            x -= 1
        elif keys[pygame.K_RIGHT]:
            x += 1
    
        if keys[pygame.K_DOWN]:
            y += 1
        elif keys[pygame.K_UP]:
            y -= 1
    
    
    
        pantalla.blit(fondo,(0,0))
        pantalla.blit(pingui,(x,y))
    
        pygame.display.update()