jueves, 29 de abril de 2010

Notas Python: GObject en Python

GObject, la clase base

GObject, una clase que implementa todo lo básico, y que forma la base de todas las demás clases, tanto en GTK+ como en el resto de librerías de la plataforma GNOME.

El lenguaje C no es un lenguaje preparado para POO, pero con un esfuerzo mínimo, y gracias al trabajo de los desarrolladores de GTK+, se puede usar en este lenguaje. Esto se consigue mediante el sistema de objetos de GLib (GObject), que, mediante el uso de las características del lenguaje C, ofrece la posibilidad de usar POO.

Este sistema de objetos de GLib tiene algunas limitaciones propias del uso del lenguaje C (lenguaje no pensado para la POO), pero aun así, su funcionalidad es tal, que es más que probable que jamás se eche ninguna funcionalidad en falta. Dicha funcionalidad incluye:



  • herencia, característica imprescindible de cualquier lenguaje orientado a objetos que se precie de serlo; permite la creación de clases que heredan la funcionalidad de otras clases ya existentes. Esto permite crear clases con la funcionalidad básica y, basadas en dicha clase, crear otras que añadan una funcionalidad más específica.
  • polimorfismo, que permite tratar a un mismo objeto bajo distintas personalidades.
  • interfaces, que permite la definición de interfaces (clases abstractas) y su posterior implementación en clases.


Python es un lenguaje orientado a objetos por lo que a primera vista se puede pensar que las librerías gobject no son necesarias pero, hay algunas características propias de gobject que son muy útiles cuando se está trabajando en GNOME. A continuación voy a poner las dos que para mi son mas importantes:


  • Señales, este mecanísmo permite conectar nuestro programa con su entorno. Es muy útil cuando se usa para comunicarse entre diferentes partes un mismo proyecto.
  • Notificaciones cuando se cambian las propiedades de la clase (properties). Permite “conectar” funciones (callback) que se ejecutan cuando una propiedad cambia.


Sub clases gobject en Python.

Vamos a ver el proceso de creación de una subclase de GObject en Python.

Crear propiedades nuevas.

Lo primero que se hace es crear una clase “Car” que hereda de “gobject.GObject”. A continuación se crea el diccionario __gproperties__. En este caso, este diccionario solamente tiene una propiedad “fuel”.
En la funnción __init__ de nuestra clase, se inicializa el gobject con “gobject.GObject.__init__(self)”, usa para registrar los métodos y señales que hayamos creado.
Después se crean los métodos “do_get_property” y “do_set_property” para leer y escribir en las “properties” respectivamente.
Por último, se llama a la función gobject.type_register, con nustra clase como parámetro para registrar la clase como “official GType”, es necesario para que funcion correctamente.

import pygtk
pygtk.require('2.0')
import gobject

class Car(gobject.GObject):
    __gproperties__ = {
        'fuel' : (gobject.TYPE_FLOAT, 'fuel of the car',
                  'amount of fuel that remains in the tank',
                  0, 60, 50, gobject.PARAM_READWRITE)# typ, nick name,\
   description, minimum value, maximum value,default value, flags
        }

    def __init__(self):
        gobject.GObject.__init__(self)
        self.fuel = 50

    def do_get_property(self, property):
        if property.name == 'fuel':
            return self.fuel
        else:
            raise AttributeError, 'unknown property %s' % property.name

    def do_set_property(self, property, value):
        if property.name == 'fuel':
            self.fuel = value
        else:
            raise AttributeError, 'unknown property %s' % property.name

gobject.type_register(Car)
código procedente de http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm



Uso de las propiedades.
Para tener usar los métodos de lectura y escritura que hemos creado anteriormente (“do_get_property” y “do_set_property”), se escriben “get_property” y “set_propertie” respectivamente. Ver el código de ejemplo.

>>> from car1 import Car
>>> aCar = Car()
>>> print "The car has %f of fuel at the beginning" % aCar.get_property('fuel')
The car has 50.000000 of fuel at the beginning
>>> aCar.set_property('fuel', 20)
>>> print "Now the car has %f of fuel" % aCar.get_property('fuel')
Now the car has 20.000000 of fuel
código procedente de http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm

A continiación se muestra como hacer que se cree una señal cuando se modifica una propiedad. En el primer ejemplo, la señal se genera cuando se modifica cualquier “property”.

import pygtk
pygtk.require('2.0')
import gobject

from car1 import Car

def myCallback(obj, property):
    if property.name == 'fuel':
        if obj.get_property('fuel') < 10:
            print 'we are running out of fuel!!'
            
def test():
    aCar = Car()
    aCar.connect('notify', myCallback)
    aCar.set_property('fuel', 5.0)

if __name__ == '__main__':
    test()

En cambio en el segundo ejemplo, sólo se genera la señal cuando se modifica la “property” fuel.

import pygtk
pygtk.require('2.0')
import gobject
from car1 import Car

def myCallback(obj, property):
    if obj.get_property('fuel') < 10: 
        print 'we are running out of fuel!!'

def test():
    aCar = Car()
    aCar.connect('notify::fuel', myCallback) 
    aCar.set_property('fuel', 5.0)

if __name__ == '__main__':
    test()
código procedente de http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm


Aviso


Si se usan nombres compuestos para las propiedades, cuado se definen en el diccionariop __gproperties__ se deben separar con el carácter '_' (background_color), pero en cambio, cuando se define y se usa el argumento de la clase, se debe hacer con el carácter '-' (self.background-color).


Creación de señales propias con gobject.


Al crear una clase heredada de gobject se pueden crear señales para que una vez que creamos objetos de esa clase, se puedan conectar a las señales que hemos creado.

A continuación voy a porner un ejemplo de la clase Car, creada antes, añadiendo una señal ('engine-started').

Es necesario crear un diccionario llamado __gsinals__, que contiene:
El nombre de la señal como clave y
los datos de configuración de la señal

Para emitir la señal, se usa el método emit. Los parámetros de dicho método, son el nombre de la señal y, en este caso, la propiedad que hemos creado (fuel).

El método do_engine_started, es opcional y se ejecuta cuando la señal se emite.

import pygtk
pygtk.require('2.0')
import gobject

class Car(gobject.GObject):
    __gproperties__ = {
        'fuel' : (gobject.TYPE_FLOAT, 'fuel of the car',
                  'amount of fuel that remains in the tank',
                  0, 60, 50, gobject.PARAM_READWRITE)
        }

    __gsignals__ = {
        'engine-started' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                            (gobject.TYPE_FLOAT,))
        }

    def __init__(self):
        gobject.GObject.__init__(self)
        self.fuel = 50

    def do_get_property(self, property):
        if property.name == 'fuel':
            return self.fuel
        else:
            raise AttributeError, 'unknown property %s' % property.name

    def do_set_property(self, property, value):
        if property.name == 'fuel':
            self.fuel = value
        else:
            raise AttributeError, 'unknown property %s' % property.name

    def do_engine_started(self, remaining_fuel):
        print '***** Beginning of class closure *****'
        print 'The engine is ready and we still have %f of fuel' % self.fuel
        print '***** End of class closure *****'

    def start(self):
        self.emit('engine-started', self.get_property('fuel'))
        
gobject.type_register(Car)
código procedente de http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm

Uso de las señales. Se muestra como podemos conectar un objeto de la clase que hemos creado a las señales que hemos definido:


import pygtk
pygtk.require('2.0')
import gobject

from car4 import Car

def myCallback(obj, remaining_fuel, data=None):
    print '***** Beginning of User callback *****'
    print 'The engine is starting and we still have %f of fuel' % remaining_fuel
    print '***** End of User callback *****'

def lastCallback(obj, remaining_fuel, data=None):
    print '***** Callback connected with connect_after *****'
    obj.set_property('fuel', remaining_fuel - 10)
    print 'Now we have %f of fuel' % obj.get_property('fuel')
    print '***** End of this callback *****'
    
def test():
    aCar = Car()
    aCar.connect('engine-started', myCallback)
    aCar.connect_after('engine-started', lastCallback)

    aCar.start()
    
if __name__ == '__main__':
    test()
código procedente de http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm



Documentación:

python subclass
http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm
gobject reference manual:
http://library.gnome.org/devel/gobject/stable/
glib:
http://www.es.gnome.org/Documentacion/Desarrollo/SistemaDeObjetosDeGlib

No hay comentarios:

Publicar un comentario