Question

I have some external objects listening/handling another object's traits. How can I get a list of the listeners/handlers to that objects traits? I have multiple objects listening to another's traits and I'd like to be able to query somehow and determine which ones are still connected.

Thanks!

Here's an example using the Enthought Traits module:

 from traits.api import  HasTraits,Str,Int,Float

 class GenerateEvents ( HasTraits ):
     name   = Str
     age    = Int
     weight = Float

 class ListenEvents ( HasTraits ):
     def _name_changed ( self, object, name, old, new ):
         print "_name_changed:", object, name, old, new

     def _age_changed ( self, object, name, old, new ):
         print "_age_changed:", object, name, old, new

     def _weight_changed ( self, object, name, old, new ):
         print "_weight_changed:", object, name, old, new

 class AnotherListenEvents ( HasTraits ):
     def _name_changed ( self, object, name, old, new ):
         print "Another _name_changed:", object, name, old, new

     def _age_changed ( self, object, name, old, new ):
         print "another _age_changed:", object, name, old, new

     def _weight_changed ( self, object, name, old, new ):
         print "another _weight_changed:", object, name, old, new

 ge = GenerateEvents()
 le = ListenEvents()
 ale = AnotherListenEvents()
 ge.set( name = 'Joe', age = 22, weight = 152.0 )
 ge.add_trait_listener( le )
 ge.add_trait_listener( ale )
 ge.set( name = 'Mike', age = 34, weight = 178.0 )

Note that ge has two listeners, le and ale. However, given ge how could I find out what the listeners are? Note that listeners can added/removed dynamically in the code so they are not fixed.

I hope that clarifies a bit.

Was it helpful?

Solution

Take a look at Robert Kern's answer in this thread from the enthought-dev mailing list: http://enthought-dev.117412.n3.nabble.com/How-do-I-find-the-listeners-for-a-trait-td1716192.html

Here's a modified version of your code, with a couple functions added for retrieving and displaying the listeners. I added a static listener _age_changed, a listener created with the on_trait_change decorator, and a listener created with ge.on_trait_change(...).

from traits.api import (HasTraits, Str, Int, Float, on_trait_change,
                        TraitChangeNotifyWrapper)
from traits.trait_notifiers import StaticTraitChangeNotifyWrapper


def get_listeners(h):
    """
    h must be a HasTraits instance.

    Returns a dictionary whose keys are trait names and whose values
    are lists of notifiers.
    """
    listeners = {}
    for name in h.traits():
        notifiers = h.trait(name)._notifiers(0)
        if notifiers is not None:
            # Filter out the static listeners.  Comment this out
            # if you want to keep those.
            notifiers = [notifier for notifier in notifiers
                            if not isinstance(notifier, StaticTraitChangeNotifyWrapper)]
            listeners[name] = notifiers
    return listeners


def print_listeners(listeners):
    """
    Print the dictionary of listeners returned by `get_listeners(h)`.
    """
    for name, notifiers in listeners.items():
        print "trait '%s' has the following listeners:" % (name,)
        for notifier in notifiers:
            if notifier.name is None:
                handler = notifier.handler
                print "    '%s' %r" % (handler.__name__, type(handler))
            else:
                print "    '%s' on object %s" % (notifier.name, notifier.object)


class GenerateEvents ( HasTraits ):
    name   = Str
    age    = Int
    weight = Float

    def _age_changed(self, old):
        print "age changed from ", old, "to", self.age

    @on_trait_change('weight')
    def do_something(self, obj, name, old, new):
        print "do_something: name =", name


class ListenEvents ( HasTraits ):
    def _name_changed ( self, object, name, old, new ):
        print "_name_changed:", object, name, old, new

    def _age_changed ( self, object, name, old, new ):
        print "_age_changed:", object, name, old, new

    def _weight_changed ( self, object, name, old, new ):
        print "_weight_changed:", object, name, old, new


class AnotherListenEvents ( HasTraits ):
    def _name_changed ( self, object, name, old, new ):
        print "Another _name_changed:", object, name, old, new

    def _age_changed ( self, object, name, old, new ):
        print "another _age_changed:", object, name, old, new

    def _weight_changed ( self, object, name, old, new ):
        print "another _weight_changed:", object, name, old, new


def printit(foo):
    print foo


ge = GenerateEvents()
le = ListenEvents()
ale = AnotherListenEvents()
ge.set( name = 'Joe', age = 22, weight = 152.0 )
ge.add_trait_listener( le )
ge.add_trait_listener( ale )
ge.set( name = 'Mike', age = 34, weight = 178.0 )

# Make the function `printit` a listener to ge.name.
ge.on_trait_change(printit, name='name')

Here's what you get when you run it (in ipython):

In [103]: run trait_listeners_question
age changed from  22 to 22
do_something: name = weight
age changed from  34 to 34
_age_changed: <__main__.GenerateEvents object at 0x2680950> age 22 34
another _age_changed: <__main__.GenerateEvents object at 0x2680950> age 22 34
_name_changed: <__main__.GenerateEvents object at 0x2680950> name Joe Mike
Another _name_changed: <__main__.GenerateEvents object at 0x2680950> name Joe Mike
do_something: name = weight
_weight_changed: <__main__.GenerateEvents object at 0x2680950> weight 152.0 178.0
another _weight_changed: <__main__.GenerateEvents object at 0x2680950> weight 152.0 178.0

In [104]: listeners = get_listeners(ge)

In [105]: print_listeners(listeners)
trait 'trait_added' has the following listeners:
    '_trait_added_changed' on object <weakref at 0x2656d08; to 'ListenEvents' at 0x2680d70>
    '_trait_added_changed' on object <weakref at 0x2656e68; to 'AnotherListenEvents' at 0x26808f0>
trait 'age' has the following listeners:
    '_age_changed' on object <weakref at 0x2656c58; to 'ListenEvents' at 0x2680d70>
    '_age_changed' on object <weakref at 0x2656db8; to 'AnotherListenEvents' at 0x26808f0>
trait 'ev' has the following listeners:
    '_ev_changed' on object <weakref at 0x2656c00; to 'ListenEvents' at 0x2680d70>
trait 'name' has the following listeners:
    '_name_changed' on object <weakref at 0x2656cb0; to 'ListenEvents' at 0x2680d70>
    '_name_changed' on object <weakref at 0x2656e10; to 'AnotherListenEvents' at 0x26808f0>
    'printit' <type 'function'>
trait 'weight' has the following listeners:
    'do_something' on object <weakref at 0x2671368; to 'GenerateEvents' at 0x2680950>
    '_weight_changed' on object <weakref at 0x2656ba8; to 'ListenEvents' at 0x2680d70>
    '_weight_changed' on object <weakref at 0x2656d60; to 'AnotherListenEvents' at 0x26808f0>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top