Hoy he estado implementando un sistema de plugins para www.sweetter.net, y para ello he decidido utilizar un modelo de señales, de tal forma que se puedan enlazar funciones a señales, y cuando se lance esta señal, se hace una llamada a todas las funciones enlazadas a esta señal.
La idea es simple, pero la implementación lo es aún más. Solo es necesario implementar un par de clases, Event y EventManager.
La clase Event
Como su propio nombre indica, esta clase representa un evento, o señal. Como atributos de clase tiene:
- (str)self.name, para nombrar a la señal.
- (dict)self.listeners, para guardar todas las funciones conectadas a esta señal, junto con los posibles parametros de estas.
Como metodos tiene:
- add(self, function, data=None)
- def delete(self, function), esta es la inversa a la función anterior, desconecta una función de una señal.
- def called(self, data=None), en esta función es donde está el meollo de la cuestión, ya que se encarga de llamar a las funciones de este evento. Si se pasa el parametro data, pues se utilizan estos parametros para la llamada a todas las funciones conectadas a esta señal
, que conecta una funcion a este evento, en data se pueden pasar los parametros de la función, como una tupla, o un dict. Si no se pasan, se llama a la función sin parametros.
La clase EventManager
Esta clase es la encargada de gestionar todos los eventos que tengamos, y que emite las señales.
Como atributos:
- (dict)self.events, en este parametro se almacenan todos los eventos.
Como metodos:
- def add_event(self, Event), este metodo añade un evento al EventManager.
- def del_event(self, Event), este metodo es el inverso del anterior, borra un evento.
- def connect(self, event, function, data=None), con este metodo se conecta una función con un evento, el evento debe existir en el event manager.
- def disconnect(self, event, function), esta es la función inversa a la anterior.
- def signal(self, event, data=None), esta es la función encargada de mandar señales, así que se llama cuando queramos generar un evento.
Ejemplo de uso
import events
em = events.EventManager()
em.add_event(events.Event('misenal1'))
...
em.add_event(events.Event('otrasenal'))
em.connect('misenal1', print)
em.connect('misenal1', str.join, [['uno', 'dos', 'tres']])
em.connect('otrasenal', str.split, "cadena dos")
...
em.signal('misenal1', 'esto se imprimiria')
em.signal('otrasenal')
...
Aquí el código completo
class Event():
def __init__(self, name):
self.name = name
self.listeners = {}
def add(self, function, data=None):
self.listeners[function] = data
def delete(self, function):
self.listeners.pop(function)
def called(self, data=None):
for function, d in self.listeners.items():
if data is None:
if d is None:
function()
else:
if type(d) == type([]):
function(*d)
elif type(d) == type({}):
function(**d)
else:
function(d)
else:
if type(data) == type([]):
function(*data)
elif type(data) == type({}):
function(**data)
else:
function(data)
class EventManager():
def __init__(self):
self.events = {}
def add_event(self, Event):
self.events[Event.name] = Event
def del_event(self, Event):
self.events.pop(Event.name)
def connect(self, event, function, data=None):
self.events[event].add(function, data)
def disconnect(self, event, function):
self.events[event].delete(function)
def signal(self, event, data=None):
if data is None:
self.events[event].called()
else:
self.events[event].called(data)
¡Great!