我正在处理traits
presentation from PyCon 2010。大约凌晨2:30:45,演示者开始覆盖trait event notifications,这允许(除其他外)在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.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
是否应该能够触发静态事件?如果是这样,我做错了吗?
答案 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
事件通知)如果您使用traits
或traits.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
参数可用于区分容器本身的更改,或容器内的单个项目更改。