propertiesenthoughttraitsui

How do I make an edit_traits() GUI item responsive to changes in its dependencies?


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:

Initial GUI

And if I change the value of Zap:

After changing Zap

Note the following:

  1. After changing Zap, the address of Bar has changed.

    This means that changes to Bar are being dynamically updated in the GUI, while it's still opened; that's great! However...

  2. The way Bar is displayed in the GUI is not very useful.

    I'd love to have Bar displayed as Baz is displayed: selectable by the user.

What I'd like is to have the best of both worlds:

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.)


Solution

  • 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:

    enter image description here

    And after changing to Zap to '1':

    enter image description here