Panda's event handling...

Question, if an event gets sent via messenger.send('event name',[param list]), can this single event instance be received by many different objects that have registered themselves via base.accept('event name',[param list]) ? Or can an event instance only be received once?

I tried to do this (having many objects receive one event instance), but my objects don’t seem to be responding to the events, and when I did print messenger I didn’t see any of my objects or events in the output, just a lot of DirectGUI objects and events. Is this a bad idea?

I was trying to adopt a pattern where objects would send lots of different events, and many other objects might respond to each event, so a general many-to-many communication system. For example, to implement drag and drop, when the user presses down the mouse button you would do send('drag',[draggee]). Each draggable object will have registered itself upon initialisation with base.accept('drag'). So each draggable should receive the drag event and have a chance to respond, each will check if draggee == self and the one that is the draggee will begin dragging itself. Lots of unnecessary checking, but it’s simple and flexible, because other interested objects could subscribe to the drag event also and respond in other ways.

Then when the user releases the mouse button a ‘drop’ event will be sent and things would proceed similarly. Container objects may want to subscribe to the drop event and check to see if the thing was dragged onto them, then they may want to take control of it.

I was thinking of using this pattern lots and lots, to allow many objects to respond to events generated by many objects, without the objects all needing to have direct or indirect references to each other.

I guess if Panda’s messenger does not work like this I can just implement my own, you just need a singleton messenger object that everybody has a reference to, and that maintains a dictionary mapping event names to lists of callable objects, and provides the send and accept methods.

Panda’s messenger system does indeed work like this. Any number of objects can be accepting the same message, and they will all get called. The framework you describe should work. Something else must be wrong–perhaps your objects did not actually call accept() when you thought they did, or something along those lines.

David

Good, I’m glad cause this seems like a really simple yet useful pattern. I’ll try to get to the bottom of it.

Actually it seems to be the case that only one object can receive an event at once, and that each call to base.accept overwrites any previous calls to the same. This code prints out ‘bar’ once and does not print ‘foo’:

import direct.directbase.DirectStart
from pandac.PandaModules import *

def foo():
    print 'foo'
    
def bar():
    print 'bar'

base.accept('foobar',foo)
base.accept('foobar',bar)

messenger.send('foobar')

and also if I do it this way, it only prints bar:

import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject

class FooBar(DirectObject):

    def __init__(self):
    
        self.accept('foobar',self.foo)
        self.accept('foobar',self.bar)

    def foo(self):
        print 'foo'
    
    def bar(self):
        print 'bar'

foobar = FooBar()
messenger.send('foobar')

Hmm, but this one prints bar foo:

import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject

class Foo(DirectObject):

    def __init__(self):
    
        self.accept('foobar',self.foo)

    def foo(self):
        print 'foo'

class Bar(DirectObject):

    def __init__(self):

        self.accept('foobar',self.bar)
    
    def bar(self):
        print 'bar'

f = Foo()
b = Bar()
messenger.send('foobar')

Looking at the source code in messenger.py, it seems to be managing a list of lists called acceptorDict (that’s weird, calling a list dict), and the keys in this list are not the event names as I would expect but the objects that call the accept method. (And the corresponding values in the list are lists [method, extraArgs, persistent]). This is a little surprising since you don’t pass self to the accept method, but it turns out DirectObject does it for you. Hence why the same object can only subscribe to the same event once, even if it tries to subscribe two different functions, but multiple different objects can subscribe to the same event.

That’ll be the problem in my application, I always used base.accept to subscribe to the same event lots of times from lots of different objects, but because it’s the same object every time (base) each call will overwrite the previous. I just need to subclass DirectObject and do self.accept instead. Should have followed the manual in the first place.

There is also something called eventDict in the source that looks important but I don’t get what it does at a glance.

I would have thought that the way to implement this messenger would be to have a global singleton object messenger that manages a dictionary mapping event name strings to lists of function objects, when an event is sent it gets the list of functions for that event and calls them one by one, then user classes just call messenger.accept(…) and messenger.send(…) etc. There must be some reason it’s implemented differently?

Ah yes, sorry–it is true that any particular object can only accept each message once. I think there was some historical reason we did that, probably to help clean up messenger leaks. (Otherwise, what does it mean when an object calls accept(‘foo’) twice and ignore(‘foo’) once?)

Also, having a dictionary on objects allows the object to call self.ignoreAll() and conveniently ignore all objects it is accepting.

Incidentally, acceptorDict is in fact a dictionary, and not a list. It is a dictionary on object -> [method, extraArgs, persistent].

David