Código fuente para pilas.evento
# -*- encoding: utf-8 -*-
# Pilas engine - A video game framework.
#
# Copyright 2010 - Hugo Ruscitti
# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
#
# Website - http://www.pilas-engine.com.ar
#
# Este sistema de eventos está basado en:
#
# http://stackoverflow.com/questions/1092531/event-system-in-python
#
import weakref
import new
import inspect
import pilas
__doc__ = """
Módulo pilas.evento
===================
"""
class Evento():
def __init__(self, nombre):
self.respuestas = set()
self.nombre = nombre
def emitir(self, **evento):
a_eliminar = []
for respuesta in set(self.respuestas):
try:
respuesta(**evento)
except ReferenceError:
a_eliminar.append(respuesta)
if a_eliminar:
for x in a_eliminar:
self.desconectar(x)
def conectar(self, respuesta, id=None):
if inspect.isfunction(respuesta):
self.respuestas.add(ProxyFuncion(respuesta, id))
elif inspect.ismethod(respuesta):
self.respuestas.add(ProxyMetodo(respuesta, id))
else:
raise ValueError("Solo se permite conectar nombres de funciones o metodos.")
def desconectar(self, respuesta):
try:
self.respuestas.remove(respuesta)
except:
raise ValueError("La funcion indicada no estaba agregada como respuesta del evento.")
def desconectar_por_id(self, id):
a_eliminar = []
for respuesta in self.respuestas:
if respuesta.id == id:
a_eliminar.append(respuesta)
for x in a_eliminar:
self.desconectar(x)
def esta_conectado(self):
return len(self.respuestas) > 0
def imprimir_funciones_conectadas(self):
if not self.esta_conectado():
print "\t << sin funciones conectadas >>"
else:
for x in self.respuestas:
print "\t +", x.nombre, " en ", x.receptor
class AttrDict(dict):
"""Envoltorio para que el diccionario de eventos
se pueda acceder usando como si tuviera attributos
de objeto.
Por ejemplo, con esta clase es posible que un diccionario
se pueda usar así:
>>> b = AttrDict({'x': 123})
>>> b.x
123
>>> b['x']
123
"""
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
def __getattr__(self, name):
return self[name]
class ProxyFuncion(object):
"""
Representa a una función de repuesta pero usando
una referencia débil.
"""
def __init__(self, cb, id):
self.funcion = weakref.ref(cb)
self.id = id
self.nombre = str(cb)
self.receptor = str('modulo actual')
def __call__(self, **evento):
f = self.funcion()
if f is not None:
f(AttrDict(evento))
else:
raise ReferenceError("La funcion dejo de existir")
class ProxyMetodo(object):
"""
Permite asociar funciones pero con referencias débiles, que no
incrementan el contador de referencias.
Este proxy funciona tanto con funciones como con métodos enlazados
a un objeto.
@organization: IBM Corporation
@copyright: Copyright (c) 2005, 2006 IBM Corporation
@license: The BSD License
"""
def __init__(self, cb, id):
try:
try:
self.inst = weakref.ref(cb.im_self)
except TypeError:
self.inst = None
self.func = cb.im_func
self.klass = cb.im_class
except AttributeError:
self.inst = None
try:
self.func = cb.im_func
except AttributeError:
self.func = cb
self.klass = None
self.id = id
self.nombre = str(cb.__name__)
self.receptor = self.klass
def __call__(self, **evento):
if self.inst is not None and self.inst() is None:
raise ReferenceError("El metodo ha dejado de existir")
elif self.inst is not None:
mtd = new.instancemethod(self.func, self.inst(), self.klass)
else:
mtd = self.func
return mtd(AttrDict(evento))
def __eq__(self, other):
try:
return self.func == other.func and self.inst() == other.inst()
except Exception:
return False
def __ne__(self, other):
return not self.__eq__(other)
[documentos]class ProxyEventos(object):
"""Representa el objeto pilas.evento, que internamente delega todos los metodos
conectados a la escena actual.
Para acceder a este objeto, usar una sentencia como la siguiente:
>>> pilas.eventos.click_de_mouse.conectar(una_funcion)
La función enviada como parámetro será invocada cuando el evento
ocurra. Y se enviará como argumento los datos del evento, por ejemplo:
>>> def cuando_hace_click(evento):
... print evento.x
... print evento.y
...
>>> pilas.eventos.click_de_mouse.conectar(cuando_hace_click)
"""
@property
[documentos] def click_de_mouse(self):
"""Informa ante la pulsación del mouse.
:param x: Posición horizontal del mouse.
:param y: Posición vertical del mouse.
:param dx: Posición horizontal relativa del mouse.
:param dy: Posición vertical relativa del mouse.
:param boton: Botón del mouse que se pulsó (1 - Izquierdo, 2 - Derecho, 4 - Central)
"""
return pilas.escena_actual().click_de_mouse
@property
[documentos] def mueve_camara(self):
"""Informa que ha cambiado la posición de la cámara.
:param x: Posición horizontal de la cámara.
:param y: Posición vertical de la cámara.
:param dx: Movimiento relativo horizontal que sufrió la cámara.
:param dy: Movimiento relativo vertical que sufrió la cámara.
"""
return pilas.escena_actual().mueve_camara
@property
[documentos] def mueve_mouse(self):
"""Informa que la posición del mouse ha cambiado.
:param x: Posición horizontal del mouse.
:param y: Posición vertical del mouse.
:param dx: Posición horizontal relativa del mouse.
:param dy: Posición vertical relativa del mouse.
"""
return pilas.escena_actual().mueve_mouse
@property
[documentos] def termina_click(self):
"""Informa cuando la pulsación del mouse termina.
:param x: Posición horizontal del mouse.
:param y: Posición vertical del mouse.
:param dx: Posición horizontal relativa del mouse.
:param dy: Posición vertical relativa del mouse.
:param boton: Botón del mouse que se pulsó (1 - Izquierdo, 2 - Derecho, 4 - Central)
"""
return pilas.escena_actual().termina_click
@property
[documentos] def mueve_rueda(self):
"""Indica que cambió la rueda del mouse que se utiliza para desplazamiento o scroll.
:param delta: indica el grado de rotación de la rueda del mouse.
"""
return pilas.escena_actual().mueve_rueda
@property
[documentos] def pulsa_tecla(self):
"""Informa que se ha pulsado una tecla del teclado.
:param codigo: Codigo de la tecla normalizado, por ejemplo ``simbolos.m``.
:param es_repeticion: Indica si el evento surgió por repetición de teclado. False indica que es la primer pulsación.
:param texto: Cadena de texto que indica la tecla pulsada, por ejemplo ``"m"``.
"""
return pilas.escena_actual().pulsa_tecla
@property
[documentos] def suelta_tecla(self):
"""Informa que se ha soltado una tecla del teclado.
:param codigo: Codigo de la tecla normalizado, por ejemplo ``simbolos.m``.
:param es_repeticion: Indica si el evento surgió por repetición de teclado. False indica que es la primer pulsación.
:param texto: Cadena de texto que indica la tecla pulsada, por ejemplo ``"m"``.
"""
return pilas.escena_actual().suelta_tecla
@property
[documentos] def pulsa_tecla_escape(self):
"""Indica que se ha pulsado la tecla ``scape``."""
return pilas.escena_actual().pulsa_tecla_escape
@property
[documentos] def actualizar(self):
"""Se invoca regularmente, 60 veces por segundo."""
return pilas.escena_actual().actualizar
@property
[documentos] def log(self):
"""Indica que se emitió un mensaje para depuración usando la función ``pilas.log``."""
return pilas.escena_actual().log
@property
def Evento(self):
return Evento