I'm trying to build a prototype application that consists of a table of data linked to a graph, which will be displayed alongside the table and updated as the data changes.
For the table I'm using a ListCtrl-derived object and, because I want to be able to edit the data in-place, I'm also inheriting the TextEditMixin class:
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin):
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.TextEditMixin.__init__(self)
I want to keep my back-end data separate from its display, so my
wx.Frame
-derived object has a data source object from which it reads
data to populate the ListCtrl.
self.list = EditableListCtrl(panel, style=wx.LC_REPORT)
self.list.InsertColumn(0, 'A', width=140)
self.list.InsertColumn(1, 'B', width=130)
for i in range(0, self.db.getNumRecords()):
item = self.db.getRecord(i)
index = self.list.InsertStringItem(sys.maxint, str(item[0]))
self.list.SetStringItem(index, 1, str(item[1]))
Since I now essentially have two copies of the data, I'd like to make sure that the data source is updated whenever the ListCtrl is edited by the user.
Is there a standard way to do this?
I have tried binding to the EVT_LIST_ITEM_DESELECTED event, but it triggers before the TextEditMixin functionality changes the data in the ListCtrl - the data retrieved from the ListCtrl by the callback function is the old data.
There are two ways to synchronise the data.
1. Use a Virtual ListCtrl
When using a Virtual ListCtrl, you don't have to add data manually. It pulls data from your data source.
To make a ListCtrl virtual, initialise the ListCtrl with the
wx.LC_VIRTUAL
style flag.
For the Virtual ListCtrl to pull the data, you need to override the following functions (obviously you need to subclass ListCtrl first):
OnGetItemText(self, item, column)
OnGetItemAttr(self, item)
OnGetItemImage(self, item)
The first of these handles string data. I haven't used the other
two. (If you're not using them, just return None
and -1
respectively.)
You also need to call SetItemCount(item_count)
to tell the ListCtrl
how many records to retrieve.
To update the data source when the user modifies a cell, you need to
implement SetVirtualData(self, row, col, text)
.
See the presentation "Advanced wxPython Nuts and Bolts" by Robin Dunn for more information.
2. Use a regular ListCtrl
Subclass ListCtrl and override the function SetStringItem(self, row,
col, text)
. In your new implementation, update your data source. Don't
forget to also call the base class SetStringItem()
though! Otherwise,
the ListCtrl appearance won't change.
Virtual ListCtrl is a bit more work, but recommended because you no longer end up with two copies of the data.
(Thanks to Mike Driscoll for pointing me in the right direction to find this information!)