open_toontown_panda3d/direct/src/showbase/Messenger.py

457 lines
17 KiB
Python

"""Undocumented Module"""
__all__ = ['Messenger']
from PythonUtil import *
from direct.directnotify import DirectNotifyGlobal
import types
class Messenger:
notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
def __init__(self):
"""
One is keyed off the event name. It has the following structure:
{event1: {object1: [method, extraArgs, persistent],
object2: [method, extraArgs, persistent]},
event2: {object1: [method, extraArgs, persistent],
object2: [method, extraArgs, persistent]}}
This dictionary allow for efficient callbacks when the messenger
hears an event.
A second dictionary remembers which objects are accepting which
events. This allows for efficient ignoreAll commands.
Or, for an example with more real data:
{'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
"""
self.__callbacks = {}
self.__objectEvents = {}
if __debug__:
self.__isWatching=0
self.__watching={}
# I'd like this to be in the __debug__, but I fear that someone will
# want this in a release build. If you're sure that that will not be
# then please remove this comment and put the quiet/verbose stuff
# under __debug__.
self.quieting={"NewFrame":1, "avatarMoving":1} # see def quiet()
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.
"""
notifyDebug = Messenger.notify.getDebug()
if notifyDebug:
Messenger.notify.debug(
"object: %s\n accepting: %s\n method: %s\n extraArgs: %s\n persistent: %s" %
(object, event, method, extraArgs, persistent))
# Make the the method is callable
assert callable(method), (
"method not callable in accept (ignoring): %s %s"%
(method, extraArgs))
acceptorDict = self.__callbacks.setdefault(event, {})
# Make sure we are not inadvertently overwriting an existing event
# on this particular object.
if notifyDebug:
if acceptorDict.has_key(object):
oldMethod = acceptorDict[object][0]
if oldMethod == method:
self.notify.warning(
"object: %s was already accepting: \"%s\" with same callback: %s()" %
(object.__class__.__name__, event, method.__name__))
else:
self.notify.warning(
"object: %s accept: \"%s\" new callback: %s() supplanting old callback: %s()" %
(object.__class__.__name__, event, method.__name__, oldMethod.__name__))
acceptorDict[object] = [method, extraArgs, persistent]
# Remember that this object is listening for this event
# TODO: can we really guarantee that all DirectObjects are valid dict keys?
eventDict = self.__objectEvents.setdefault(object, {})
eventDict.setdefault(event, None)
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.__callbacks.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.__callbacks[event]
# This object is no longer listening for this event
eventDict = self.__objectEvents.get(object)
if eventDict and eventDict.has_key(event):
del eventDict[event]
if (len(eventDict) == 0):
del self.__objectEvents[object]
def ignoreAll(self, object):
"""
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')
# Get the list of events this object is listening to
eventDict = self.__objectEvents.get(object)
if eventDict:
for event in eventDict.keys():
# Find the dictionary of all the objects accepting this event
acceptorDict = self.__callbacks.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.__callbacks[event]
del self.__objectEvents[object]
def getAllAccepting(self, object):
"""
Returns the list of all events accepted by the indicated object.
"""
# Get the list of events this object is listening to
eventDict = self.__objectEvents.get(object)
if eventDict:
return eventDict.keys()
return []
def isAccepting(self, event, object):
""" isAccepting(self, string, DirectOject)
Is this object accepting this event?
"""
acceptorDict = self.__callbacks.get(event)
if acceptorDict and acceptorDict.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):
"""
Return objects accepting the given event
"""
return self.__callbacks.get(event)
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=[]):
"""
event is usually a string.
sentArgs is a list of any data that you want passed along to the
handlers listening to this event.
Send this event, optionally passing in arguments
"""
if Messenger.notify.getDebug() and not self.quieting.get(event):
assert Messenger.notify.debug(
'sent event: ' + event + ' sentArgs: ' + `sentArgs`)
if __debug__:
foundWatch=0
if self.__isWatching:
for i in self.__watching.keys():
if str(event).find(i) >= 0:
foundWatch=1
break
acceptorDict = self.__callbacks.get(event)
if not acceptorDict:
if __debug__:
if foundWatch:
print "Messenger: \"%s\" was sent, but no function in Python listened."%(event,)
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.
#
# NOTE: there is no danger of skipping over objects due to
# modifications to acceptorDict, since the for..in above
# iterates over a list of objects that is created once at
# the start
callInfo = acceptorDict.get(object)
if callInfo:
method, extraArgs, persistent = callInfo
# If this object was only accepting this event once,
# remove it from the dictionary
if not persistent:
# This object is no longer listening for this event
eventDict = self.__objectEvents.get(object)
if eventDict and eventDict.has_key(event):
del eventDict[event]
if (len(eventDict) == 0):
del self.__objectEvents[object]
del acceptorDict[object]
# If the dictionary at this event is now empty, remove
# the event entry from the Messenger altogether
if (self.__callbacks.has_key(event) \
and (len(self.__callbacks[event]) == 0)):
del self.__callbacks[event]
if __debug__:
if foundWatch:
print "Messenger: \"%s\" --> %s%s"%(
event,
self.__methodRepr(method),
tuple(extraArgs + sentArgs))
#print "Messenger: \"%s\" --> %s%s"%(
# event,
# self.__methodRepr(method),
# tuple(extraArgs + sentArgs))
# It is important to make the actual call here, after
# we have cleaned up the accept hook, because the
# method itself might call accept() or acceptOnce()
# again.
assert callable(method)
method (*(extraArgs + sentArgs))
def clear(self):
"""
Start fresh with a clear dict
"""
self.__callbacks.clear()
self.__objectEvents.clear()
def isEmpty(self):
return (len(self.__callbacks) == 0)
def getEvents(self):
return self.__callbacks.keys()
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.__callbacks.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
# didn't find that method, return false
return retFlag
def toggleVerbose(self):
isVerbose = 1 - Messenger.notify.getDebug()
Messenger.notify.setDebug(isVerbose)
if isVerbose:
print "Verbose mode true. quiet list = %s"%(
self.quieting.keys(),)
if __debug__:
def watch(self, needle):
"""
return a matching event (needle) if found (in haystack).
This is primarily a debugging tool.
This is intended for debugging use only.
This function is not defined if python is ran with -O (optimize).
See Also: unwatch
"""
if not self.__watching.get(needle):
self.__isWatching += 1
self.__watching[needle]=1
def unwatch(self, needle):
"""
return a matching event (needle) if found (in haystack).
This is primarily a debugging tool.
This is intended for debugging use only.
This function is not defined if python is ran with -O (optimize).
See Also: watch
"""
if self.__watching.get(needle):
self.__isWatching -= 1
del self.__watching[needle]
def quiet(self, message):
"""
When verbose mode is on, don't spam the output with messages
marked as quiet.
This is primarily a debugging tool.
This is intended for debugging use only.
This function is not defined if python is ran with -O (optimize).
See Also: unquiet
"""
if not self.quieting.get(message):
self.quieting[message]=1
def unquiet(self, message):
"""
Remove a message from the list of messages that are not reported
in verbose mode.
This is primarily a debugging tool.
This is intended for debugging use only.
This function is not defined if python is ran with -O (optimize).
See Also: quiet
"""
if self.quieting.get(message):
del self.quieting[message]
def find(self, needle):
"""
return a matching event (needle) if found (in haystack).
This is primarily a debugging tool.
"""
keys = self.__callbacks.keys()
keys.sort()
for event in keys:
if `event`.find(needle) >= 0:
print self.__eventRepr(event),
return {event: self.__callbacks[event]}
def findAll(self, needle, limit=None):
"""
return a dict of events (needle) if found (in haystack).
limit may be None or an integer (e.g. 1).
This is primarily a debugging tool.
"""
matches = {}
keys = self.__callbacks.keys()
keys.sort()
for event in keys:
if `event`.find(needle) >= 0:
print self.__eventRepr(event),
matches[event] = self.__callbacks[event]
# if the limit is not None, decrement and
# check for break:
if limit > 0:
limit -= 1
if limit == 0:
break
return matches
def __methodRepr(self, method):
"""
return string version of class.method or method.
"""
if (type(method) == types.MethodType):
functionName = method.im_class.__name__ + '.' + \
method.im_func.__name__
else:
functionName = method.__name__
return functionName
def __eventRepr(self, event):
"""
Compact version of event, acceptor pairs
"""
str = event.ljust(32) + '\t'
acceptorDict = self.__callbacks[event]
for object, (method, extraArgs, persistent) in acceptorDict.items():
str = str + self.__methodRepr(method) + ' '
str = str + '\n'
return str
def __repr__(self):
"""
Compact version of event, acceptor pairs
"""
str = "The messenger is currently handling:\n" + "="*64 + "\n"
keys = self.__callbacks.keys()
keys.sort()
for event in keys:
str += self.__eventRepr(event)
# Print out the object: event dictionary too
str += "="*64 + "\n"
for object, eventDict in self.__objectEvents.items():
str += "%s:\n" % repr(object)
for event in eventDict.keys():
str += " %s\n" % repr(event)
str += "="*64 + "\n" + "End of messenger info.\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.__callbacks.keys()
keys.sort()
for event in keys:
acceptorDict = self.__callbacks[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