wxPython - Как отсортировать элементы столбца ListCtrl? - PullRequest
1 голос
/ 20 июня 2019

Я пытаюсь создать CheckListCtrl, где можно отсортировать все данные в столбце, щелкнув его заголовок.

В базовом примере моего кода я опубликую ниже, я настраиваю "строки" в виде списка кортежей, потому что в моей окончательной версии ListCtrl покажет результат запроса SQLite.

У меня проблема с моим кодом:

Я использовал self.itemDataMap = rows неправильно, я думаю, я получаю это сообщение об ошибке, если я пытаюсь сортировать: TypeError: list indices must be integers or slices, not tuple. Так как мне использовать его со списком кортежей, а не со словарем?

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()

1 Ответ

2 голосов
/ 20 июня 2019

Сначала позвольте мне процитировать документацию wx.lib.mixins.listctrl.ColumnSorterMixin:

Объединенный класс должен иметь атрибут itemDataMap, представляющий собой словарь, отображающий значения данных в последовательность объектов, представляющих значения в каждом столбце. Эти значения сравниваются в сортировщике столбцов для определения порядка сортировки.

Это вряд ли понятно.

Что это означает, что .itemDataMap hs - это словарь , где ключом каждой записи являются данные строки. Значение представляет собой список:

self.itemDataMap = {}
for rowIndex, data in enumerate(rows):
    self.itemDataMap[data] = []

Каждый элемент списка immer связан со столбцом и используется для сортировки элементов столбца. Если строки должны быть отсортированы в алфавитном порядке в зависимости от значений столбца, то значение, которое связано с индексом столбца (в словаре строки), может быть значением поля:

self.itemDataMap[data] = []
for coldata in data:
    self.itemDataMap[data] += coldata

Поскольку строки уже организованы в виде списка, строки можно использовать напрямую:

self.itemDataMap[data] = data

То же самое можно достичь с помощью

self.itemDataMap = {data : data for data in rows} 

Обратите внимание, что ключи .itemDataMap должны соответствовать данным строки, которая установлена ​​SetItemData().

Поскольку данные строки организованы в виде списка

Когда список должен быть отсортирован по значениям индекса конкретного столбца col, тогда перечисляются все элементы в .itemDataMap, связанные с col, и список сортируется по этим элементам. Вы можете представить это как:

col = ... # integral index of the column 
sorted( [values[col] for values in self.itemDataMap.values()] )

Примечание: количество столбцов равно 4:

listmix.ColumnSorterMixin.__init__(self, 3)
listmix.ColumnSorterMixin.__init__(self, 4)


Класс 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
...