Open jonathanrocher opened 7 years ago
Was this changed via user editing of a value or by a change to the underlying list item value from some other source?
We saw this recently with Python 3.6, traitsui 6.1.3, PyQt 5.7.1, both Mac OSX and Windows. The following is a traceback from Mac OSX
Traceback (most recent call last):
File "/Users/kchoi/.edm/envs/envname/lib/python3.6/site-packages/traits/trait_notifiers.py", line 591, in _dispatch_change_event
self.dispatch(handler, *args)
File "/Users/kchoi/.edm/envs/envname/lib/python3.6/site-packages/traits/trait_notifiers.py", line 695, in dispatch
handler(*args)
File "/Users/kchoi/.edm/envs/envname/lib/python3.6/site-packages/traitsui/qt4/list_editor.py", line 252, in update_editor_item
proxy = control.proxy
AttributeError: 'QObject' object has no attribute 'proxy'
Here is the smallest example for reproducing the issue:
class Child(HasTraits):
play_list = List(Float(), [1, 2, 3, 4])
class Model(HasTraits):
child = Instance(Child, ())
class ListEditorDemo(ModelView):
# The Trait to be displayed in the editor
model = Instance(Model)
def default_traits_view(self):
return View(
Item('model.child.play_list', label='Simple'),
title='ListEditor',
height=400,
width=400,
)
popup = ListEditorDemo(model=Model())
if __name__ == '__main__':
popup.configure_traits()
It is important that the list is nested as model.child.play_list
rather than just model.play_list
.
Was this changed via user editing of a value or by a change to the underlying list item value from some other source?
Not sure how it happened for @jonathanrocher. In our case, it happened when the user edits a value in the view.
It's likely to have been the same for me too, though after 2 years, I can't be sure.
OK - I have had a quick look into this, and it works fine on PySide and fails on PyQt. ~I also get the error with model.play_list
.~
~I think that the issue is around differences in the object model for PyQt/sip and PySide/shiboken. We are tucking information away on the PyQt objects (the proxy
attribute) and then dropping the Python reference to the object. Then, when making a change, we go and find the object again, effectively reconstituting it from the C++ level object. It looks like PyQt forgets the attribute, while PySide remembers. It presumably did work at some point 12 years ago.~
Edit: I am wrong we are getting some new QObject
instances from somewhere (one per item, I think) under PyQt and these do not have proxies. I can't see where they are coming from, but in any case the solution below is still the right one.
The solution, I think, is ~that to support PyQt we need to hold on to references to the controls that we are attaching proxy information to and look those up, rather than trying to find them again after the fact; or we might just be able to get away with~ keeping track of the proxies and not worry about the actual controls.
A work around is to create simple wrappers in the list, ie. List(Instance(IntClass))
where IntClass
is something like:
class IntClass(HasStrictTraits):
value = Int
I think this is because of a bug in Traits:
from traits.api import HasTraits, List, Instance, Int
class Child(HasTraits):
values = List(Int)
class Parent(HasTraits):
child = Instance(Child)
values = List(Int)
p = Parent(
child=Child(values=[1, 2, 3]),
values=[4, 5, 6],
)
def printer(object, name, old, new):
print(object, name, old, new)
p.on_trait_change(printer, 'values')
print("Change list items, listener doesn't fire, as expected")
p.values[1] = 100
p.on_trait_change(printer, 'child.values')
print("Change list items on an instance attr, listener fires child.values_items!")
p.child.values[1] = 100
On master, pulled this morning, I just got this traceback when I changed the content of an entry in a list:
I will make a standalone script to show the problem, but thought I would report quickly since this editor was just worked on...
Seen on PyQt 4.11.4-7, Master TraitsUI, and master Pyface, Python 2.7. cc @corranwebster since you were working on the
ListEditor
this past day.