I am trying to create an CheckListCtrl where you could sort all the Data in an column by clicking on its header.
In the basic example of my code i will post below i setup "rows" as a List of Tuples because in my final version the ListCtrl will show the result of an SQLite Query.
The problem i have with my code so far:
I used self.itemDataMap = rows
wrong i think, i get this error message if i try to sort: TypeError: list indices must be integers or slices, not tuple
. So how do i use it with an List of Tuples and not with an Dictionary?
import wx
import wx.lib.mixins.listctrl as listmix
from wx.lib.agw import ultimatelistctrl as ULC
APPNAME='Sortable Ultimate List Ctrl'
APPVERSION='1.0'
MAIN_WIDTH=300
MAIN_HEIGHT=300
class TestUltimateListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(MAIN_WIDTH,MAIN_HEIGHT))
self.index = 0
self.list_ctrl = ULC.UltimateListCtrl(self, -1, agwStyle=ULC.ULC_REPORT|ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
self.list_ctrl.InsertColumn(0, "Make")
self.list_ctrl.InsertColumn(1, "Model")
self.list_ctrl.InsertColumn(2, "Year")
self.list_ctrl.InsertColumn(3, "Color")
rows = [("Ford", "Taurus", "1996", "Blue"),
("Nissan", "370Z", "2010", "Green"),
("Porche", "911", "2009", "Red")
]
index = 0
for data in rows:
pos=self.list_ctrl.InsertStringItem(index, data[0])
self.list_ctrl.SetStringItem(index, 1, data[1])
self.list_ctrl.SetStringItem(index, 2, data[2])
self.list_ctrl.SetStringItem(index, 3, data[3])
self.list_ctrl.SetItemData(index, rows[index])
index += 1
self.itemDataMap = rows
listmix.ColumnSorterMixin.__init__(self, 3)
self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list_ctrl)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
def GetListCtrl(self):
return self.list_ctrl
def OnColClick(self, event):
pass
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,wx.ID_ANY,'%s v%s' % (APPNAME,APPVERSION),size=(MAIN_WIDTH,MAIN_HEIGHT),style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN)
panel = TestUltimateListCtrlPanel(self)
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
First let me quote the documentation of wx.lib.mixins.listctrl.ColumnSorterMixin
:
The combined class must have an attribute named itemDataMap that is a dictionary mapping the data values to a sequence of objects representing the values in each column. These values are compared in the column sorter to determine sort order.
That's hardly understandable.
What it means is, that .itemDataMap
hs to be a dictionary, where the key of each entry are the data of a row. The value is a list:
self.itemDataMap = {}
for rowIndex, data in enumerate(rows):
self.itemDataMap[data] = []
Each element of the immer list is associated to a column and is used to sort the elements of a column. If the rows should be sorted in alphabetic order dependent of the values of a column, then the value which is associated to the column index (in the dictionary of a row) can be the value of the filed:
self.itemDataMap[data] = []
for coldata in data:
self.itemDataMap[data] += coldata
Since the rows are already organised in a list, the rows can be use directly:
self.itemDataMap[data] = data
The same can be achieved by
self.itemDataMap = {data : data for data in rows}
Note, the keys of .itemDataMap
have to correspond to the data of a row, which is set by SetItemData()
.
Since the data of a row are organised in a list
When the list should be sorted by the values of specific column index col
, then all then elements in .itemDataMap
which are associated to col
are listed and the list is sorted by this elements. You can imagine this like:
col = ... # integral index of the column
sorted( [values[col] for values in self.itemDataMap.values()] )
Further note, the number of columns is 4:
listmix.ColumnSorterMixin.__init__(self, 3)
listmix.ColumnSorterMixin.__init__(self, 4)
Class TestUltimateListCtrlPanel
:
class TestUltimateListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(MAIN_WIDTH,MAIN_HEIGHT))
self.list_ctrl = ULC.UltimateListCtrl(self, -1, agwStyle=ULC.ULC_REPORT|ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
self.list_ctrl.InsertColumn(0, "Make")
self.list_ctrl.InsertColumn(1, "Model")
self.list_ctrl.InsertColumn(2, "Year")
self.list_ctrl.InsertColumn(3, "Color")
rows = [("Ford", "Taurus", "1996", "Blue"),
("Nissan", "370Z", "2010", "Green"),
("Porche", "911", "2009", "Red")
]
for rowIndex, data in enumerate(rows):
for colIndex, coldata in enumerate(data):
if colIndex == 0:
self.list_ctrl.InsertStringItem(rowIndex, coldata)
else:
self.list_ctrl.SetStringItem(rowIndex, colIndex, coldata)
self.list_ctrl.SetItemData(rowIndex, data)
self.itemDataMap = {data : data for data in rows}
listmix.ColumnSorterMixin.__init__(self, 4)
self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list_ctrl)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
def GetListCtrl(self):
return self.list_ctrl
def OnColClick(self, event):
pass