【问题标题】:wxPython UltimateListCtrl Error After Deleting RowwxPython UltimateListCtrl 删除行后出错
【发布时间】:2018-09-12 01:11:37
【问题描述】:

我一直在使用 wxPython 和 UltimateListCtrl 为项目创建 GUI。因为我的 GUI 有几个具有 UltimateListCtrls 的面板,我遇到了一个我似乎无法解决的问题。我的 GUI 包含一个包含 5 列的文件列表:复选框列、文件名列、文件扩展名列、文件大小列和组合框列。 Example GUI 在列表下方,我有一个按钮,单击该按钮将删除选中 CheckBox 的任何行。我的问题出现在我删除例如第 1 行并单击最后一行的 CheckBox 或 ComboBox 之后。当我这样做时,我收到此错误:

Traceback(最近一次调用最后一次): OnSetFocus 中的文件“C:\Users\Media_i5\PycharmProjects\PicThings\venv\lib\site-packages\wx\lib\agw\ultimatelistctrl.py”,第 2260 行 选择 = listCtrl.GetItemState(self._itemId, ULC_STATE_SELECTED) GetItemState 中的文件“C:\Users\Media_i5\PycharmProjects\PicThings\venv\lib\site-packages\wx\lib\agw\ultimatelistctrl.py”,第 8944 行 raise Exception("GetItemState 中的项目索引无效") 例外:GetItemState 中的项目索引无效

当我回溯调用时,我发现当我删除一行时,后面每一行的 ID/Index 都会更新以匹配其在列表中的新位置,但 CheckBox 和 ComboBox 的 ID/Index 不是。在下面的示例代码中,当索引 1 处的行(第 2 行)被删除时,索引 2 处的行变为索引 1,索引 3 变为索引 2,依此类推......但是移动到索​​引的行上的 Checkbox/ComboBox 1 的 ID/Index 仍然为 2。在这种情况下,删除第 1 行后,如果单击最后一行的 CheckBox/ComboBox,则会标记上述错误,因为 Box 的索引大于行。

我希望这是有道理的。下面附上一个说明问题的小示例代码。运行代码,点击图片1的复选框,点击清除按钮,然后点击图片7的复选框。

谁能帮我弄清楚为什么盒子的 ID/索引没有像行一样自动更新?

import wx
from wx.lib.agw import ultimatelistctrl as ULC

class Mywin(wx.Frame):
    t_col1 = ['PICTURE 1', 'PICTURE 2', 'PICTURE 3', 'PICTURE 4', 'PICTURE 5', 'PICTURE 6', 'PICTURE 7']
    t_col4 = ['1', '1', '3', '5', '5', '1', '2']

    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent)
        box2 = wx.BoxSizer(wx.VERTICAL)
        title = wx.StaticText(self, wx.ID_ANY, label='Pictures On Frame:')
        box2.Add(title, 0, wx.ALL, 5)
        self.list = ULC.UltimateListCtrl(self, agwStyle = ULC.ULC_REPORT | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
        colhead = ["", "File", "Ext", "Size", "Rating"]
        colwidth = [30, 300, 45, 45, 45]
        for x in range(0, len(colhead)):
            self.list.InsertColumn(x, colhead[x], width=colwidth[x])
        box2.Add(self.list, 1, wx.EXPAND)
        btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
        btnC = wx.Button(self, label="Clear")
        btnC.Bind(wx.EVT_BUTTON, self.on_clear)
        btnSizer2.Add(btnC, 0, wx.ALL | wx.CENTER, 5)
        box2.Add(btnSizer2, 1, wx.EXPAND)
        self.SetSizer(box2)
        self.SetTitle('Picture Frame Selector')
        self.Centre()
        self.Maximize()
        self.Show()
        rb_list = ["1", "2", "3", "4", "5"]
        for x in range(0 , len(self.t_col1)):
            self.list.InsertStringItem(x, '')
            cBox = wx.CheckBox(self.list)
            self.list.SetItemWindow(x, 0, cBox)
            self.list.SetStringItem(x, 1, self.t_col1[x])
            self.list.SetStringItem(x, 2, '.jpg')
            dBox = wx.ComboBox(self.list, value=self.t_col4[x], choices=rb_list, style=wx.CB_READONLY)
            self.list.SetItemWindow(x, 4, dBox, expand=True)

    def on_clear(self, event):
        t = 0
        for x in range(0, len(self.t_col1)):
            if self.list.GetItemWindow(t, 0).IsChecked():
                self.list.DeleteItem(t)
            else:
                t += 1
        event.Skip()

if __name__ == "__main__":
    ex = wx.App()
    Mywin(None, 'Row Delete Issue')
    ex.MainLoop()

【问题讨论】:

  • 可能是 wxPython 中的一个错误?你有哪个版本的 {wx}Python?可以试试最新版本吗?

标签: python wxpython wxwidgets


【解决方案1】:

您可以通过使用list t_col1 重建listctrl 来解决此问题。

import wx
from wx.lib.agw import ultimatelistctrl as ULC

class Mywin(wx.Frame):
    t_col1 = ['PICTURE 1', 'PICTURE 2', 'PICTURE 3', 'PICTURE 4', 'PICTURE 5', 'PICTURE 6', 'PICTURE 7']
    t_col4 = ['1', '1', '3', '5', '5', '1', '2']

    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent)
        box2 = wx.BoxSizer(wx.VERTICAL)
        title = wx.StaticText(self, wx.ID_ANY, label='Pictures On Frame:')
        box2.Add(title, 0, wx.ALL, 5)
        self.list = ULC.UltimateListCtrl(self, agwStyle = ULC.ULC_REPORT | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
        colhead = ["", "File", "Ext", "Size", "Rating"]
        colwidth = [30, 300, 45, 45, 45]
        for x in range(0, len(colhead)):
            self.list.InsertColumn(x, colhead[x], width=colwidth[x])
        box2.Add(self.list, 1, wx.EXPAND)
        btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
        btnC = wx.Button(self, label="Clear")
        btnC.Bind(wx.EVT_BUTTON, self.on_clear)
        btnSizer2.Add(btnC, 0, wx.ALL | wx.CENTER, 5)
        box2.Add(btnSizer2, 1, wx.EXPAND)
        self.SetSizer(box2)
        self.SetTitle('Picture Frame Selector')
        self.Centre()
        self.Maximize()
        self.CreateList()
        self.Show()

    def CreateList(self):
        rb_list = ["1", "2", "3", "4", "5"]
        for x in range(0 , len(self.t_col1)):
            self.list.InsertStringItem(x, '')
            cBox = wx.CheckBox(self.list)
            self.list.SetItemWindow(x, 0, cBox)
            self.list.SetStringItem(x, 1, self.t_col1[x])
            self.list.SetStringItem(x, 2, '.jpg')
            dBox = wx.ComboBox(self.list, value=self.t_col4[x], choices=rb_list, style=wx.CB_READONLY)
            self.list.SetItemWindow(x, 4, dBox, expand=True)

    def on_clear(self, event):
        for x in range(len(self.t_col1) -1 , -1, -1):
            if self.list.GetItemWindow(x, 0).IsChecked():
                self.t_col1.pop(x)
        self.list.DeleteAllItems()
        self.CreateList()
        event.Skip()

if __name__ == "__main__":
    ex = wx.App()
    Mywin(None, 'Row Delete Issue')
    ex.MainLoop()

请注意,出于显而易见的原因,项目会从列表中反向删除,我使用 list.pop() 以便可以使用 range 中的位置而不是 list.remove()
我不允许在Rating 列下保存对choices 所做的更改。

【讨论】:

  • 我最初考虑在每次删除后重新填充列表,但犹豫不决,因为我的一些列表最终可能是 100-200 行,但在看到您的实际答案后,我决定将列表填充 25 行当我向下滚动列表时。谢谢你的帮助!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 2014-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多