from PythonUtil import * from DirectNotifyGlobal import * class Messenger: notify = None def __init__(self): """ __init__(self) One dictionary does it all. It has the following structure: {event1 : {object1: [method, extraArgs, persistent], object2: [method, extraArgs, persistent]}, event2 : {object1: [method, extraArgs, persistent], object2: [method, extraArgs, persistent]}} Or, for an example with more real data: {'mouseDown' : {avatar : [avatar.jump, [2.0], 1]}} """ self.dict = {} if (Messenger.notify == None): Messenger.notify = directNotify.newCategory("Messenger") # Messenger.notify.setDebug(1) def accept(self, event, object, method, extraArgs=[], persistent=1): """ accept(self, string, DirectObject, Function, List, Boolean) Make this object accept this event. When the event is sent (using Messenger.send or from C++), method will be executed, optionally passing in extraArgs. If the persistent flag is set, it will continue to respond to this event, otherwise it will respond only once. """ if Messenger.notify.getDebug(): Messenger.notify.debug('object: ' + `object` + '\n now accepting: ' + `event` + '\n method: ' + `method` + '\n extraArgs: ' + `extraArgs` + '\n persistent: ' + `persistent`) acceptorDict = ifAbsentPut(self.dict, event, {}) acceptorDict[object] = [method, extraArgs, persistent] def ignore(self, event, object): """ ignore(self, string, DirectObject) Make this object no longer respond to this event. It is safe to call even if it was not already accepting """ if Messenger.notify.getDebug(): Messenger.notify.debug(`object` + '\n now ignoring: ' + `event`) # Find the dictionary of all the objects accepting this event acceptorDict = self.dict.get(event) # If this object is there, delete it from the dictionary if acceptorDict and acceptorDict.has_key(object): del acceptorDict[object] # If this dictionary is now empty, remove the event # entry from the Messenger alltogether if (len(acceptorDict) == 0): del self.dict[event] def ignoreAll(self, object): """ ignoreAll(self, DirectObject) Make this object no longer respond to any events it was accepting Useful for cleanup """ if Messenger.notify.getDebug(): Messenger.notify.debug(`object` + '\n now ignoring all events') for event, acceptorDict in self.dict.items(): # If this object is there, delete it from the dictionary if acceptorDict.has_key(object): del acceptorDict[object] # If this dictionary is now empty, remove the event # entry from the Messenger alltogether if (len(acceptorDict) == 0): del self.dict[event] def isAccepting(self, event, object): """ isAccepting(self, string, DirectOject) Is this object accepting this event? """ if self.dict.has_key(event): if self.dict[event].has_key(object): # Found it, return true return 1 # If we looked in both dictionaries and made it here # that object must not be accepting that event. return 0 def whoAccepts(self, event): """ whoAccepts(self, event): Return objects accepting the given event """ return self.dict.get(event, None) def isIgnoring(self, event, object): """ isIgnorning(self, string, DirectObject) Is this object ignoring this event? """ return (not self.isAccepting(event, object)) def send(self, event, sentArgs=[]): """ send(self, string, [arg1, arg2,...]) Send this event, optionally passing in arguments """ # Do not print the new frame debug, it is too noisy! if (Messenger.notify.getDebug() and (event != 'NewFrame')): Messenger.notify.debug('sent event: ' + event + ' sentArgs: ' + `sentArgs`) acceptorDict = self.dict.get(event) if not acceptorDict: return for object in acceptorDict.keys(): # We have to make this apparently redundant check, because # it is possible that one object removes its own hooks # in response to a handler called by a previous object. callList = acceptorDict.get(object) if callList: method, extraArgs, persistent = callList apply(method, (extraArgs + sentArgs)) # If this object was only accepting this event once, # remove it from the dictionary if not persistent: # We need to check this because the apply above might # have done an ignore. if acceptorDict.has_key(object): del acceptorDict[object] # If the dictionary at this event is now empty, remove the event # entry from the Messenger alltogether if (self.dict.has_key(event) and (len(self.dict[event]) == 0)): del self.dict[event] def clear(self): """clear(self) Start fresh with a clear dict """ self.dict.clear() def replaceMethod(self, oldMethod, newFunction): """ This is only used by Finder.py - the module that lets you redefine functions with Control-c-Control-v """ import new retFlag = 0 for entry in self.dict.items(): event, objectDict = entry for objectEntry in objectDict.items(): object, params = objectEntry method = params[0] if (type(method) == types.MethodType): function = method.im_func else: function = method #print ('function: ' + `function` + '\n' + # 'method: ' + `method` + '\n' + # 'oldMethod: ' + `oldMethod` + '\n' + # 'newFunction: ' + `newFunction` + '\n') if (function == oldMethod): newMethod = new.instancemethod(newFunction, method.im_self, method.im_class) params[0] = newMethod # Found it retrun true retFlag += 1 # didnt find that method, return false return retFlag def toggleVerbose(self): Messenger.notify.setDebug(1 - Messenger.notify.getDebug()) def __repr__(self): """ Compact version of event, acceptor pairs """ str = "="*64 + "\n" keys = self.dict.keys() keys.sort() for event in keys: str = str + event.ljust(32) + '\t' acceptorDict = self.dict[event] for object in acceptorDict.keys(): method, extraArgs, persistent = acceptorDict[object] className = object.__class__.__name__ methodName = method.__name__ str = str + className + '.' + methodName + ' ' str = str + '\n' return str def detailedRepr(self): """ Print out the table in a detailed readable format """ import types str = 'Messenger\n' str = str + '='*50 + '\n' keys = self.dict.keys() keys.sort() for event in keys: acceptorDict = self.dict[event] str = str + 'Event: ' + event + '\n' for object in acceptorDict.keys(): function, extraArgs, persistent = acceptorDict[object] if (type(object) == types.InstanceType): className = object.__class__.__name__ else: className = "Not a class" functionName = function.__name__ str = (str + '\t' + 'Acceptor: ' + className + ' instance' + '\n\t' + 'Function name:' + functionName + '\n\t' + 'Extra Args: ' + `extraArgs` + '\n\t' + 'Persistent: ' + `persistent` + '\n') # If this is a class method, get its actual function if (type(function) == types.MethodType): str = (str + '\t' + 'Method: ' + `function` + '\n\t' + 'Function: ' + `function.im_func` + '\n') else: str = (str + '\t' + 'Function: ' + `function` + '\n') str = str + '='*50 + '\n' return str