问题
I'm designing a HasTraits
subclass with dependent properties:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Trait, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
@cached_property
def _get_bar(self):
return Trait(list(self._dicts)[self.zap], self._dicts)
traits_view = View(
Item("zap"),
Item("bar"),
Item("baz"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
When I run this code I see:
And if I change the value of Zap
:
Note the following:
After changing
Zap
, the address ofBar
has changed.This means that changes to
Bar
are being dynamically updated in the GUI, while it's still opened; that's great! However...The way
Bar
is displayed in the GUI is not very useful.I'd love to have
Bar
displayed asBaz
is displayed: selectable by the user.
What I'd like is to have the best of both worlds:
- the dynamic GUI updating I see with
Bar
, and - the display format of
Baz
.
Does anyone know how I can get this?
I've tried several ways of updating a Baz
-like item dynamically, to no avail.
(See this previous SO question.)
回答1:
The following code gets me the behavior I want:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property, Enum, List
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dict = {
"zero": 0,
"one": 1,
"two": 2,
}
_zaps = [
["zero", "one"],
["one", "two"],
["zero", "two"],
]
zaps = List(_zaps[0])
zap = Enum([0,1,2]) # Selection of `zap` should limit the items of `_dict` available for selection.
bar = Enum(_zaps[0][0], values="zaps")
bar_ = Int(_dict[_zaps[0][0]])
def _zap_changed(self, new_value):
self.zaps = self._zaps[new_value]
self.bar_ = self._dict[self.bar]
def _bar_changed(self, new_value):
self.bar_ = self._dict[self.bar]
traits_view = View(
Item("zap"),
Item("bar"),
Item("bar_", style="readonly"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
Immediately after program start-up:
And after changing to Zap
to '1':
回答2:
I assume you wish both bar
and baz
to be dict
type (in traits Dict
). Actually, there are default display widgets for pre-defined trait types, which are more useful than showing address. I believe traitsui
doesn't know how to properly display your custom Trait object unless you explicitly assign an editor for it. Note that for baz, although a dropdown menu is generated, it is only displaying the keys, which is not very useful either.
With that said, the following codes might meet your expectations.
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Dict, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
@cached_property
def _get_bar(self):
return self._dicts[self.zap]
traits_view = View(
Item("zap"),
Item("bar", style="custom"),
Item("baz"),
width=500,
)
来源:https://stackoverflow.com/questions/59010527/how-do-i-make-an-edit-traits-gui-item-responsive-to-changes-in-its-dependencie