How can I trigger a static notification of events in a list?

I am working on the presentation of traits from PyCon 2010 . At about 2:30:45, the host begins to cover notifications of feature events that allow (among other things) the ability to automatically call the subroutine at any time a trait has changed.

I run a modified copy of the example that he gave ... In this test, I try to check if I can fire a static event whenever I make changes to volume or inputs .

 from traits.api import HasTraits, Range, List, Float import traits class Amplifier(HasTraits): """ Define an Amplifier (a la Spinal Tap) with Enthought traits. Use traits to enforce values boundaries on the Amplifier objects. Use events to notify via the console when the volume trait is changed and when new volume traits are added to inputs. """ volume = Range(value=5.0, trait=Float, low=0.0, high=11.0) inputs = List(volume) # I want to fire a static trait event notification # when another volume element is added def __init__(self, volume=5.0): super(Amplifier, self).__init__() self.volume = volume self.inputs.append(volume) def _volume_changed(self, old, new): # static event listener for self.volume if not (new in self.inputs): self.inputs.append(self.volume) if new == 11.0: print "This one goes to eleven... so far, we have seen", self.inputs def _inputs_changed(self, old, new): # static event listener for self.inputs print "Check it out!!" if __name__=='__main__': spinal_tap = Amplifier() spinal_tap.volume = 11.0 print "DIRECTLY adding a new volume input..." spinal_tap.inputs.append(4.0) try: print "NEGATIVE Test... adding 12.0" spinal_tap.inputs.append(12.0) except traits.trait_errors.TraitError: print "Test passed" 

When I run this script, I can see This one goes to eleven... so far, we have seen [5.0, 11.0] on the console output, so I know that _volume_changed() triggered when I assign 11.0 to spinal_tap.volume .

However, I never see any events from _inputs_changed() . No matter what example I am preparing, I cannot get a List to fire the event.

This is the result that I see ... I note that there is no evidence that _inputs_changed() ever works.

 [ mpenning@Bucksnort ~]$ python spinaltap.py This one goes to eleven... so far, we have seen [5.0, 11.0] DIRECTLY adding a new volume input... NEGATIVE Test... adding 12.0 Test passed [ mpenning@Bucksnort ~]$ 

I ran this in both Python2.6 / Cygwin / Windows 7 and Python 2.5 / Linux (all using traits version 4.0.0, which I easy_install directly from the Enthought site ). The results are the same no matter what I have tried so far.

If List can fire a static event when using attributes? If so, am I doing something wrong?

+6
source share
2 answers

After looking at my unit tests, I found a test for the Dict attributes in the enthought event unittest coverage ... it looks like when you have a container like Dict or List that you need to configure for the method for listening to the magic event as follows:

 ## Broken method definition: def _inputs_changed(self, old, new): # container event static listeners must be in the form of _foo_items_changed() def _inputs_items_changed(self, old, new): # static event listener for self.inputs if len(new.added) > 0: print "Check it out, we added %s to self.items" % new.added elif len(new.removed) > 0: print "Check it out, we removed %s from self.items" % new.removed 

Similarly, I also found that the on_trait_change decorator (used to dynamically notify the traits event) requires a similar nomenclature if you call it using traits.api.List or traits.api.Dict ... so I can also write the code above:

 from traits.api import on_trait_change # ... @on_trait_change('inputs_items') def something_changed(self, name, new): # static event listener for self.inputs if len(new.added) > 0: print "Check it out, we added %s to self.items" % new.added elif len(new.removed) > 0: print "Check it out, we removed %s from self.items" % new.removed 

In any case, when I run the code, I get the expected result:

 [ mpenning@Bucksnort ~]$ python spinaltap.py Check it out, we added [5.0] to self.items Check it out, we added [11.0] to self.items This one goes to eleven... so far, we have seen [5.0, 11.0] DIRECTLY adding a new volume input... Check it out, we added [4.0] to self.items NEGATIVE Test... adding 12.0 Test passed [ mpenning@Bucksnort ~]$ 
+5
source

As it recently caught me, I just confirmed Mike Pennington's answer to Traits 4.2.1. It seems that there is a difference between changes to the list attribute itself (for example, assigning it a new list) and changing the list membership (for example, adding or setting by index). The former uses the same name as the attribute (for example, inputs ), while the latter uses the suffix "_items". This example demonstrates this:

 from traits.api import Float, HasTraits, Instance, List class Part(HasTraits): costs = List(Float) # called when the actual List trait changes: def _costs_changed(self, old, new): print("Part::_costs_changed %s -> %s" % (str(old), str(new))) # called when the contents of the List trait changes: def _costs_items_changed(self, old, new): print("Part::_costs_changed %s -> %s" % (str(old), str(new))) class Widget(HasTraits): part = Instance(Part) def __init__(self): self.part = Part() self.part.on_trait_change(self.update_costs, 'costs') self.part.on_trait_change(self.update_costs_items, 'costs_items') def update_costs(self, name, new): print("update_costs: %s = %s" % (name, str(new),)) def update_costs_items(self, name, new): print("update_costs_items: %s = %s" % (name, str(new),)) w = Widget() w.part.costs = [ 1.0, 2.0, 3.0 ] # Part::_costs_changed [] -> [1.0, 2.0, 3.0] # update_costs: costs = [1.0, 2.0, 3.0] w.part.costs = [ 1.0, 2.0, 3.1 ] # Part::_costs_changed [1.0, 2.0, 3.0] -> [1.0, 2.0, 3.1] # update_costs: costs = [1.0, 2.0, 3.1] w.part.costs[0] = 5.0 # Part::_costs_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007bd810> # update_costs_items: costs_items = <traits.trait_handlers.TraitListEvent object at 0x1007bd810> w.part.costs.append(4.0) # Part::_costs_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007bd810> # update_costs_items: costs_items = <traits.trait_handlers.TraitListEvent object at 0x1007bd810> 

This behavior is outlined in the documentation here .

However, if used

In this case, the name parameter of the update_costs handler can be used to differentiate between changing the container itself or replacing one element in the container.

+1
source

Source: https://habr.com/ru/post/903026/


All Articles