0

I am having some issues building a GUI with traits and traitsui. What I am trying to do can be simplified to the following:

from traits.api import List, Str, on_trait_change, Trait, HasTraits
from traitsui.api import View, Item, EnumEditor

a_names = ['aaa', 'bbb', 'ccc']
b_names = ['ddd', 'eee', 'fff']

class SomeClass( HasTraits ):
    x = Str('aaa')
    y = Str('bbb')
    names = List( a_names, comparison_mode=2 )
    select_names = Trait('list A', {'list A': a_names, 'list B': b_names},
        label='Select list names')

    traits_view = View(Item('select_names'),
                       Item('x', editor=EnumEditor( name='names' )),
                       Item('y', editor=EnumEditor( name='names' )))

    def __init( self ):
        pass

    @on_trait_change( 'x' )
    def _x_changed( self ):
        print('x changed' )
        print( self.names.index( self.x ) )
        print( self.names.index( self.y ) )

    @on_trait_change( 'y' )
    def _y_changed( self ):
        print('y changed' )
        print( self.names.index( self.x ) )
        print( self.names.index( self.y ) )

    @on_trait_change( 'select_names', post_init=True )
    def _select_names_changed( self ):
        if self.select_names == 'list A':
            self.x, self.y = 'aaa', 'bbb'
            self.names = self.select_names_
        elif self.select_names == 'list B':
            self.x, self.y = 'ddd', 'eee'
            self.names = self.select_names_

if __name__ == '__main__':
    sc = SomeClass()
    sc.configure_traits()

The idea is that I have a trait (names) representing a list, which can 'point' to two different lists of strings, depending on the value of select_names. I also have two traits x and y which are two elements of the list names. When select_names is changed, then the values of names, x and y are automatically changed. However, since the changes of traits x and y are automatically monitored, the function _x_changed and _y_changed are called before the end of _select_names_changed call, resulting in not finding x or y in names.

I tried to used to post_init=True option of on_trait_changed('select_names') to avoid this behaviour, without success.

Do you have any idea of how I should proceed ?

narsonalin
  • 69
  • 5

1 Answers1

0

I finally found a workaround. In my class, I had an boolean attribute self.locked, which value is True, if _x_changed or _y_changed are not to be executed. This gives:

class SomeClass( HasTraits ):
    
    def __init( self ):
        self.locked = False

    @on_trait_change( 'x' )
    def _x_changed( self ):
        if self.locked: return
        print('x changed' )
        print( self.names.index( self.x ) )
        print( self.names.index( self.y ) )

    @on_trait_change( 'y' )
    def _y_changed( self ):
        if self.locked: return
        print('y changed' )
        print( self.names.index( self.x ) )
        print( self.names.index( self.y ) )

    @on_trait_change( 'select_names', post_init=True )
    def _select_names_changed( self ):
        self.locked = True
        if self.select_names == 'list A':
            self.x, self.y = 'aaa', 'bbb'
            self.names = self.select_names_
        elif self.select_names == 'list B':
            self.x, self.y = 'ddd', 'eee'
            self.names = self.select_names_
        self.locked = False

        # If really needed, I can call the "changed" methods in the end.
        self._x_changed()
        self._y_changed()
narsonalin
  • 69
  • 5