UltimateListCtrl - 选择和排序

时间:2015-12-23 16:00:21

标签: python wxpython python-2.x

我用wxpython和ulc为个人数据库写了一个小python程序。 我以为我已经完成并最终从原始访问数据库导入了所有数据,现在遇到了问题。在ulc中有近1000个条目(而不是开发中的50个)会减慢它的速度,因此变得无法使用。 所以我开始使用虚拟模式重写所有内容来解决这个问题。但现在我面临另一个问题。

我在listctrl中有一个复选框来选择多个人,我也使用ColumnSorterMixin。在正常模式下,以不同的方式对列表进行排序时,列表中的选择将保持正确,但在虚拟模式下,选择将保留在行而不是真实项目上。

例如,如果您选择第三行然后求助列表,则第三行中的项目现在位于第四行,但仍然选择第三行。难道我做错了什么?我尝试使用ctrl键复选框和选择。两者都有同样的问题。

这是我的问题测试代码......

import wx
from wx.lib.agw import ultimatelistctrl as ULC
import wx.lib.mixins.listctrl  as  listmix

data = {
1 : ("John Doe", "Madeup Road 11\nFakeTown", "000-383783763"),
2 : ("Jane Doe", "Madeup Road 11\nFakeTown", "000-383783763"),
3 : ("Max Mustermann", "Madeup Road 16\nFakeTown", "043-3434763"),
4 : ("Myself", "Fake Road 9\nFakeTown", "323-3843457773"),
}


class mylist(ULC.UltimateListCtrl, listmix.ColumnSorterMixin):
    def __init__(self, parent):
        ULC.UltimateListCtrl.__init__(self, parent, -1, agwStyle=ULC.ULC_VIRTUAL|ULC.ULC_REPORT|ULC.ULC_USER_ROW_HEIGHT|ULC.ULC_SINGLE_SEL|ULC.ULC_VRULES|ULC.ULC_HRULES)
        self.SetUserLineHeight(40)
        self.itemDataMap = data
        self.itemIndexMap = data.keys()
        self.SetItemCount(len(data))
        self.table_fields=['Name','Street','Phone']
        field_index=0

        for field in self.table_fields:
            info = ULC.UltimateListItem()
            info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
            info._image = []
            info._format = wx.LIST_FORMAT_LEFT
            info._kind = 1
            info._text = field
            info._font= wx.Font(13, wx.ROMAN, wx.NORMAL, wx.BOLD)
            self.InsertColumnInfo(field_index, info)
            self.SetColumnWidth(field_index,175)
            field_index += 1


        self.checked = []
        self.Bind(ULC.EVT_LIST_ITEM_CHECKING, self.OnCheck)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumn)

        #mixins
        listmix.ColumnSorterMixin.__init__(self, 3)

        #sort by column (2), A->Z ascending order (1)
        self.SortListItems(0, 1)


    def SortItems(self,sorter=cmp):
        items = list(self.itemDataMap.keys())
        items.sort(sorter)
        self.itemIndexMap = items

        # redraw the list
        self.Refresh()

    def OnColumn(self, e):
        self.Update()
        e.Skip()

    def GetListCtrl(self):
        return self

    def OnCheck(self, event):
        item_column = (event.m_itemIndex, event.m_item.GetColumn())
        print item_column
        try:
            idx = self.checked.index(item_column)
        except ValueError:
            idx = None

        if idx == None:
            self.checked.append(item_column)
        else:
            del(self.checked[idx])
        self.Refresh()

    def getColumnText(self, index, col):
        item = self.GetItem(index, col)
        return item.GetText()

    def OnGetItemText(self, item, col):
        index=self.itemIndexMap[item]
        s = self.itemDataMap[index][col]
        return s

    def OnGetItemColumnImage(self, item, col):
        return []

    def OnGetItemImage(self, item):
        return []

    def OnGetItemAttr(self, item):
        return None

    def OnGetItemTextColour(self, item, col):
        return None

    def OnGetItemToolTip(self, item, col):
        return None

    def OnGetItemKind(self, item):
        return 1

    def OnGetItemColumnKind(self, item, col):
        if col==0:
            return self.OnGetItemKind(item)
        return 0

    def OnGetItemColumnCheck(self, item, column):
        item_column = (item, column)
        if item_column in self.checked:
            return True
        else:
            return False

class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "UltimateListCtrl in wx.LC_VIRTUAL mode", size=(700, 600))
        panel = wx.Panel(self, -1)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.listctrl = listCtrl = mylist(panel)
        sizer.Add(listCtrl, 1, wx.EXPAND)
        panel.SetSizer(sizer)
        sizer.Layout()
        self.CenterOnScreen()
        self.Show()

class MyApp(wx.App):
    def OnInit(self):
        frame = TestFrame(None)
        frame.Show(True)
        return True

app = MyApp(0)
app.MainLoop()

这可能是在虚拟ListCtrl中还是我做错了什么?

我在Windows 8.1,Python 2.7.10和wxPython 3.0.2.0上测试它。

1 个答案:

答案 0 :(得分:0)

编辑注意到你在3.0.2上测试,所以我从之前更新了我的演示并使用相同的wx版本(和Python 2.7.11)而不是wx 3.0.3'Phoenix'进行了测试我正在使用。
你问的答案是:是的,这是可能的 不止一种方式可以满足,但是这是一个可以完全粘贴并运行在自己的py文件中进行测试的工作演示。
此演示在wx版本3.0.2.0 osx-cocoa(经典)上测试,并符合以下条件:

  

列出一千条记录,在虚拟模式下使用ULC,在任何列上排序,检查多个项目,可以使用全选框检查/取消选中,在排序时保留项目检查而不是保持原位,不会变慢/像您原来的版本一样无法使用。

    import wxversion
    wxversion.select(['3.0'])  #for me this switches from 'Phoenix' to 'Classic'
    import wx; print wx.version();
    from wx.lib.agw import ultimatelistctrl as ULC
    from wx.lib.mixins import listctrl as listmix


    class mylist(ULC.UltimateListCtrl, listmix.ColumnSorterMixin):
        """
        Sortable list with check box for each row and a 'select all' box
        When sorting, the checkboxes follow the data, rather than staying on their row
        """
        def __init__(self, parent):
            ULC.UltimateListCtrl.__init__(self, parent, -1,
                                          agwStyle=ULC.ULC_VIRTUAL | ULC.ULC_REPORT# | ULC_SINGLE_SEL
                                          )
            datasource = Records()
            self.itemDataMap = datasource.data
            self.itemIndexMap = self.itemDataMap.keys()
            self.itemCheckedMap1 = set()
            self.itemCheckedMap2 = set()
            self.table_fields = ["id", "rockcol", "papercol", "scissorscol", "lizardcol"]
            field_index = 0
            for field in self.table_fields:
                info = ULC.UltimateListItem()
                masktoset = ULC.ULC_MASK_TEXT
                if field_index == 0:
                    masktoset |= ULC.ULC_MASK_KIND | ULC.ULC_MASK_CHECK
                    kindtoset = ULC.ULC_KIND_CHECK = wx.ITEM_CHECK
                    widthtoset = 60
                    info.Check(False)
                else:
                    widthtoset = ULC.ULC_AUTOSIZE
                info.SetText(field)
                info.SetKind(kindtoset)
                info.SetMask(masktoset)
                self.InsertColumnInfo(field_index, info)
                self.SetItemCount(len(datasource.data))
                self.SetColumnWidth(field_index, widthtoset)
                field_index += 1
            self.Bind(ULC.EVT_LIST_ITEM_CHECKING, self.OnSetItemCheck)
            self.Bind(ULC.EVT_LIST_COL_CHECKING, self.OnSetColumnCheck)
            self.Bind(ULC.EVT_LIST_COL_CLICK, self.OnColClick, self)
            listmix.ColumnSorterMixin.__init__(self, len(self.table_fields))  #binds col click event
            #self.SortListItems(1, 1)


    ####
    # ----------------------------------------------------------------------------
    # bound event handling
    # ----------------------------------------------------------------------------
        def OnSetItemCheck(self, e):
            x1 = e.m_itemIndex
            y1 = e.EventObject.itemCheckedMap1
            x2 = int(e.m_item._text)
            y2 = e.EventObject.itemCheckedMap2
            for x, y in (x1, y1), (x2, y2):
                if x not in y:
                    y.add(x)
                else:
                    y.remove(x)
            self.Refresh()

        def OnSetColumnCheck(self, e):
            e.EventObject.GetColumn(e.m_col)._checked = x = not e.EventObject.GetColumn(e.m_col)._checked
            if x:
                e.EventObject.itemCheckedMap1 = set(e.EventObject.itemIndexMap)
            else:
                e.EventObject.itemCheckedMap1.clear()
            self.Refresh()

        def OnColClick(self, e):
            self.Refresh()
            e.Skip()

        def GetListCtrl(self): #for sorter init
            return self

        def SortItems(self, sorter=cmp):
            self.itemIndexMap.sort(sorter)
            self.Refresh()

        def SortListItems(self, col=-1, ascending=1):
            self.fix()
            super(mylist, self).SortListItems(col, ascending)

        def fix(self):
            pass

        def OnSortOrderChanged(self):
            self.itemCheckedMap1 = set(i for i, x in enumerate(self.itemIndexMap) if x in self.itemCheckedMap2)


    ####
    # ----------------------------------------------------------------------------
    # overrides for 'virtual' mode support
    # ----------------------------------------------------------------------------
        def OnGetItemText(self, item, col):
            idx = self.itemIndexMap[item]
            return str(self.itemDataMap[idx][col])

        def OnGetItemTextColour(self, item, col):
            return None

        def OnGetItemToolTip(self, item, col):
            return None

        def OnGetItemColumnImage(self, item, column=0):
            return []

        def OnGetItemCheck(self, idx):
            return idx in self.itemCheckedMap1

        def OnGetItemColumnCheck(self, item, column=0):
            return self.OnGetItemCheck(item)

        def OnGetItemKind(self, itemidx):
            return wx.ITEM_CHECK

        def OnGetItemColumnKind(self, itemidx, columnidx):
            if self.GetColumn(columnidx)._kind == wx.ITEM_CHECK:
                return self.OnGetItemKind(itemidx)
            return wx.ITEM_NORMAL


    class Records:
        """
        Generates some unsorted data for testing sort and checkbox preservation
        """
        def __init__(self):
            self.data = {}
            self.makedataset(1000)

        def makedataset(self, rcount):
            for ci in xrange(rcount):
                c1 = str(hash(ci^2*2))
                c2 = str(hash(ci^3*2))
                c3 = str(hash(ci^4*2))
                c4 = str(hash(ci^1*2))
                self.data[ci] = ci, c1, c2, c3, c4


    class TestFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, -1, "virtual-mode ULC", size=(800, 300))
            panel = wx.Panel(self, -1)
            sizer = wx.BoxSizer(wx.VERTICAL)
            self.listctrl = mylist(panel)
            sizer.Add(self.listctrl, 1, wx.EXPAND)
            panel.SetSizer(sizer)
            sizer.Layout()
            self.Show()

    mySandbox = wx.App()
    mySandbox.SetTopWindow(TestFrame())
    mySandbox.MainLoop()

    exit()

使用的策略是每次更改排序顺序时更新选中框的数组。还有其他方法,但这个方法依赖于显示数据中的id列。