首页 > 解决方案 > wxpython DataViewCtrl - 正确识别放置目标

问题描述

我想使用带有 PyDataViewModel 的 DataViewCtrl 作为具有拖放支持的树状控件。我快到了,但我不知道如何区分在行之间或父项顶部放置项目。两张截图说明了这个问题。在这两种情况下,标识为放置目标的项目都是父项目。

落在父母身上 落在两行之间

这是代码:

import wx
import wx.dataview as dv

print(wx.__version__)

class Container(list):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Container {}'.format(self.name)

class Element(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Element {}'.format(self.name)

    @property
    def len(self):
        return str(len(self.name))

class MyTreeListModel(dv.PyDataViewModel):
    def __init__(self, data):
        dv.PyDataViewModel.__init__(self)
        self.data = data

    def GetColumnCount(self):
        return 2

    def GetColumnType(self, col):
        mapper = { 0 : 'string',
                   1 : 'string'
                   }
        return mapper[col]


    def GetChildren(self, parent, children):
        if not parent:
            for cont in self.data:
                children.append(self.ObjectToItem(cont))
            return len(self.data)

        node = self.ItemToObject(parent)
        if isinstance(node, Container):
            for ds in node:
                children.append(self.ObjectToItem(ds))
            return len(node)
        return 0

    def IsContainer(self, item):
        if not item:
            return True
        node = self.ItemToObject(item)
        if isinstance(node, Container):
            return True
        return False

    def GetParent(self, item):
        if not item:
            return dv.NullDataViewItem

        node = self.ItemToObject(item)
        if isinstance(node, Container):
            return dv.NullDataViewItem
        elif isinstance(node, Element):
            for g in self.data:
                try:
                    g.index(node)
                except ValueError:
                    continue
                else:
                    return self.ObjectToItem(g)

    def GetValue(self, item, col):
        node = self.ItemToObject(item)

        if isinstance(node, Container):
            mapper = { 0 : node.name,
                       1 : '',
                       }
            return mapper[col]

        elif isinstance(node, Element):
            mapper = { 0 : node.name,
                       1 : node.len
                       }
            return mapper[col]

        else:
            raise RuntimeError("unknown node type")

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Table")

        panel = wx.Panel(self)

        dvcTree = dv.DataViewCtrl(panel,style=wx.BORDER_THEME|dv.DV_MULTIPLE)
        self.model = MyTreeListModel(data)
        dvcTree.AssociateModel(self.model)

        dvcTree.AppendTextColumn("Container",   0, width=80)
        dvcTree.AppendTextColumn("Element",   1, width=80)

        dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_BEGIN_DRAG, self._onDrag)
        dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_DROP, self._onEndDrag)
        dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_DROP_POSSIBLE, self._onDropPossible)

        self.dvcTree = dvcTree
        dvcTree.EnableDragSource(wx.DataFormat(wx.DF_UNICODETEXT))
        dvcTree.EnableDropTarget(wx.DataFormat(wx.DF_UNICODETEXT))

        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(dvcTree, 1, wx.EXPAND)
        panel.SetSizer(box)
        self.Layout()

    def _onDropPossible(self, evt):
        item = evt.GetItem()
        mod = evt.GetModel()

        if not evt.GetItem().IsOk():
            return

    def _onEndDrag(self, evt):
        if not evt.GetItem().IsOk():
            evt.Veto()
            return

        mod = evt.GetModel()
        print('dropped at', mod.ItemToObject(evt.GetItem()))
        try:
            print('parent:',mod.ItemToObject(mod.GetParent(evt.GetItem())))
        except TypeError:
            print('parent: None')

    def _onDrag(self, evt):
        evt.Allow()
        mod = evt.GetModel()
        print('from', mod.GetValue(evt.GetItem(),0))
        evt.SetDataObject(wx.TextDataObject('don\'t know how to retrieve that information in the drop handler'))
        evt.SetDragFlags(wx.Drag_AllowMove)      

data = [Container('eins'),Container('zwei'),Container('drei')]
for d in data:
    d[:] = [Element('element {}'.format('X'*q)) for q in range(5)]

if __name__ == "__main__":
    app = wx.App()
    f = MyFrame(None)
    f.Show()
    app.MainLoop()

编辑:

这主要是 OSX 上的一个问题,因为 MSW 实现不提供在行之间删除。虽然我无法在 linux 上测试它。

标签: pythonmacoswxpython

解决方案


推荐阅读