enthought / enthought/traitsui

TableColumn renderer to display a combobox even when a cell is in display mode

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

Description

**Context**
Suppose one has an enum trait to be used as an `ObjectColumn` with the `TableEditor`, the enum can be edited using an EnumEditor:
```
from traits.api import HasStrictTraits, Str, Int, List, Instance, observe
from traitsui.api import EnumEditor, View, Item, TableEditor, ObjectColumn

class MyEntry(HasStrictTraits):
name = Str()
value = Int()
@observe("value")
def _print_value_change_event(self, event):
print(event)

class ManyEntry(HasStrictTraits):
entries = List(Instance(MyEntry))

view = View(
Item(
"entries",
editor=TableEditor(
columns=[
ObjectColumn(name="value", editor=EnumEditor(values=[0, 1, 2, 3])),
],
)
)
)

if __name__ == "__main__":
many = ManyEntry(entries=[MyEntry(), MyEntry()])
many.configure_traits()

```

This is how it behaves
![Jun-02-2021 14-07-42](https://user-images.githubusercontent.com/3673984/120485422-f9a38800-c3ab-11eb-8590-e0a3b3e62332.gif)

**The issue**
The display mode of the item does not hint about the fact that it is editable. If it was drawn with a combobox, that would be more obvious.

**Feature/Enhancement request**
A renderer targeted at the EnumEditor which paints the combobox even during display mode.
Perhaps an ObjectColumn with this renderer provided as a default would be useful (similar to the `CheckboxColumn` in `traitsui.extras`).

**Performance Consideration**
- Drawing/painting a widget on a per-cell basis could be expensive if the table has many items. Therefore this should be an opt-in that users of TraitsUI can decide. In cases where a table is expected to contain a few items, it would be a nicer UX to immediately see a cell being editable.

**Potential implementation**
The following renderer will paint a combobox even when the cell is in display mode:
```
from pyface.qt import QtCore, QtGui
from traitsui.qt4.table_editor import TableDelegate

class EnumRenderer(TableDelegate):
""" Paint the combobox widget even when a cell is in display mode.

Note that this has a performance cost when a table contains many rows.
"""

def createEditor(self, parent, option, index):
control = super().createEditor(parent, option, index)
# this makes the editor has the same width as the combobox drawn while not being edited.
control.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToContentsOnFirstShow
)
return control

def paint(self, painter, option, index):
self.initStyleOption(option, index)
style = QtGui.QApplication.style()
cb_option = QtGui.QStyleOptionComboBox()
cb_option.palette = option.palette
cb_option.rect = option.rect
cb_option.state = option.state
cb_option.currentText = str(index.data(QtCore.Qt.DisplayRole))
widget = option.widget
style.drawComplexControl(QtGui.QStyle.CC_ComboBox, cb_option, painter, widget)
style.drawControl(
QtGui.QStyle.CE_ComboBoxLabel, cb_option, painter, widget
)
```
and define the `ObjectColumn` like this:
```
ObjectColumn(name="value", editor=EnumEditor(values=[0, 1, 2, 3]), renderer=EnumRenderer())
```
Then the view looks like this
Screenshot 2021-06-02 at 14 15 00

**Orthogonal Observation: The value is changed even before the editor is closed**
- After the user has clicked a new value, they have to click somewhere else to close the editor, even though, actually, the underlying value has already changed when the combobox value changes. The idea of staying in an edit mode without closing the editor is to defer committing a change and to allow for a change to be cancelled (e.g. by hitting Escape), but that does not apply here. This is related to https://github.com/enthought/traitsui/issues/1299. In some situations, it would be desirable to commit and close the editor immediately. In some other situation, one may want to be able to cancel a change and/or defer the change until after the editor is closed.
- To close the editor immediately, one could do something like this in the renderer:
```

def createEditor(self, parent, option, index):
control = super().createEditor(parent, option, index)
control.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToContentsOnFirstShow
)
control.currentIndexChanged.connect(
self.commitAndCloseEditor, type=QtCore.Qt.QueuedConnection
)
return control

def commitAndCloseEditor(self):
sender = self.sender()
if sender is None:
return
self.commitData.emit(sender)
self.closeEditor.emit(sender)
```

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.