MindV0rtex
MindV0rtex

Reputation: 346

Using TraitsUI ListEditor Factory for arbitrary collections

How can I adapt a ListEditor to list the contents of an arbitrary collection using TraitsUI? Here is a sample code

from traits.api import HasStrictTraits, Instance, Int, List, Str
from traitsui.api import View, Item, ListEditor, InstanceEditor

from sortedcontainers import SortedListWithKey

class Person(HasStrictTraits):
    name = Str
    age = Int

class Office(HasStrictTraits):
    # employees = Instance(SortedListWithKey, 
                           kw={'key': lambda employee: employee.age})
    employees = List

employee_view = View(
    Item(name='name', show_label=False, style='readonly')
)

office_view = View(
    Item(name='adults',
         show_label=False,
         style='readonly',
         editor=ListEditor(
             style='custom',
             editor=InstanceEditor(view=employee_view),
         ),
    ),
    resizable=True
)

employee_list = [Person(name='John', age=31), Person(name='Mike', age=31),
                 Person(name='Jill', age=37), Person(name='Eric', age=28)]

#office = Office()
#office.employees.update(employee_list)
office = Office(employees=employee_list)

office.configure_traits(view=office_view)

If I replace the standard list with SortedListWithKey by using the code I commented out, I get the 'AttributeError: 'Office' object has no attribute 'value'' error. How can I resolve this?

Upvotes: 0

Views: 196

Answers (1)

Corran Webster
Corran Webster

Reputation: 46

Traits uses a list subclass (TraitListObject) for anything stored in a List trait: this is what allows trait events to be fired on changes to items in the list as well as to the attribute. I'm guessing that the SortedListWithKey class is from the "Sorted Containers" third-party package and so isn't a Traits list. The ListEditor expects a TraitsListObject (or a work-alike) for it to work properly, since it needs to know if the list items have changed.

Fixes/work-arounds that I can think of:

  1. Using two List traits, one unsorted (which could be a Set) and one sorted, and have trait change handlers to synchronize the two. This sort of pattern works well if your unordered data is part of the "model" layer and the way it is sorted is part of the user-facing "view" or "presentation" layer (ie. possibly in a TraitsUI Controller or ModelView object).

  2. Write a subclass of TraitListObject that has the self-sorting behaviour of SortedListWithKey. Use a regular List trait but assign instances of your subclass into it, or for really slick behaviour subclass List to do conversion to your new subclass on any set operation.

  3. Use a regular List trait, but a TableEditor with columns for the name and age: this is a different UI from what you are intending, and may not suit what your real-world is, but the TableEditor can be set to auto-sort on columns. For more simple examples, the ListStrEditor may also work.

  4. Add functionality to the TraitsUI ListEditor so that the list items are optionally displayed in a sorted order. This is probably the most difficult option.

While it is clearly the least elegant solution, I'd probably just go with the first in most cases. You might also consider posting this question on the ETS-Users group to see if anyone else has some thoughts on it.

Upvotes: 1

Related Questions