Editing Cell Values¶
ListCtrls are normally used for displaying information. The standard ListCtrl allows the value at column 0 (the primary cell) to be edited, but nothing beyond that. ObjectListView allows all cells to be edited. Depending on how the data for a cell is sourced, the edited values can be automagically written back into the model object.
The “editability” of an ObjectListView is controlled by the cellEditMode attribute. This attribute can be set to one of the following values:
Cell editing is not allowed on the control This is the default.
Single clicking on any subitem cell begins an edit operation on that cell. Single clicking on the primary cell does not start an edit operation. It simply selects the row. Pressing
F2edits the primary cell.
Double clicking any cell starts an edit operation on that cell, including the primary cell. Pressing
F2edits the primary cell.
F2edits the primary cell.
Shift-Tabcan be used to edit other cells. Clicking does not start any editing.
Individual columns can be marked as editable via the isEditable attribute (default value is True), though this only has meaning once the ObjectListView itself is editable. If you know that the user should not be allowed to change cells in a particular column, set isEditable to False. Be aware, though, that this may create some surprises, resulting in user complaints like “How come I can’t edit this value by clicking on it like I can on all the other cells?”.
Once a cell editor is active, the normal editing conventions apply:
Returnfinishes the edit and commits the new value to the model object.
Escapecancels the edit.
Tabcommits the current edit, and starts a new edit on the next editable cell.
Shift-Tabcommits the current end, and starts a new edit on the previous editable cell.
Deciding on a cell editor¶
When a cell is to be edited, we need to decide what sort of editor to use.
There are three ways this decision can be made:
- Column based decision
Most simply, the column can be configured with a
When a cell in this column is to be edited, the
cellEditorCreator will be invoked.
This attribute must be set to a “cell editor factory” callable. A “cell editor factory”
must be a callable that accepts three parameters:
- the ObjectListView that needs the editor
- the index of the row to be edited
- the index of the subitem to be edited
This factory should return a fully configured widget that can edit the value at that cell.
- Event based decision
If this is not enough, the programmer can have complete control over the process
by listening for a cell editing starting event
ObjectListView.EVT_CELL_EDIT_STARTING). Within the handler for this event, the
programmer can create and configure any sort of widget they like and then return this
widget via the
newEditor attribute of the event.
shouldConfigureEditor attribute of the event is True (this is the default), the
ObjectListView will perform all the normal default configuration of the cell editor. This
includes setting the controls value, positioning it correctly and hooking up any required
shouldConfigureEditor is False, it is assumed that all configuration has
already been done and nothing else will be done to the widget.
- Registry based decision
Most generally, you can register a “cell editor factory” for a type of object. This
is done using
RegisterCreatorFunction method of the
For example, there is no standard editor for a wx.Colour. To handle the editing of
colours, we would need a factory callable, and then to register it with the
CellEditorRegistry. Which migh look something like this:
def makeColourEditor(olv, rowIndex, subItemIndex): odcb = OwnerDrawnEditor.ColourComboBox(olv) # OwnerDrawnComboxBoxes don't generate EVT_CHAR so look for keydown instead odcb.Bind(wx.EVT_KEY_DOWN, olv._HandleChar) return odcb CellEditorRegistry().RegisterCreatorFunction(type(wx.BLACK), makeColourEditor)
By default, the cell registry is configured with editors for the following standard
You can replace the standard editors with editors of your own devising using the
registry. So if someone make a better date-time editor (yes, please!), they could
use it to edit all
datetime values by doing this:
import datetime ... CellEditorRegistry().RegisterCreatorFunction(datetime.datetime, makeBetterDateTimeEditor)
Getting and Setting the Editors value¶
A cell editor must implement both
Once the cell editor has been created, it is given the cell’s value via the control’s
When the user has finished editing the value, the new value in the editor is retrieved via
Updating the Model Object¶
Once the user has entered a new value into a cell and pressed
Enter, the ObjectListView
tries to store the modified value back into the model object. There are three ways this
1. ObjectListView.EVT_CELL_EDIT_FINISHING Event Handler¶
You can create an event handler for the EVT_CELL_EDIT_FINISHING event (see below). In that
handler, you would write the code to get the modified value from the editor, put that new
value into the model object, and then call
Veto on the event, so that the ObjectListView knows that
it doesn’t have to do anything else. You will also need to call at least
RefreshObject(), so that the changes to the model object are shown in the ObjectListView.
There are cases where this is necessary, but as a general solution, it doesn’t fit my philosophy of slothfulness.
2. Via the Column’s
You can set the
valueSetter attribute on the corresponding ColumnDefn. Like
attribute is quite flexible:
It can be a callable that accepts the model object and the new value:
def updateSalary(person, newValue): person.SetSalary(newValue) if person.userId == self.currentUser.userId: self.NotifySupervisorOfSalaryChange() ColumnDefn("Salary", ... valueSetter=updateSalary)
It can be the name of a method to be invoked,. This method must accept the new value as its sole parameter. Example:
class Track(): ... def SetDateLastPlayed(self, newValue): self.dateLastPlayed = newValue ColumnDefn("Last Played", ... valueSetter="SetDateLastPlayed")
It can be the name of an attribute to be updated. This attribute will not be created: it must already exist. Example:
ColumnDefn("Last Played", ... valueSetter="dateLastPlayed")
For dictionary like model objects, it can be the key into the dictionary. The key would commonly be a string, but it doesn’t have to be.
3. Via the Column’s
Updating the value through the value-GETTER attribute seems wrong somehow. In practice, it is neat and commonly used.
valueGetter attribute is the name of an attribute, or the key into a dictionary, it will very commonly
be the same place where any modified value should be written.
So if a value needs to be written back into the model, and there is no
valueSetter attribute, the ObjectListView
will try to use the
valueGetter attribute to decide how to update the model.
After the update¶
If the model is updated, the row will be automatically refreshed to display the new data.
If the user enters a new value, presses
Enter, and the value in the ObjectListView doesn’t change,
then almost certainly the ObjectListView could not automagically update the model object.
In that case, you will need to track down, which of the above three strategies should be being used, and why it is not.
How Can You Customise The Editing¶
To do something other than the default processing, you can listen for two events: ObjectListView.EVT_CELL_EDIT_STARTING and ObjectListView.EVT_CELL_EDIT_FINISHING.
## MORE HERE ##