virtual wx.ListCtrl 在 SetItemCount 上引发 wxAssertionError

virtual wx.ListCtrl raises wxAssertionError on SetItemCount

我想创建一个自定义的 ListCtrl "MyListCtrlCrafting",它从另一个名为 "DBInterface" 的 class 中提取数据(实际上它不是一个真正的数据库,而是一个复杂的 python字典)。有关于如何做到这一点的教程,我遵循 "Python in Action"。要点是: 继承自 ListCtrl 并将样式参数设置为 wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VIRTUAL,在 init() 期间调用 SetItemCount() 并覆盖一些方法。

为了测试它是如何工作的,我制作了一个小应用程序,它只包含一个(主)框架、虚拟 ListCtrl 和一个基本版本的 DBInterface。在此设置中一切正常。但是当我连接真实应用程序的 classes 时,我得到一个 Traceback:

Traceback (most recent call last):
  File "DesktopCrafter.py", line 198, in <module>
    controller = AppCtrl()
  File "DesktopCrafter.py", line 186, in __init__
    self.frame = GUI.MainWindow(back_end=self.back_end, crafting_controller=self.crafting_controller, parent=None, title='Desktop Crafter')
...
    self.view = MyListCtrlCrafting(name='crafting-hand', back_end=self.db, crafting_controller=self.cc, parent=self, id=wx.ID_ANY)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 107, in __init__
    self.bindData()
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 121, in bindData
    self.SetItemCount(count)
wx._core.wxAssertionError: C++ assertion "m_count == ListView_GetItemCount(GetHwnd())" failed at ..\..\src\msw\listctrl.cpp(3120) in wxListCtrl::SetItemCount(): m_count should match ListView_GetItemCount

与简单的App相比,virtualListCtrl现在是深度嵌套的。这个错误只能由嵌套内部或 DBInterface 和 ListCtrl 之间的错误连接产生吗?还是我必须了解 m_count 的计算方式才能解决此错误?如果是这样,我如何读取 _core 文件?我已经在 core.py 文件中阅读了有关 ListCtrl 的信息,但它不包含相关部分。

我对这个回溯的问题是我不明白为什么在 SetItemCount() 期间会引发它。这个方法应该类似于定义,因为它处理的是列表的行,所以它应该接受正整数,可能是 0,也可能是标准的 -1。我插入 5,所以这不是真正的问题(?)

非常感谢任何帮助或提示!

完整的回溯:

Traceback (most recent call last):
  File "DesktopCrafter.py", line 198, in <module>
    controller = AppCtrl()
  File "DesktopCrafter.py", line 186, in __init__
    self.frame = GUI.MainWindow(back_end=self.back_end, crafting_controller=self.crafting_controller, parent=None, title='Desktop Crafter')
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 285, in __init__
    self.InitUI()
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 299, in InitUI
    self.panel = MainPanel(back_end=self.db, crafting_controller=self.cc)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 251, in __init__
    self.splitter = MySplitter(back_end=back_end, crafting_controller=crafting_controller)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 229, in __init__
    self.l_frame = LPanel(back_end=back_end, crafting_controller=crafting_controller)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 188, in __init__
    self.panel = CraftingPanel(back_end=back_end, crafting_controller=crafting_controller, *args, **kwargs)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 154, in __init__
    self.view = MyListCtrlCrafting(name='crafting-hand', back_end=self.db, crafting_controller=self.cc, parent=self, id=wx.ID_ANY)
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 107, in __init__
    self.bindData()
  File "d:\workspace-fun\DesktopCrafter\dc\util\DCUI.py", line 121, in bindData
    self.SetItemCount(count)
wx._core.wxAssertionError: C++ assertion "m_count == ListView_GetItemCount(GetHwnd())" failed at ..\..\src\msw\listctrl.cpp(3120) in wxListCtrl::SetItemCount(): m_count should match ListView_GetItemCount

虚拟 ListCtrl(两个打印都给我预期的结果):

 class MyListCtrlCrafting(wx.ListCtrl):


    def __init__(self, name, back_end, crafting_controller, *args, **kwargs):

        wx.ListCtrl.__init__(self, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VIRTUAL, *args, **kwargs)

        self.name = name
        self.db = back_end 
        self.cc = crafting_controller
        #print(self.db.retrieveValue(['player', self.name]))
        self.Bind(wx.EVT_LIST_CACHE_HINT, self.DoCacheItems)

        self.bindData()

        self.InsertColumn(0, "Slot")
        self.InsertColumn(1, "Item")
        self.InsertColumn(2, "Amount")      

        print("initialized MyListCtrl")


    def bindData(self):

        data = self.db.retrieveValue(['player', self.name])
        count = len(data)
        #print(count)
        self.itemDataMap = {i: ('slot'+str(i+1), data[str(i)]['item'], data[str(i)]['amount']) for i in range(count)}
        self.SetItemCount(count)

    def DoCacheItems(self, e):

        self.db.updateCache(e.GetCacheFrom(), e.GetCacheTo())           

    def OnGetItemText(self, row_no, col_no):

        data = self.db.retrieveValue(['player', self.name, str(row_no)])    
        return data[col_no]

    def OnGetItemAttr(self, item): return None
    def OnGetItemImage(self, item): return -1

    def OnBackEndUpdated(self):

        self.bindData()
        self.RefreshItems(0, self.GetItemCount())

数据库接口:

 class DBInterface(dict):


    def __init__(self, path, *args, **kwargs):        

        super(DBInterface, self).__init__(*args, **kwargs)
        self.path = path


    def load(self):

        with open(self.path, 'r') as f:
            try:
                self.update(json.loads(f.read()))
            except Exception as e:
                print(e); print(self.path)


    def save(self):

        self.path.ensure()
        with open(self.path, 'w') as f:
            f.write(json.dumps(self))


    def retrieveValue(self, path):

        a = self
        for i in range(len(path)):
            a = a[path[i]]
        return a

该断言基本上是通过询问本机控件它有多少项(应该由先前的 API 调用设置)来确保操作(设置计数)成功的检查。看起来确实是不应该失败的事情,尽管它显然是失败的。也许某些事情正在以某种方式重置或更改本机控件?是否涉及任何线程?

我没有一个可靠的答案给你,但作为建议,我建议采取有效的小样本并逐步构建它,直到它与 window 层次结构和环境以及基本功能相匹配完整应用程序的那部分被破坏的地方。最终它应该以同样的方式开始失败,然后您就会知道是什么改变触发了它,并且可以更仔细地观察它。