如何在列表上触发Traits静态事件通知?

时间:2011-12-03 23:45:31

标签: python events traits enthought

我正在处理traits presentation from PyCon 2010。大约凌晨2:30:45,演示者开始覆盖trait event notifications,这允许(除其他外)在trait发生变化时自动调用子例程的能力。

我正在运行他给出的示例的修改后的副本...在此试用版中,我试图看看每当我对volumeinputs进行更改时是否可以触发静态事件

from traits.api import HasTraits, Range, List, Float
import traits
class Amplifier(HasTraits):
    """
    Define an Amplifier (a la Spinal Tap) with Enthought's traits.  Use traits  
    to enforce values boundaries on the Amplifier's 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"

当我运行此脚本时,我可以在控制台输出中看到This one goes to eleven... so far, we have seen [5.0, 11.0],因此当我将_volume_changed()分配给11.0时,我知道spinal_tap.volume会被触发。

但是,我从未见过来自_inputs_changed()的任何事件。无论我做什么样的例子,我都无法获得List来举办活动。

这是我看到的输出...请注意,没有证据表明_inputs_changed()会被触发。

[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 ~]$

我在Python2.6 / Cygwin / Windows 7和Python 2.5 / Linux下运行了这一切(全部使用traits版本4.0.0,easy_install直接关闭Enthought's site)。无论我到目前为止尝试过的结果都是一样的。

使用特征时,List是否应该能够触发静态事件?如果是这样,我做错了吗?

2 个答案:

答案 0 :(得分:5)

在浏览了他们的单元测试后,我在enthought的event unittest coverage中找到了Dict特征的测试...当你有一个像Dict或{{1}这样的容器时你需要像这样设置魔术事件监听器方法:

List

同样,我还发现## 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 装饰器(用于动态on_trait_change事件通知)如果您使用traitstraits.api.List调用它,则需要使用类似的命名法。 ..所以我也可以把上面的代码写成:

traits.api.Dict

无论哪种方式,当我运行代码时,我得到预期的输出:

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

答案 1 :(得分:1)

由于这最近也引起了我的注意,我刚刚通过Traits 4.2.1验证了Mike Pennington的回答。列表特征本身的更改(例如为其分配新列表)和列表的成员资格的更改(例如通过索引追加或设置)之间似乎存在区别。前者使用与特征相同的名称(例如inputs),而后者使用" _items"后缀。这个例子似乎证明了这一点:

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>

文档here中暗示了此行为。

但是,如果使用extended名称,则当整个列表成员资格发生更改时,似乎可以调用相同的处理程序:

from traits.api import Float, HasTraits, Instance, List

class Part(HasTraits):
    costs = List(Float)

def _costs_changed(self, old, new):
    print("_costs_changed %s -> %s" % (str(old), str(new)))

def _costs_items_changed(self, old, new):
    print("_costs_items_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[]')  # <-- extended name

    def update_costs(self, name, new):
        print("update_costs: %s = %s" % (name, str(new),))

w = Widget()

w.part.costs = [ 1.0, 2.0, 3.0 ]
# _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 ]
# _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
# _costs_items_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007c6f90>
# update_costs: costs_items = [5.0]

w.part.costs.append(4.0)
# _costs_items_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007c6f90>
# update_costs: costs_items = [4.0]

在这种情况下,name处理程序的update_costs参数可用于区分容器本身的更改,或容器内的单个项目更改。