enthought/traitsui

Using InstanceEditor to select from Instances, combobox options not updated correctly when Instance options change

Closed this issue · 1 comments

MWE:

from traits.api import Button, HasTraits, Instance, List, observe, Str
from traitsui.api import (
    InstanceEditor,
    Item,
    OKCancelButtons,
    UCustom,
    UItem,
    View,
)


class Account(HasTraits):
    name = Str()

    traits_view = View(
        Item("name"),
        buttons=OKCancelButtons
    )


class AccountManager(HasTraits):

    new_account = Button()

    accounts = List(Instance(Account))

    #: Selected project
    selected_account = Instance(Account)

    def _accounts_default(self):
        return [Account(name='A')]

    @observe('new_account')
    def _add_new_account(self, event):
        new = Account()
        new.configure_traits()
        self.accounts.append(new)

    def _selected_account_default(self):
        return self.accounts[0]

    traits_view = View(
        UCustom(
            'selected_account',
            editor=InstanceEditor(
                name='accounts',
            )
        ),
        UItem('new_account'),
    )

if __name__=='__main__':
    AccountManager().configure_traits()

yields:

show_bug.mov

If you replace

new = Account()
new.configure_traits()

in _add_new_account with new = Account(name='B'), things work as expected:

show_bug2.mov

Ah I think I see the problem. I added the following print statements:

@observe('new_account')
def _add_new_account(self, event):
    print('a')
    new = Account()
    print('b')
    new.configure_traits()
    print('c')
    self.accounts.append(new)
    print('d')

and in traitsui.qt4.instance_editor.CustomEditor.rebuild_items I print "rebuild_items" at the start of the method call.
When clicking "new account" I immediately see:

a
b
c
rebuild_items
d

before interacting with the configure_traits created ui for the new Account at all.

So, for example a "fix" is to simply replace the configure_traits call with new.configure_traits(kind='livemodal') and then rebuild_items will get called after the account name is set, like we want it to.

There is still a problem tough, in that if the name trait for an account is modified, that does not get reflected by the text displayed in the combobox.

This is because the current observe statement is:

self._object.observe(
    self.rebuild_items, self._name + ".items", dispatch="ui"
)

ie in this case we only rebuild the items when the acccounts list changes. However we don't update when the name of an item changes.

EDIT: I have an attempt at a fix for this in #1725