6

我正在完成PyCon 2010 的演示文稿。大约在 2:30:45,演示者开始介绍trait 事件通知,它允许(除其他外)在 a发生变化时自动调用子例程。traits trait

我正在运行他给出的示例的修改副本......在这个试验中,我试图看看我是否可以在对volume或进行更改时触发静态事件inputs

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.0spinal_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 下运行了它(全部使用traitseasy_install直接关闭Enthought 网站的4.0.0 版本)。无论我到目前为止尝试了什么,结果都是一样的。

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

4

2 回答 2

5

在浏览了他们的单元测试之后,我Dict在 enthought 的事件单元测试覆盖率中发现了一个对特征的测试......看起来当你有一个像 a 这样的容器Dict或者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装饰器(用于动态traits事件通知)如果你用traits.api.Listor调用它需要类似的命名法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

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

[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 ~]$
于 2011-12-04T07:54:21.813 回答
1

由于这最近也让我感到困惑,我刚刚用 Traits 4.2.1 验证了 Mike Pennington 的回答。对 List 特征本身的更改(例如为其分配一个新列表)和对 List成员资格的更改(例如附加或按索引设置)之间似乎确实存在区别。前者使用与特征相同的名称(例如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>

此处的文档中暗示了此行为。

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

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可用于区分容器本身发生变化,还是容器内的单个项目发生变化。

于 2013-10-30T03:45:11.963 回答