Browse Source

don't use DirectObjects as dict keys

Darren Ranalli 17 years ago
parent
commit
fcdb44e081
1 changed files with 69 additions and 24 deletions
  1. 69 24
      direct/src/showbase/Messenger.py

+ 69 - 24
direct/src/showbase/Messenger.py

@@ -31,6 +31,8 @@ class Messenger:
         """
         self.__callbacks = {}
         self.__objectEvents = {}
+        self._messengerIdGen = 0
+        self._id2object = {}
 
         if __debug__:
             self.__isWatching=0
@@ -41,6 +43,35 @@ class Messenger:
         # under __debug__.
         self.quieting={"NewFrame":1, "avatarMoving":1} # see def quiet()
 
+    def _getMessengerId(self, object):
+        # TODO: allocate this id in DirectObject.__init__ and get derived
+        # classes to call down (speed optimization, assuming objects
+        # accept/ignore more than once over their lifetime)
+        # get unique messenger id for this object
+        if not hasattr(object, '_messengerId'):
+            object._messengerId = self._messengerIdGen
+            self._messengerIdGen += 1
+        return object._messengerId
+
+    def _storeObject(self, object):
+        # store reference-counted reference to object in case we need to
+        # retrieve it later
+        id = self._getMessengerId(object)
+        if id not in self._id2object:
+            self._id2object[id] = [1, object]
+        else:
+            self._id2object[id][0] += 1
+
+    def _getObject(self, id):
+        return self._id2object[id][1]
+
+    def _releaseObject(self, object):
+        id = self._getMessengerId(object)
+        if id in self._id2object:
+            record = self._id2object[id]
+            record[0] -= 1
+            if record[0] <= 0:
+                del self._id2object[id]
 
     def accept(self, event, object, method, extraArgs=[], persistent=1):
         """ accept(self, string, DirectObject, Function, List, Boolean)
@@ -65,11 +96,13 @@ class Messenger:
 
         acceptorDict = self.__callbacks.setdefault(event, {})
 
+        id = self._getMessengerId(object)
+
         # 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 acceptorDict.has_key(id):
+                oldMethod = acceptorDict[id][0]
                 if oldMethod == method:
                     self.notify.warning(
                         "object: %s was already accepting: \"%s\" with same callback: %s()" %
@@ -79,12 +112,12 @@ class Messenger:
                         "object: %s accept: \"%s\" new callback: %s() supplanting old callback: %s()" %
                         (object.__class__.__name__, event, method.__name__, oldMethod.__name__))
 
-        acceptorDict[object] = [method, extraArgs, persistent]
+        acceptorDict[id] = [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 = self.__objectEvents.setdefault(id, {})
         eventDict.setdefault(event, None)
+        self._storeObject(object)
 
     def ignore(self, event, object):
         """ ignore(self, string, DirectObject)
@@ -94,22 +127,26 @@ class Messenger:
         if Messenger.notify.getDebug():
             Messenger.notify.debug(`object` + '\n now ignoring: ' + `event`)
 
+        id = self._getMessengerId(object)
+
         # 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 acceptorDict and acceptorDict.has_key(id):
+            del acceptorDict[id]
             # 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)
+        eventDict = self.__objectEvents.get(id)
         if eventDict and eventDict.has_key(event):
             del eventDict[event]
             if (len(eventDict) == 0):
-                del self.__objectEvents[object]
+                del self.__objectEvents[id]
+
+        self._releaseObject(object)
 
     def ignoreAll(self, object):
         """
@@ -118,27 +155,32 @@ class Messenger:
         """
         if Messenger.notify.getDebug():
             Messenger.notify.debug(`object` + '\n now ignoring all events')
+        id = self._getMessengerId(object)
         # Get the list of events this object is listening to
-        eventDict = self.__objectEvents.get(object)
+        eventDict = self.__objectEvents.get(id)
         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 acceptorDict and acceptorDict.has_key(id):
+                    del acceptorDict[id]
                     # 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]
+            del self.__objectEvents[id]
+        if id in self._id2object:
+            del self._id2object[id]
 
     def getAllAccepting(self, object):
         """
         Returns the list of all events accepted by the indicated object.
         """
+        id = self._getMessengerId(object)
+
         # Get the list of events this object is listening to
-        eventDict = self.__objectEvents.get(object)
+        eventDict = self.__objectEvents.get(id)
         if eventDict:
             return eventDict.keys()
         return []
@@ -148,7 +190,8 @@ class Messenger:
         Is this object accepting this event?
         """
         acceptorDict = self.__callbacks.get(event)
-        if acceptorDict and acceptorDict.has_key(object):
+        id = self._getMessengerId(object)
+        if acceptorDict and acceptorDict.has_key(id):
             # Found it, return true
             return 1
         # If we looked in both dictionaries and made it here
@@ -191,7 +234,7 @@ class Messenger:
                 if foundWatch:
                     print "Messenger: \"%s\" was sent, but no function in Python listened."%(event,)
             return
-        for object in acceptorDict.keys():
+        for id 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.
@@ -200,20 +243,20 @@ class Messenger:
             # 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)
+            callInfo = acceptorDict.get(id)
             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)
+                    eventDict = self.__objectEvents.get(id)
                     if eventDict and eventDict.has_key(event):
                         del eventDict[event]
                         if (len(eventDict) == 0):
-                            del self.__objectEvents[object]
+                            del self.__objectEvents[id]
 
-                    del acceptorDict[object]
+                    del acceptorDict[id]
                     # If the dictionary at this event is now empty, remove
                     # the event entry from the Messenger altogether
                     if (self.__callbacks.has_key(event) \
@@ -395,7 +438,7 @@ class Messenger:
         """
         str = event.ljust(32) + '\t'
         acceptorDict = self.__callbacks[event]
-        for object, (method, extraArgs, persistent) in acceptorDict.items():
+        for key, (method, extraArgs, persistent) in acceptorDict.items():
             str = str + self.__methodRepr(method) + ' '
         str = str + '\n'
         return str
@@ -411,7 +454,8 @@ class Messenger:
             str += self.__eventRepr(event)
         # Print out the object: event dictionary too
         str += "="*64 + "\n"
-        for object, eventDict in self.__objectEvents.items():
+        for key, eventDict in self.__objectEvents.items():
+            object = self._getObject(key)
             str += "%s:\n" % repr(object)
             for event in eventDict.keys():
                 str += "     %s\n" % repr(event)
@@ -431,8 +475,9 @@ class Messenger:
         for event in keys:
             acceptorDict = self.__callbacks[event]
             str = str + 'Event: ' + event + '\n'
-            for object in acceptorDict.keys():
-                function, extraArgs, persistent = acceptorDict[object]
+            for key in acceptorDict.keys():
+                function, extraArgs, persistent = acceptorDict[key]
+                object = self._getObject(key)
                 if (type(object) == types.InstanceType):
                     className = object.__class__.__name__
                 else: