Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何正确更改DataViewModel的内容_Python_Wxpython_Wxwidgets - Fatal编程技术网

Python 如何正确更改DataViewModel的内容

Python 如何正确更改DataViewModel的内容,python,wxpython,wxwidgets,Python,Wxpython,Wxwidgets,wxPython最近让我很头疼,所以我不得不再次问你们:) 我的设置 视窗7 可移植Python v 2.7.6.1() wxPython 3.0.2.0() 给出的代码是我实际应用程序的一个非常精简的版本。实际上,我有一个大模型,它以不同的方式显示在不同的控件中。 因此,我有一个模型,即代码示例中的modelRoot,从中我为不同的DataViewCtrl构建了不同的DataViewModels(MyDvcModel)。在代码示例中,我只有一个DataViewModel和一个DataVie

wxPython最近让我很头疼,所以我不得不再次问你们:)

我的设置
  • 视窗7
  • 可移植Python v 2.7.6.1()
  • wxPython 3.0.2.0()
给出的代码是我实际应用程序的一个非常精简的版本。实际上,我有一个大模型,它以不同的方式显示在不同的控件中。 因此,我有一个模型,即代码示例中的
modelRoot
,从中我为不同的DataViewCtrl构建了不同的DataViewModels(
MyDvcModel
)。在代码示例中,我只有一个DataViewModel和一个DataViewCtrl,因为它足以显示我的问题。 我试图紧跟中的DataViewModel示例

代码 这是我最简单的工作示例:

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
及其子代/子代),并让所有DataViewModels都通过它进行更新。不幸的是,我必须在每个模型上调用
itemsadded
,这是一个相当大的痛苦(因为我必须在删除、编辑和移动项目时执行相同的操作)。 另外,我不知道新添加对象的项目ID,因为每个DataViewModel中的项目ID都不同。因此,我使用pub向所有DataViewModels发送一条消息,然后这些DataViewModels搜索新对象,并分别调用
itemsadded
。 由于这不能正常工作,我试图直接调用
itemsadded
,这也不起作用。 通过更改
变量
变量的值,可以在两种实现之间切换。最终目标是让变体
'viaMessage'
工作

问题 以下是如何重现这种奇怪行为的描述:

  • 启动应用程序。您应该只看到折叠的根项目(旁边有“+”)。不触摸树状图,只需点击“添加子对象”按钮几次
  • 现在展开根项目。你会看到里面有几个孩子(和你点击的频率一样多)。但是,在程序开始时,添加了两个子项,它们现在丢失了。这是不希望的,我认为这是错误的行为。
    • 编辑:好吧,事情变得更奇怪了:
      我编辑了代码并实现了一个附加的删除按钮。当我重复所有步骤直到第二步。然后删除所有添加的子项,子项1和2突然神奇地再次出现!(在添加2个子项后向左,然后展开根| |在删除两个添加的子项并再次展开根后向右)
  • 无论如何,现在请重新启动应用程序(关闭窗口并再次运行脚本)。现在展开根项目并单击“添加子项”。哇,突然间,它开始工作了。
  • 好的,让我们尝试另一种方法:重新启动应用程序。再次展开并折叠根项。现在单击“添加子项”几次。现在再次展开根项目
  • 这似乎又起了作用。所有的子项,在开始时添加的子项以及通过按钮添加的子项都在那里

    因此,该错误显然只有在您扩展父项之前添加子项时才会出现

    这是什么魔法

    我的印象是,我想要实现的并不是什么特别的东西,我想知道错误在哪里,我无法通过谷歌找到这个问题,所以我不得不假设错误在我这边,但我找不到它

    只是为了证明这个问题的标题:我在删除一个项目时也有类似的问题。因此,问题通常是关于如何正确更改DataViewModel的内容(例如,删除、添加和更改项的值),而不仅仅是添加项

    我的尝试
    • 我试图在谷歌上搜索“wxwidgets dataviewmodel itemadded collapsed”,但结果不是我想要的
    • 我有一个想法,到目前为止我还没有尝试过,因为这只是一个解决办法:在程序启动时,我可以以编程方式展开和折叠所有子树。但是,我希望避免这种变通方法
    • 我试图调试它,但看不到任何可疑的东西
    • 我检查了原始的wxWidgets代码,但没有完全掌握它
    我的问题
    • 怎么了?为什么它不能按预期工作?这是wxPython错误还是我的代码中的错误?
    • 我如何修复它?
    次要任务
    • 我有没有比我如何实现目标更好的方法
    • 您是否看到我的代码中有任何其他缺陷或缺点?(除了这是一个精简版,我尽量避免使用样板文件,如
      if uuuuu name\uuuuuu=='uuu main\uuuuu':main()
      和MVC设计(至少缺少C)等。)
    • 为什么我不能直接使用
      MyDvcModel.Update
      作为消息处理程序,但我必须通过
      onimadded()
      使用间接方式?如果我使用
      MyDvcModel.Update
      ,我会在应用程序实际启动之前得到一个异常
      (TypeError:在方法“DataViewItem\uuuuuuuu cmp\uuuuuuuuu”中,应为“wxDataViewItem*”类型的参数2)
    如果这些问题也能得到回答,那就太好了,但我没有必要也不足以接受你的答案作为解决方案;)


    非常感谢您的帮助。

    您的
    PyDataViewModel
    代码比应用程序所需的复杂。不必更新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()
    

    您的
    PyDataViewModel
    代码比应用程序所需的复杂。而不是
    更新