pilas.evento

Package pilas :: Module evento |
[hide private]
[frames] | no frames]

# Source Code for Module pilas.evento

      1  # -*- encoding: utf-8 -*-
      2  # Pilas engine - A video game framework.
      3  #
      4  # Copyright 2010 - Hugo Ruscitti
      5  # License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
      6  #
      7  # Website - http://www.pilas-engine.com.ar
      8  #
      9  # Este sistema de eventos está basado en:
     10  #
     11  #     http://stackoverflow.com/questions/1092531/event-system-in-python
     12  #
     13
     14  import weakref
     15  import new
     16  import inspect
     17  import pilas
     18
     19  __doc__ = """
     20  Módulo pilas.evento
     21  ===================
     22
     23  """
     24
     25 -class Evento():
     26
     27 -    def __init__(self, nombre):
     28          self.respuestas = set()
     29          self.nombre = nombre
     30
     31 -    def emitir(self, **evento):
     32          a_eliminar = []
     33
     34          for respuesta in set(self.respuestas):
     35              try:
     36                  respuesta(**evento)
     37              except ReferenceError:
     38                  a_eliminar.append(respuesta)
     39
     40          if a_eliminar:
     41              for x in a_eliminar:
     42                  self.desconectar(x)
     43
     44 -    def conectar(self, respuesta, id=None):
     45          if inspect.isfunction(respuesta):
     46              self.respuestas.add(ProxyFuncion(respuesta, id))
     47          elif inspect.ismethod(respuesta):
     48              self.respuestas.add(ProxyMetodo(respuesta, id))
     49          else:
     50              raise ValueError("Solo se permite conectar nombres de funciones o metodos.")
     51
     52 -    def desconectar(self, respuesta):
     53          try:
     54              self.respuestas.remove(respuesta)
     55          except:
     56              raise ValueError("La funcion indicada no estaba agregada como respuesta del evento.")
     57
     58 -    def desconectar_por_id(self, id):
     59          a_eliminar = []
     60          for respuesta in self.respuestas:
     61              if respuesta.id == id:
     62                  a_eliminar.append(respuesta)
     63
     64          for x in a_eliminar:
     65              self.desconectar(x)
     66
     67 -    def esta_conectado(self):
     68          return len(self.respuestas) > 0
     69
     70 -    def imprimir_funciones_conectadas(self):
     71          if not self.esta_conectado():
     72              print "\t << sin funciones conectadas >>"
     73          else:
     74              for x in self.respuestas:
     75                  print "\t +", x.nombre, " en ", x.receptor
     76
     77
     78 -class AttrDict(dict):
     79      """Envoltorio para que el diccionario de eventos
     80      se pueda acceder usando como si tuviera attributos
     81      de objeto.
     82
     83      Por ejemplo, con esta clase es posible que un diccionario
     84      se pueda usar así:
     85
     86          >>> b = AttrDict({'x': 123})
     87          >>> b.x
     88          123
     89          >>> b['x']
     90          123
     91      """
     92
     93 -    def __init__(self, *args, **kwargs):
     94          dict.__init__(self, *args, **kwargs)
     95
     96 -    def __getattr__(self, name):
     97          return self[name]
     98
     99
    100 -class ProxyFuncion(object):
    101      """
    102      Representa a una función de repuesta pero usando
    103      una referencia débil.
    104      """
    105
    106 -    def __init__(self, cb, id):
    107          self.funcion = weakref.ref(cb)
    108          self.id = id
    109          self.nombre = str(cb)
    110          self.receptor = str('modulo actual')
    111
    112 -    def __call__(self, **evento):
    113          f = self.funcion()
    114
    115          if f is not None:
    116              f(AttrDict(evento))
    117          else:
    118              raise ReferenceError("La funcion dejo de existir")
    119
    120
    121 -class ProxyMetodo(object):
    122      """
    123      Permite asociar funciones pero con referencias débiles, que no
    124      incrementan el contador de referencias.
    125
    126      Este proxy funciona tanto con funciones como con métodos enlazados
    127      a un objeto.
    128
    129      @organization: IBM Corporation
    130      @copyright: Copyright (c) 2005, 2006 IBM Corporation
    131      @license: The BSD License
    132      """
    133
    134 -    def __init__(self, cb, id):
    135          try:
    136              try:
    137                  self.inst = weakref.ref(cb.im_self)
    138              except TypeError:
    139                  self.inst = None
    140              self.func = cb.im_func
    141              self.klass = cb.im_class
    142          except AttributeError:
    143              self.inst = None
    144              try:
    145                  self.func = cb.im_func
    146              except AttributeError:
    147                  self.func = cb
    148
    149              self.klass = None
    150
    151          self.id = id
    152          self.nombre = str(cb.__name__)
    153          self.receptor = self.klass
    154
    155 -    def __call__(self, **evento):
    156          if self.inst is not None and self.inst() is None:
    157              raise ReferenceError("El metodo ha dejado de existir")
    158          elif self.inst is not None:
    159              mtd = new.instancemethod(self.func, self.inst(), self.klass)
    160          else:
    161              mtd = self.func
    162
    163          return mtd(AttrDict(evento))
    164
    165 -    def __eq__(self, other):
    166          try:
    167              return self.func == other.func and self.inst() == other.inst()
    168          except Exception:
    169              return False
    170
    171 -    def __ne__(self, other):
    172          return not self.__eq__(other)
    173
    174
    175 -class ProxyEventos(object):
    176      """Representa el objeto pilas.evento, que internamente delega todos los metodos
    177      conectados a la escena actual.
    178
    179      Para acceder a este objeto, usar una sentencia como la siguiente:
    180
    181          >>> pilas.eventos.click_de_mouse.conectar(una_funcion)
    182
    183      La función enviada como parámetro será invocada cuando el evento
    184      ocurra. Y se enviará como argumento los datos del evento, por ejemplo:
    185
    186          >>> def cuando_hace_click(evento):
    187          ...     print evento.x
    188          ...     print evento.y
    189          ...
    190          >>> pilas.eventos.click_de_mouse.conectar(cuando_hace_click)
    191
    192      """
    193
    194      @property
    195 -    def click_de_mouse(self):
    196          """Informa ante la pulsación del mouse.
    197
    198          :param x: Posición horizontal del mouse.
    199          :param y: Posición vertical del mouse.
    200          :param dx: Posición horizontal relativa del mouse.
    201          :param dy: Posición vertical relativa del mouse.
    202          :param boton: Botón del mouse que se pulsó (1 - Izquierdo, 2 - Derecho, 4 - Central)
    203          """
    204          return pilas.escena_actual().click_de_mouse
    205
    206      @property
    207 -    def mueve_camara(self):
    208          """Informa que ha cambiado la posición de la cámara.
    209
    210          :param x: Posición horizontal de la cámara.
    211          :param y: Posición vertical de la cámara.
    212          :param dx: Movimiento relativo horizontal que sufrió la cámara.
    213          :param dy: Movimiento relativo vertical que sufrió la cámara.
    214          """
    215          return pilas.escena_actual().mueve_camara
    216
    217      @property
    218 -    def mueve_mouse(self):
    219          """Informa que la posición del mouse ha cambiado.
    220
    221          :param x: Posición horizontal del mouse.
    222          :param y: Posición vertical del mouse.
    223          :param dx: Posición horizontal relativa del mouse.
    224          :param dy: Posición vertical relativa del mouse.
    225          """
    226          return pilas.escena_actual().mueve_mouse
    227
    228      @property
    229 -    def termina_click(self):
    230          """Informa cuando la pulsación del mouse termina.
    231
    232          :param x: Posición horizontal del mouse.
    233          :param y: Posición vertical del mouse.
    234          :param dx: Posición horizontal relativa del mouse.
    235          :param dy: Posición vertical relativa del mouse.
    236          :param boton: Botón del mouse que se pulsó (1 - Izquierdo, 2 - Derecho, 4 - Central)
    237          """
    238          return pilas.escena_actual().termina_click
    239
    240      @property
    241 -    def mueve_rueda(self):
    242          """Indica que cambió la rueda del mouse que se utiliza para desplazamiento o scroll.
    243
    244          :param delta: indica el grado de rotación de la rueda del mouse.
    245          """
    246          return pilas.escena_actual().mueve_rueda
    247
    248      @property
    249 -    def pulsa_tecla(self):
    250          """Informa que se ha pulsado una tecla del teclado.
    251
    252          :param codigo: Codigo de la tecla normalizado, por ejemplo ``simbolos.m``.
    253          :param es_repeticion: Indica si el evento surgió por repetición de teclado. False indica que es la primer pulsación.
    254          :param texto: Cadena de texto que indica la tecla pulsada, por ejemplo ``"m"``.
    255          """
    256          return pilas.escena_actual().pulsa_tecla
    257
    258      @property
    259 -    def suelta_tecla(self):
    260          """Informa que se ha soltado una tecla del teclado.
    261
    262          :param codigo: Codigo de la tecla normalizado, por ejemplo ``simbolos.m``.
    263          :param es_repeticion: Indica si el evento surgió por repetición de teclado. False indica que es la primer pulsación.
    264          :param texto: Cadena de texto que indica la tecla pulsada, por ejemplo ``"m"``.
    265          """
    266          return pilas.escena_actual().suelta_tecla
    267
    268      @property
    269 -    def pulsa_tecla_escape(self):
    270          """Indica que se ha pulsado la tecla ``scape``."""
    271          return pilas.escena_actual().pulsa_tecla_escape
    272
    273      @property
    274 -    def actualizar(self):
    275          """Se invoca regularmente, 60 veces por segundo."""
    276          return pilas.escena_actual().actualizar
    277
    278      @property
    279 -    def log(self):
    280          """Indica que se emitió un mensaje para depuración usando la función ``pilas.log``."""
    281          return pilas.escena_actual().log
    282
    283      @property
    284 -    def Evento(self):
    285          return Evento
    286