wxPython最近给我带来了很多麻烦,所以我再一次在这里问你们:)
给定代码是我实际应用程序的一个非常简化的版本。实际上,我有一个大模型,以不同的方式显示在不同的控件中。
因此,我有一个模型,它是代码示例中的modelRoot
,我从中为不同的DataViewCtrls构建了不同的DataViewModels(MyDvcModel
)。在代码示例中,我只有一个DataViewModel和一个DataViewCtrl,因为它足以显示我的问题。
我试图贴近https://github.com/svn2github/wxPython/blob/master/trunk/demo/DVC_DataViewModel.py
这是我最小的工作示例:
import wx
import wx.dataview
from wx.lib.pubsub import pub
#class for a single item
class DvcTreeItem(object):
def __init__(self, value='item'):
self.parent = None
self.children = []
self.value = value
def AddChild(self, dvcTreeItem):
self.children.append(dvcTreeItem)
dvcTreeItem.parent = self
def RemoveChild(self, dvcTreeItem):
self.children.remove(dvcTreeItem)
dvcTreeItem.parent = None
#class for the model
class MyDvcModel(wx.dataview.PyDataViewModel):
def __init__(self, root):
wx.dataview.PyDataViewModel.__init__(self)
self.root = root
pub.subscribe(self.OnItemAdded, 'ITEM_ADDED')
#-------------------- REQUIRED FUNCTIONS -----------------------------
def GetColumnCount(self):
return 1
def GetChildren(self, item, children):
if not item:
children.append(self.ObjectToItem(self.root))
return 1
else:
objct = self.ItemToObject(item)
for child in objct.children:
#print "GetChildren called. Items returned = " + str([child.value for child in objct.children])
children.append(self.ObjectToItem(child))
return len(objct.children)
def IsContainer(self, item):
if not item:
return True
else:
return (len(self.ItemToObject(item).children) != 0)
return False
def GetParent(self, item):
if not item:
return wx.dataview.NullDataViewItem
parentObj = self.ItemToObject(item).parent
if parentObj is None:
return wx.dataview.NullDataViewItem
else:
return self.ObjectToItem(parentObj)
def GetValue(self, item, col):
if not item:
return None
else:
return self.ItemToObject(item).value
#-------------------- CUSTOM FUNCTIONS -----------------------------
def OnItemAdded(self, obj):
self.Update(obj) #for some weird reason, the update function cannot be used directly as event handler for pub (?).
def Update(self, obj, currentItem=wx.dataview.DataViewItem()):
children = []
self.GetChildren(currentItem, children)
for child in children:
self.Update(obj, child) #recursively step through the tree to find the item that belongs to the added object
if self.ItemToObject(child) == obj:
self.ItemAdded(self.GetParent(child), child)
print "item " + obj.value + " was added!"
break
#class for the frame
class wxTreeAddMini(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
self.myDVC = wx.dataview.DataViewCtrl(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0)
self.myButton = wx.Button(self, wx.ID_ANY, u"Add Child", wx.DefaultPosition, wx.DefaultSize, 0)
self.myDelButton = wx.Button(self, wx.ID_ANY, u"Del Child", wx.DefaultPosition, wx.DefaultSize, 0)
mySizer = wx.BoxSizer(wx.VERTICAL)
mySizer.Add(self.myDVC, 1, wx.ALL|wx.EXPAND, 5)
mySizer.Add(self.myButton, 0, wx.ALL, 5)
mySizer.Add(self.myDelButton, 0, wx.ALL, 5)
self.SetSizer(mySizer)
app = wx.App(False)
modelRoot = DvcTreeItem('root')
child1 = DvcTreeItem('child1 - the forgotten one')
child1.AddChild(DvcTreeItem('even complete subtrees'))
child1.AddChild(DvcTreeItem('disappear'))
modelRoot.AddChild(child1)
modelRoot.AddChild(DvcTreeItem('child2 - the forgotten brother'))
childNum = 3
model = MyDvcModel(modelRoot)
frame = wxTreeAddMini(None)
frame.myDVC.AssociateModel(model)
frame.myDVC.AppendTextColumn("stuff", 0, width=250, mode=wx.dataview.DATAVIEW_CELL_INERT)
frame.Show()
def DeleteLastItemFromRoot(*ignoreEvent):
global childNum
if modelRoot.children != []:
obj = modelRoot.children[-1] #select last item
modelRoot.RemoveChild(obj)
model.ItemDeleted(model.ObjectToItem(modelRoot), model.ObjectToItem(obj))
def AddItemToRoot(*ignoreEvent):
global childNum
newObject = DvcTreeItem('child' + str(childNum))
modelRoot.AddChild(newObject)
childNum += 1
VARIANT = 'callItemAdded'
if VARIANT == 'viaMessage':
wx.CallAfter(pub.sendMessage, 'ITEM_ADDED', obj=newObject)
elif VARIANT == 'callItemAdded':
model.ItemAdded(model.ObjectToItem(modelRoot), model.ObjectToItem(newObject))
frame.myButton.Bind(wx.EVT_BUTTON, AddItemToRoot)
frame.myDelButton.Bind(wx.EVT_BUTTON, DeleteLastItemFromRoot)
app.MainLoop()
我的最终目标是仅更新低级模型(modelRoot
及其后代/子级)并使所有DataViewModel更新。不幸的是,我必须在每个模型上调用ItemAdded
,这是一个非常大的痛苦(因为我必须对删除,编辑和移动项目做同样的事情)。
此外,我不知道新添加的对象的项ID,因为每个DataViewModel中的项ID都不同。因此,我使用pub向所有DataViewModel发送消息,然后DataViewModel搜索该新对象并分别调用ItemAdded
。
由于这没有成功,我试图直接致电ItemAdded
,这也不起作用。
您可以通过更改VARIANT
变量的值来在两种实现之间切换。最终目标是让VARIANT 'viaMessage'
起作用。
以下是如何重现奇怪行为的描述:
再次,它似乎工作。所有孩子,开头添加的孩子以及按钮添加的孩子都在那里。
因此,只有在您扩展父项目之前添加子项时,才会显示该错误。
这是什么样的巫术?
我的印象是,我想要实现的并不是什么特别的事情,我想知道错误在哪里以及我无法通过谷歌找到这个问题,所以我不得不假设错误在于我的身边,但我无法找到它。
仅为了证明这个问题的标题:删除项目时遇到类似的问题。因此,更一般地说,问题是如何正确更改DataViewModel的内容(例如删除,添加和更改项目的值),而不仅仅是添加项目。
"wxwidgets dataviewmodel itemadded collapsed"
,但结果不是我想要的。if __name__ == '__main__': main()
和MVC设计(至少C缺失)等。)MyDvcModel.Update
作为消息处理程序,但我必须通过OnItemAdded()
使用间接?如果我使用MyDvcModel.Update
,我会在应用实际启动(TypeError: in method 'DataViewItem___cmp__', expected argument 2 of type 'wxDataViewItem *')
之前收到异常。如果这些问题也可以回答,那会很好,但对我来说,接受你的答案作为解决方案既不必要也不够;)
感谢任何帮助。
答案 0 :(得分:1)
您的PyDataViewModel
代码比您的应用程序所需的代码更复杂。不是Update
你的DVC模型,而是完全可以清除它(使模型本身弄清楚数据如何变化并根据它发送信息给DVC)。这项工作没有明显的延迟百件(我没有测试过几千件)。
请执行以下操作:
# remove subscription, no longer needed
# pub.subscribe(self.OnItemAdded, 'ITEM_ADDED')
# remove OnItemAdded and Update
#-------------------- CUSTOM FUNCTIONS -----------------------------
简化为:
def DeleteLastItemFromRoot(*ignoreEvent):
global childNum
if modelRoot.children != []:
obj = modelRoot.children[-1] #select last item
modelRoot.RemoveChild(obj)
# no longer required, handled my model.Cleared()
# model.ItemDeleted(model.ObjectToItem(modelRoot), model.ObjectToItem(obj))
# Forcing a synchronisation python model/PyDataViewModel/DVC
model.Cleared()
def AddItemToRoot(*ignoreEvent):
global childNum
newObject = DvcTreeItem('child' + str(childNum))
modelRoot.AddChild(newObject)
childNum += 1
# syncing
model.Cleared()