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
has to be a dictionary, where the key of each entry is 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 inner list is associated with a column and is used to sort the elements of that column. If the rows should be sorted in alphabetical order depending on the values of that column, then the value which is associated with the column index (in the dictionary of a row) can be the value of the field:
self.itemDataMap[data] = []
for coldata in data:
self.itemDataMap[data] += coldata
Since the rows are already organised in a list, the rows can be used 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 a specific column index col
, then all the elements in .itemDataMap
which are associated with col
are listed and the list is sorted by these 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