问题
I have been working with wxPython and UltimateListCtrl to create a GUI for a project. For my GUI is have a few panels that have UltimateListCtrls and have I run into an issue I cant seem to fix. My GUI consists of a list of files with 5 columns: a CheckBox Column, a File Name Column, a File Extension Column, a File Size Column, and a ComboBox Column. Example GUI Below the list I have a button that, when clicked, will delete any row whose CheckBox is checked. My issue comes after I delete, for example, row 1 and click on either the CheckBox or the ComboBox for the last row. When I do I receive this error:
Traceback (most recent call last): File "C:\Users\Media_i5\PycharmProjects\PicThings\venv\lib\site-packages\wx\lib\agw\ultimatelistctrl.py", line 2260, in OnSetFocus select = listCtrl.GetItemState(self._itemId, ULC_STATE_SELECTED) File "C:\Users\Media_i5\PycharmProjects\PicThings\venv\lib\site-packages\wx\lib\agw\ultimatelistctrl.py", line 8944, in GetItemState raise Exception("invalid item index in GetItemState") Exception: invalid item index in GetItemState
When I trace back the calls I find that when I delete a row, the ID/Index for each following row is updated to match its new position on the list, but the ID/Index for the CheckBox, ComboBox is not. In the example code below, when the row at index 1 (row 2) is deleted, the row at index 2 becomes index 1, index 3 becomes index 2, etc... However the Checkbox/ComboBox on the row that moved to index 1 still has an ID/Index of 2. In this scenario, after row 1 is deleted, if you click on the CheckBox/ComboBox on the last row, the error above is flagged because the index for the Box is larger than the number of rows.
I hope this makes sense. Attached below is a small example code the illustrates the issue. Run the code, click the CheckBox for Picture 1, Click the Clear Button, then Click the CheckBox for Picture 7.
Can anyone help me figure out why the ID/Index for the Boxes are not updating automatically like the row?
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()
回答1:
You can get around this issue by rebuilding the listctrl
using the list
t_col1.
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()
Note that items are removed from the list in reverse, for obvious reasons and I use list.pop()
so that I can use its position from range
rather than list.remove()
I have not allowed for saving changes made to choices
under the Rating
column.
来源:https://stackoverflow.com/questions/52286106/wxpython-ultimatelistctrl-error-after-deleting-row