enthought / enthought/traitsui

ArrayEditor not updated for changes from observer

Open
#1,884 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
306
Forks
99
PR merge metrics
No merged PRs in 30d

Description

When editing a cell of an array with an `ArrayEditor`, sometimes it's useful to be able to monitor the modified array for changes and to make changes to other cells in the array as a result of a change to an edited cell. Currently, the array editor doesn't show the result of those changes (though the read-only array editor does).

Below is a code example, cut down from something more real. This shows a 2-by-2 array of values. The intent is that in "locked" mode, when one array entry is changed, all corresponding model values should be updated, while in unlocked mode, only the target value is updated.

The read-only view of the array shows the actual values held in the array, but the editable view doesn't get updated for new values. Here's a screenshot showing that:

Screenshot 2022-03-28 at 11 44 35

```python
import numpy as np

from qt_binder.loopback_guard import LoopbackGuard
from traits.api import (
Array,
Bool,
Float,
HasStrictTraits,
Instance,
List,
observe,
)
from traitsui.api import HGroup, Item, ModelView, VGroup, View

class Member(HasStrictTraits):
girth = Float()

class Gang(HasStrictTraits):
members = List(Instance(Member()))

class ExtentsView(ModelView):
model = Instance(Gang)

locked = Bool(True)

display_girths = Array(shape=(2, 2), dtype=np.float64)

_guard = Instance(LoopbackGuard, args=())

@observe("model:members:items:girth")
def _update_displayed_array_when_girths_change(self, event):
index = self.model.members.index(event.object)

new_display_girths = self.display_girths.copy()
new_display_girths.flat[index] = event.new

with self._guard("model"):
self.display_girths = new_display_girths

def _display_girths_default(self):
girths = [member.girth for member in self.model.members]
return np.array(girths).reshape((2, 2))

@observe("display_girths")
def _update_model_on_edit(self, event):
# If this was triggered by a model change, do nothing
if "model" in self._guard:
return

# Figure out which value changed
old = event.old.ravel()
new = event.new.ravel()
(changed,) = np.where(old != new)

# Don't try to deal with the case where multiple values changed
# simultaneously, or no values changed.
if len(changed) != 1:
return

index = changed[0]
value = new[index]

if self.locked:
# Keep all girths in lockstep.
for member in self.model.members:
member.girth = value

else:
# Update only the edited value
self.model.members[index].girth = value

view = View(
HGroup(
VGroup(
Item("locked"),
Item("display_girths"),
Item("display_girths", style="readonly"),
),
),
)

if __name__ == "__main__":
members = [
Member(girth=1.2),
Member(girth=5.6),
Member(girth=2.7),
Member(girth=3.4),
]

star = Gang(members=members)
girths_view = ExtentsView(
model=star,
)

girths_view.configure_traits()
```

The immediate cause seems to be the `_busy` flag in the array editor implementation.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.