# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# $Id: eventmanager.py,v 1.4 2005/03/08 16:13:42 mbaas Exp $
## \file eventmanager.py
## Contains the EventManager class.
import sys, types, bisect
# Receiver
class _Receiver:
"""Stores a receiver (=callable) and a priority.
This class defines a comparison operator (that's why this class is
used instead of a tuple (priority, receiver)).
"""
def __init__(self, receiver, priority=1):
self.receiver = receiver
self.priority = priority
def __str__(self):
# If the receiver is an instance method, then obtain the corresponding
# class name
cls = getattr(self.receiver, "im_class", None)
if cls!=None:
s = getattr(cls, "__name__", "?")+"."
else:
s = ""
s += getattr(self.receiver, "__name__", "<unnamed>")
return "(%d, %s)"%(self.priority, s)
__repr__ = __str__
def __cmp__(self, other):
return self.priority-other.priority
# EventManager
class EventManager:
"""Manages any kind of events.
System wide event receivers must indicate if the event should
be consumed (return value = True) or passed to the scene wide
receivers.
"""
def __init__(self):
"""Constructor."""
# System wide connections.
# Key: Event name - Value: Sorted list of _Receivers
self.system_connections = {}
# Scene wide connections.
# Key: Event name - Value: Sorted list of _Receivers
self.scene_connections = {}
def __str__(self):
s = 70*"-"+"\n"
s += "System events\n"
s += 70*"-"+"\n"
for event in self.system_connections:
s+='Event: "%s"\n'%event
for rec in self.system_connections[event]:
s+=" %s\n"%rec
s+="\n"
s += 70*"-"+"\n"
s += "Scene events\n"
s += 70*"-"+"\n"
for event in self.scene_connections:
s+='Event: "%s"\n'%event
for rec in self.scene_connections[event]:
s+=" %s\n"%rec
return s
# event
def event(self, name, *params, **keyargs):
"""Signal an event.
When an event handler returns True the notification is interrupted
(i.e. the event is consumed and not available anymore for other
handlers).
\param name (\c str) Name of the event.
\return True, if any event handler returned True.
\todo Aufruf der Empfaenger-Methode kann Exception verursachen.
"""
# Process system wide connections
receivers = self.system_connections.get(name, [])
for rec in receivers:
if rec.receiver(*params, **keyargs):
return True
# Process scene wide connections
receivers = self.scene_connections.get(name, [])
for rec in receivers:
if rec.receiver(*params, **keyargs):
return True
return False
# connect
def connect(self, name, receiver, priority=10, system=False):
"""Connect a function or method to an event.
The priority determines the calling order when the corresponding
event is emitted. Receivers with a smaller priority are invoked first.
\param name (\c str) Name of the event.
\param receiver The receiving function or method, or an instance
of a class that must implement an on<Event>() method.
\param priority (\c int) Priority of the receiver
\param system (\c bool) Specifies if the connection is system wide or
not (default: \c False).
"""
receiver = self._determine_receiver(name, receiver)
# Disconnect the receiver if it was already connected
try:
self.disconnect(name,receiver,system)
except KeyError:
pass
if system:
connections = self.system_connections
else:
connections = self.scene_connections
rec = _Receiver(receiver, priority)
# Has the event already any connections? then add the new receiver
if connections.has_key(name):
bisect.insort(connections[name], rec)
# connections[name].append(receiver)
# otherwise create a new list
else:
connections[name] = [rec]
return (name,receiver)
# disconnect
def disconnect(self, name, receiver=None, system=False):
"""Disconnect a function or method from an event.
\param name (\c str) Name of the event.
\param receiver The receiving function or method, or an instance
of a class that must implement an on<Event>() method.
\param system (\c bool) Specifies if the connection is system wide or
not (default: \c False).
"""
if isinstance(name,tuple):
name,receiver = name
if system:
connections = self.system_connections
else:
connections = self.scene_connections
# Are all connections to be removed?
if receiver==None:
if connections.has_key(name):
del connections[name]
return
receiver = self._determine_receiver(name, receiver)
# Has the event any connections at all?
if not connections.has_key(name):
raise KeyError, 'Receiver is not connected to event "%s"'%name
# Try to remove the connection
# try:
# connections[name].remove(receiver)
# except ValueError:
# raise KeyError, 'Receiver is not connected to event "%s"'%name
for i,rec in enumerate(connections[name]):
if rec.receiver==receiver:
break
else:
raise KeyError, 'Receiver is not connected to event "%s"'%name
del connections[name][i]
# disconnectAll
def disconnectAll(self, system=False):
"""Remove all connections.
\param system (\c bool) Specifies if the system wide connections or
the scene wide connections shall be removed
(default: \c False).
"""
if system:
self.system_connections = {}
else:
self.scene_connections = {}
## private:
def _determine_receiver(self, name, receiver):
"""Returns the receiver object.
The receiver is the callable that gets called when an event
occurs.
\param name (\c str) Name of the event.
\param receiver The receiving function or method, or an instance
of a class that must implement an on<Event>() method.
\return Callable object.
"""
methodname = "on"+name
if hasattr(receiver, methodname):
return getattr(receiver, methodname)
if callable(receiver):
return receiver
raise ValueError, "Receiver argument must be a callable or an object with an %s() method"%methodname
######################################################################
_global_event_manager = EventManager()
# eventManager
def eventManager():
"""Returns the global event manager.
\return Event manager (EventManager)
"""
global _global_event_manager
return _global_event_manager
|