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?
Traits uses a
listsubclass (TraitListObject) for anything stored in aListtrait: 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 theSortedListWithKeyclass is from the "Sorted Containers" third-party package and so isn't a Traits list. TheListEditorexpects aTraitsListObject(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:
Using two
Listtraits, one unsorted (which could be aSet) 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 TraitsUIControllerorModelViewobject).Write a subclass of
TraitListObjectthat has the self-sorting behaviour ofSortedListWithKey. Use a regularListtrait but assign instances of your subclass into it, or for really slick behaviour subclassListto do conversion to your new subclass on any set operation.Use a regular
Listtrait, but aTableEditorwith columns for thenameandage: this is a different UI from what you are intending, and may not suit what your real-world is, but theTableEditorcan be set to auto-sort on columns. For more simple examples, theListStrEditormay also work.Add functionality to the TraitsUI
ListEditorso 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.