Add wx.lib.mixins.treemixin from Frank Niessink.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@44516 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -70,6 +70,7 @@ _treeList = [
|
|||||||
'SearchCtrl',
|
'SearchCtrl',
|
||||||
'SizedControls',
|
'SizedControls',
|
||||||
'AUI_MDI',
|
'AUI_MDI',
|
||||||
|
'TreeMixin',
|
||||||
]),
|
]),
|
||||||
|
|
||||||
# managed windows == things with a (optional) caption you can close
|
# managed windows == things with a (optional) caption you can close
|
||||||
@@ -214,6 +215,7 @@ _treeList = [
|
|||||||
'Throbber',
|
'Throbber',
|
||||||
'Ticker',
|
'Ticker',
|
||||||
'TimeCtrl',
|
'TimeCtrl',
|
||||||
|
'TreeMixin',
|
||||||
'VListBox',
|
'VListBox',
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
230
wxPython/demo/TreeMixin.py
Normal file
230
wxPython/demo/TreeMixin.py
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import wx, wx.lib.customtreectrl, wx.gizmos
|
||||||
|
import wx.lib.mixins.treemixin as treemixin
|
||||||
|
|
||||||
|
class TreeModel(object):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.items = []
|
||||||
|
self.itemCounter = 0
|
||||||
|
super(TreeModel, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def GetItem(self, indices):
|
||||||
|
text, children = 'Hidden root', self.items
|
||||||
|
for index in indices:
|
||||||
|
text, children = children[index]
|
||||||
|
return text, children
|
||||||
|
|
||||||
|
def GetText(self, indices):
|
||||||
|
return self.GetItem(indices)[0]
|
||||||
|
|
||||||
|
def GetChildren(self, indices):
|
||||||
|
return self.GetItem(indices)[1]
|
||||||
|
|
||||||
|
def GetChildrenCount(self, indices):
|
||||||
|
return len(self.GetChildren(indices))
|
||||||
|
|
||||||
|
def SetChildrenCount(self, indices, count):
|
||||||
|
children = self.GetChildren(indices)
|
||||||
|
while len(children) > count:
|
||||||
|
children.pop()
|
||||||
|
while len(children) < count:
|
||||||
|
children.append(('item %d'%self.itemCounter, []))
|
||||||
|
self.itemCounter += 1
|
||||||
|
|
||||||
|
def MoveItem(self, itemToMoveIndices, newParentIndices):
|
||||||
|
itemToMove = self.GetItem(itemToMoveIndices)
|
||||||
|
newParentChildren = self.GetChildren(newParentIndices)
|
||||||
|
newParentChildren.append(itemToMove)
|
||||||
|
oldParentChildren = self.GetChildren(itemToMoveIndices[:-1])
|
||||||
|
oldParentChildren.remove(itemToMove)
|
||||||
|
|
||||||
|
|
||||||
|
class DemoVirtualTreeMixin(treemixin.VirtualTree, treemixin.DragAndDrop):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.model = kwargs.pop('treemodel')
|
||||||
|
self.log = kwargs.pop('log')
|
||||||
|
super(DemoVirtualTreeMixin, self).__init__(*args, **kwargs)
|
||||||
|
self.CreateImageList()
|
||||||
|
|
||||||
|
def CreateImageList(self):
|
||||||
|
size = (16, 16)
|
||||||
|
self.imageList = wx.ImageList(*size)
|
||||||
|
for art in wx.ART_FOLDER, wx.ART_FILE_OPEN, wx.ART_NORMAL_FILE:
|
||||||
|
self.imageList.Add(wx.ArtProvider.GetBitmap(art, wx.ART_OTHER,
|
||||||
|
size))
|
||||||
|
self.AssignImageList(self.imageList)
|
||||||
|
|
||||||
|
def OnGetItemText(self, indices):
|
||||||
|
return self.model.GetText(indices)
|
||||||
|
|
||||||
|
def OnGetChildrenCount(self, indices):
|
||||||
|
return self.model.GetChildrenCount(indices)
|
||||||
|
|
||||||
|
def OnGetItemFont(self, indices):
|
||||||
|
if self.model.GetChildrenCount(indices) > 0:
|
||||||
|
return wx.SMALL_FONT
|
||||||
|
else:
|
||||||
|
return super(DemoVirtualTreeMixin, self).OnGetItemFont(indices)
|
||||||
|
|
||||||
|
def OnGetItemTextColour(self, indices):
|
||||||
|
if len(indices) % 2 == 0:
|
||||||
|
return wx.RED
|
||||||
|
elif len(indices) % 3 == 0:
|
||||||
|
return wx.BLUE
|
||||||
|
else:
|
||||||
|
return super(DemoVirtualTreeMixin,
|
||||||
|
self).OnGetItemTextColour(indices)
|
||||||
|
|
||||||
|
def OnGetItemBackgroundColour(self, indices):
|
||||||
|
if indices[-1] == len(indices):
|
||||||
|
return wx.GREEN
|
||||||
|
else:
|
||||||
|
return super(DemoVirtualTreeMixin,
|
||||||
|
self).OnGetItemBackgroundColour(indices)
|
||||||
|
|
||||||
|
def OnGetItemImage(self, indices, which):
|
||||||
|
if which in [wx.TreeItemIcon_Normal, wx.TreeItemIcon_Selected]:
|
||||||
|
if self.model.GetChildrenCount(indices):
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def OnDrop(self, dropTarget, dragItem):
|
||||||
|
dropIndices = self.ItemIndices(dropTarget)
|
||||||
|
dropText = self.model.GetText(dropIndices)
|
||||||
|
dragIndices = self.ItemIndices(dragItem)
|
||||||
|
dragText = self.model.GetText(dragIndices)
|
||||||
|
self.log.write('drop %s %s on %s %s'%(dragText, dragIndices,
|
||||||
|
dropText, dropIndices))
|
||||||
|
self.model.MoveItem(dragIndices, dropIndices)
|
||||||
|
self.GetParent().RefreshItems()
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTreeCtrl(DemoVirtualTreeMixin, wx.TreeCtrl):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTreeListCtrl(DemoVirtualTreeMixin, wx.gizmos.TreeListCtrl):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['style'] = wx.TR_DEFAULT_STYLE | wx.TR_FULL_ROW_HIGHLIGHT
|
||||||
|
super(VirtualTreeListCtrl, self).__init__(*args, **kwargs)
|
||||||
|
self.AddColumn('Column 0')
|
||||||
|
self.AddColumn('Column 1')
|
||||||
|
for art in wx.ART_TIP, wx.ART_WARNING:
|
||||||
|
self.imageList.Add(wx.ArtProvider.GetBitmap(art, wx.ART_OTHER,
|
||||||
|
(16, 16)))
|
||||||
|
|
||||||
|
def OnGetItemText(self, indices, column=0):
|
||||||
|
return '%s, column %d'%\
|
||||||
|
(super(VirtualTreeListCtrl, self).OnGetItemText(indices), column)
|
||||||
|
|
||||||
|
def OnGetItemImage(self, indices, which, column=0):
|
||||||
|
if column == 0:
|
||||||
|
return super(VirtualTreeListCtrl, self).OnGetItemImage(indices,
|
||||||
|
which)
|
||||||
|
elif self.OnGetChildrenCount(indices):
|
||||||
|
return 4
|
||||||
|
else:
|
||||||
|
return 3
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualCustomTreeCtrl(DemoVirtualTreeMixin,
|
||||||
|
wx.lib.customtreectrl.CustomTreeCtrl):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.checked = {}
|
||||||
|
kwargs['ctstyle'] = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | \
|
||||||
|
wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT
|
||||||
|
super(VirtualCustomTreeCtrl, self).__init__(*args, **kwargs)
|
||||||
|
self.Bind(wx.lib.customtreectrl.EVT_TREE_ITEM_CHECKED,
|
||||||
|
self.OnItemChecked)
|
||||||
|
|
||||||
|
def OnGetItemType(self, indices):
|
||||||
|
if len(indices) == 1:
|
||||||
|
return 1
|
||||||
|
elif len(indices) == 2:
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def OnGetItemChecked(self, indices):
|
||||||
|
return self.checked.get(tuple(indices), False)
|
||||||
|
|
||||||
|
def OnItemChecked(self, event):
|
||||||
|
item = event.GetItem()
|
||||||
|
indices = tuple(self.ItemIndices(item))
|
||||||
|
if self.GetItemType(item) == 2:
|
||||||
|
# It's a radio item; reset other items on the same level
|
||||||
|
for index in range(self.GetChildrenCount(self.GetItemParent(item))):
|
||||||
|
self.checked[indices[:-1]+(index,)] = False
|
||||||
|
self.checked[indices] = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TreeNotebook(wx.Notebook):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
treemodel = kwargs.pop('treemodel')
|
||||||
|
log = kwargs.pop('log')
|
||||||
|
super(TreeNotebook, self).__init__(*args, **kwargs)
|
||||||
|
self.trees = []
|
||||||
|
for class_, title in [(VirtualTreeCtrl, 'TreeCtrl'),
|
||||||
|
(VirtualTreeListCtrl, 'TreeListCtrl'),
|
||||||
|
(VirtualCustomTreeCtrl, 'CustomTreeCtrl')]:
|
||||||
|
tree = class_(self, treemodel=treemodel, log=log)
|
||||||
|
self.trees.append(tree)
|
||||||
|
self.AddPage(tree, title)
|
||||||
|
self.RefreshItems()
|
||||||
|
|
||||||
|
def RefreshItems(self):
|
||||||
|
for tree in self.trees:
|
||||||
|
tree.RefreshItems()
|
||||||
|
tree.UnselectAll()
|
||||||
|
|
||||||
|
def GetSelectedItemIndices(self):
|
||||||
|
tree = self.trees[self.GetSelection()]
|
||||||
|
if tree.GetSelection():
|
||||||
|
return tree.ItemIndices(tree.GetSelection())
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class TestPanel(wx.Panel):
|
||||||
|
def __init__(self, parent, log):
|
||||||
|
self.log = log
|
||||||
|
super(TestPanel, self).__init__(parent)
|
||||||
|
self.treemodel = TreeModel()
|
||||||
|
self.notebook = TreeNotebook(self, treemodel=self.treemodel, log=log)
|
||||||
|
label = wx.StaticText(self, label='Number of children: ')
|
||||||
|
self.childrenCountCtrl = wx.SpinCtrl(self, value='0', max=10000)
|
||||||
|
button = wx.Button(self, label='Update children')
|
||||||
|
button.Bind(wx.EVT_BUTTON, self.onEnter)
|
||||||
|
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
options = dict(flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=2)
|
||||||
|
hSizer.Add(label, **options)
|
||||||
|
hSizer.Add(self.childrenCountCtrl, 2, **options)
|
||||||
|
hSizer.Add(button, **options)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
sizer.Add(self.notebook, 1, wx.EXPAND)
|
||||||
|
sizer.Add(hSizer, 0, wx.EXPAND)
|
||||||
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
|
def onEnter(self, event):
|
||||||
|
indices = self.notebook.GetSelectedItemIndices()
|
||||||
|
text = self.treemodel.GetText(indices)
|
||||||
|
oldChildrenCount = self.treemodel.GetChildrenCount(indices)
|
||||||
|
newChildrenCount = self.childrenCountCtrl.GetValue()
|
||||||
|
self.log.write('%s %s now has %d children (was %d)'%(text, indices,
|
||||||
|
newChildrenCount, oldChildrenCount))
|
||||||
|
self.treemodel.SetChildrenCount(indices, newChildrenCount)
|
||||||
|
self.notebook.RefreshItems()
|
||||||
|
|
||||||
|
|
||||||
|
def runTest(frame, nb, log):
|
||||||
|
win = TestPanel(nb, log)
|
||||||
|
return win
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys, os, run
|
||||||
|
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
|
||||||
|
|
@@ -14,6 +14,8 @@ Refactored the inspection tool such that it can be used as a wx.App
|
|||||||
mix-in class as it was used before (with the wx.lib.mixins.inspect
|
mix-in class as it was used before (with the wx.lib.mixins.inspect
|
||||||
module) and also as a non mix-in tool (using wx.lib.inspect.InspectionTool).
|
module) and also as a non mix-in tool (using wx.lib.inspect.InspectionTool).
|
||||||
|
|
||||||
|
Add wx.lib.mixins.treemixin from Frank Niessink.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
237
wxPython/tests/TreeMixinTest.py
Normal file
237
wxPython/tests/TreeMixinTest.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import wx, wx.gizmos, wx.lib.customtreectrl, unittest, treemixin
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTreeCtrl(treemixin.VirtualTree, wx.TreeCtrl):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.children = {}
|
||||||
|
super(VirtualTreeCtrl, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def OnGetItemText(self, indices):
|
||||||
|
return 'item %s'%'.'.join([str(index) for index in indices])
|
||||||
|
|
||||||
|
def OnGetChildrenCount(self, indices=None):
|
||||||
|
indices = indices or []
|
||||||
|
return self.children.get(tuple(indices), 0)
|
||||||
|
|
||||||
|
def SetChildrenCount(self, indices, childrenCount):
|
||||||
|
self.children[tuple(indices)] = childrenCount
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTreeCtrlTest_NoRootItems(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.frame = wx.Frame(None)
|
||||||
|
self.tree = VirtualTreeCtrl(self.frame)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
|
||||||
|
def testNoRootItems(self):
|
||||||
|
self.assertEqual(0, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testAddTwoRootItems(self):
|
||||||
|
self.tree.SetChildrenCount([], 2)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.assertEqual(2, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testAddOneRootItemAndOneChild(self):
|
||||||
|
self.tree.SetChildrenCount([], 1)
|
||||||
|
self.tree.SetChildrenCount([0], 1)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.tree.ExpandAll()
|
||||||
|
self.assertEqual(2, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testAddOneRootItemAndTwoChildren(self):
|
||||||
|
self.tree.SetChildrenCount([], 1)
|
||||||
|
self.tree.SetChildrenCount([0], 2)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.tree.ExpandAll()
|
||||||
|
self.assertEqual(3, self.tree.GetCount())
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTreeCtrlTest_OneRoot(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.frame = wx.Frame(None)
|
||||||
|
self.tree = VirtualTreeCtrl(self.frame)
|
||||||
|
self.tree.SetChildrenCount([], 1)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
|
||||||
|
def testOneRoot(self):
|
||||||
|
self.assertEqual(1, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testDeleteRootItem(self):
|
||||||
|
self.tree.SetChildrenCount([], 0)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.assertEqual(0, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testAddOneChild(self):
|
||||||
|
self.tree.SetChildrenCount([0], 1)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.tree.ExpandAll()
|
||||||
|
self.assertEqual(2, self.tree.GetCount())
|
||||||
|
|
||||||
|
def testAddTwoChildren(self):
|
||||||
|
self.tree.SetChildrenCount([0], 2)
|
||||||
|
self.tree.RefreshItems()
|
||||||
|
self.tree.ExpandAll()
|
||||||
|
self.assertEqual(3, self.tree.GetCount())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerTestCase(unittest.TestCase):
|
||||||
|
style = wx.TR_DEFAULT_STYLE
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.frame = wx.Frame(None)
|
||||||
|
class HarmonizedTreeCtrl(treemixin.TreeAPIHarmonizer, self.TreeClass):
|
||||||
|
pass
|
||||||
|
self.tree = HarmonizedTreeCtrl(self.frame, style=self.style)
|
||||||
|
self.populateTree()
|
||||||
|
|
||||||
|
def populateTree(self):
|
||||||
|
self.root = self.tree.AddRoot('Root')
|
||||||
|
self.item = self.tree.AppendItem(self.root, 'item')
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerCommonTests(object):
|
||||||
|
def testSetItemImage(self):
|
||||||
|
self.tree.SetItemImage(self.item, -1, wx.TreeItemIcon_Normal)
|
||||||
|
self.assertEqual(-1,
|
||||||
|
self.tree.GetItemImage(self.item, wx.TreeItemIcon_Normal))
|
||||||
|
|
||||||
|
def testGetColumnCount(self):
|
||||||
|
self.assertEqual(0, self.tree.GetColumnCount())
|
||||||
|
|
||||||
|
def testGetItemType(self):
|
||||||
|
self.assertEqual(0, self.tree.GetItemType(self.item))
|
||||||
|
|
||||||
|
def testGetMainWindow(self):
|
||||||
|
self.assertEqual(self.tree, self.tree.GetMainWindow())
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerSingleSelectionTests(object):
|
||||||
|
def testUnselectAll(self):
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.assertEqual([], self.tree.GetSelections())
|
||||||
|
|
||||||
|
def testGetSelections_NoSelection(self):
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.assertEqual([], self.tree.GetSelections())
|
||||||
|
|
||||||
|
def testGetSelections_OneSelectedItem(self):
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.assertEqual([self.item], self.tree.GetSelections())
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerMultipleSelectionTests(object):
|
||||||
|
style = wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE
|
||||||
|
|
||||||
|
def testUnselectAll(self):
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.assertEqual([], self.tree.GetSelections())
|
||||||
|
|
||||||
|
def testGetSelections_NoSelection(self):
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.assertEqual([], self.tree.GetSelections())
|
||||||
|
|
||||||
|
def testGetSelections_OneSelectedItem(self):
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.assertEqual([self.item], self.tree.GetSelections())
|
||||||
|
|
||||||
|
def testGetSelections_TwoSelectedItems(self):
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
item2 = self.tree.AppendItem(self.root, 'item 2')
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.tree.SelectItem(item2)
|
||||||
|
self.assertEqual([self.item, item2], self.tree.GetSelections())
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeCtrlTestCase(TreeAPIHarmonizerTestCase):
|
||||||
|
TreeClass = wx.TreeCtrl
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeCtrl_SingleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerSingleSelectionTests,
|
||||||
|
TreeAPIHarmonizerWithTreeCtrlTestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeCtrl_MultipleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerMultipleSelectionTests,
|
||||||
|
TreeAPIHarmonizerWithTreeCtrlTestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeListCtrlTestCase(TreeAPIHarmonizerTestCase):
|
||||||
|
TreeClass = wx.gizmos.TreeListCtrl
|
||||||
|
|
||||||
|
def populateTree(self):
|
||||||
|
self.tree.AddColumn('Column')
|
||||||
|
super(TreeAPIHarmonizerWithTreeListCtrlTestCase, self).populateTree()
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeListCtrl_SingleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerSingleSelectionTests,
|
||||||
|
TreeAPIHarmonizerWithTreeListCtrlTestCase):
|
||||||
|
|
||||||
|
def testGetColumnCount(self):
|
||||||
|
self.assertEqual(1, self.tree.GetColumnCount())
|
||||||
|
|
||||||
|
def testGetMainWindow(self):
|
||||||
|
self.assertNotEqual(self.tree, self.tree.GetMainWindow())
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithTreeListCtrl_MultipleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerMultipleSelectionTests,
|
||||||
|
TreeAPIHarmonizerWithTreeListCtrlTestCase):
|
||||||
|
|
||||||
|
def testGetColumnCount(self):
|
||||||
|
self.assertEqual(1, self.tree.GetColumnCount())
|
||||||
|
|
||||||
|
def testGetMainWindow(self):
|
||||||
|
self.assertNotEqual(self.tree, self.tree.GetMainWindow())
|
||||||
|
|
||||||
|
def testGetSelections_TwoSelectedItems(self):
|
||||||
|
''' Override TreeAPIHarmonizerMultipleSelectionTests.-
|
||||||
|
testGetSelections_TwoSelectedItems, because
|
||||||
|
TreeListCtrl.SelectItem needs an extra parameter. '''
|
||||||
|
self.tree.UnselectAll()
|
||||||
|
item2 = self.tree.AppendItem(self.root, 'item 2')
|
||||||
|
self.tree.SelectItem(self.item)
|
||||||
|
self.tree.SelectItem(item2, unselect_others=False)
|
||||||
|
self.assertEqual([self.item, item2], self.tree.GetSelections())
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithCustomTreeCtrlTestCase(TreeAPIHarmonizerTestCase):
|
||||||
|
TreeClass = wx.lib.customtreectrl.CustomTreeCtrl
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerCustomTreeCtrlTests(object):
|
||||||
|
def testGetCheckItemType(self):
|
||||||
|
item = self.tree.AppendItem(self.root, 'item', ct_type=1)
|
||||||
|
self.assertEqual(1, self.tree.GetItemType(item))
|
||||||
|
|
||||||
|
def testGetRadioItemType(self):
|
||||||
|
item = self.tree.AppendItem(self.root, 'item', ct_type=2)
|
||||||
|
self.assertEqual(2, self.tree.GetItemType(item))
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithCustomTreeCtrl_SingleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerSingleSelectionTests,
|
||||||
|
TreeAPIHarmonizerCustomTreeCtrlTests,
|
||||||
|
TreeAPIHarmonizerWithCustomTreeCtrlTestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TreeAPIHarmonizerWithCustomTreeCtrl_MultipleSelection( \
|
||||||
|
TreeAPIHarmonizerCommonTests, TreeAPIHarmonizerMultipleSelectionTests,
|
||||||
|
TreeAPIHarmonizerCustomTreeCtrlTests,
|
||||||
|
TreeAPIHarmonizerWithCustomTreeCtrlTestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = wx.App(False)
|
||||||
|
unittest.main()
|
||||||
|
|
||||||
|
|
443
wxPython/wx/lib/mixins/treemixin.py
Normal file
443
wxPython/wx/lib/mixins/treemixin.py
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
'''
|
||||||
|
treemixin.py
|
||||||
|
|
||||||
|
This module provides two mixin classes that can be used with tree
|
||||||
|
controls:
|
||||||
|
- VirtualTree is a class that, when mixed in with a tree control,
|
||||||
|
makes the tree control virtual, similar to a ListCtrl in virtual mode.
|
||||||
|
A virtual tree control builds the tree itself by means of callbacks,
|
||||||
|
so the programmer is freed from the burden of building the tree herself.
|
||||||
|
- DragAndDrop is a mixin class that helps with dragging and dropping of
|
||||||
|
items. The graphical part of dragging and dropping tree items is done by
|
||||||
|
this mixin class. You only need to implement the OnDrop method that is
|
||||||
|
called when the drop happens.
|
||||||
|
|
||||||
|
Both mixin classes work with wx.TreeCtrl, wx.gizmos.TreeListCtrl,
|
||||||
|
and wx.lib.customtree.CustomTreeCtrl. They can be used together or
|
||||||
|
separately.
|
||||||
|
|
||||||
|
Both mixins force the wx.TR_HIDE_ROOT style.
|
||||||
|
|
||||||
|
Frank Niessink <frank@niessink.com>
|
||||||
|
License: wxWidgets license
|
||||||
|
Version: 0.5
|
||||||
|
Date: 11 February 2007
|
||||||
|
'''
|
||||||
|
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class TreeAPIHarmonizer(object):
|
||||||
|
''' This class attempts to hide the differences in API between the
|
||||||
|
different tree controls currently supported. '''
|
||||||
|
|
||||||
|
def GetColumnCount(self):
|
||||||
|
# Only TreeListCtrl has columns, return 0 if we are mixed in
|
||||||
|
# with another tree control.
|
||||||
|
try:
|
||||||
|
return super(TreeAPIHarmonizer, self).GetColumnCount()
|
||||||
|
except AttributeError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def GetItemType(self, item):
|
||||||
|
# Only CustomTreeCtrl has different item types, return the
|
||||||
|
# default item type if we are mixed in with another tree control.
|
||||||
|
try:
|
||||||
|
return super(TreeAPIHarmonizer, self).GetItemType(item)
|
||||||
|
except AttributeError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetItemImage(self, item, imageIndex, which, column=0):
|
||||||
|
# The SetItemImage signature is different for TreeListCtrl and
|
||||||
|
# other tree controls. This adapter method hides the differences.
|
||||||
|
if self.GetColumnCount():
|
||||||
|
args = (item, imageIndex, column, which)
|
||||||
|
else:
|
||||||
|
args = (item, imageIndex, which)
|
||||||
|
super(TreeAPIHarmonizer, self).SetItemImage(*args)
|
||||||
|
|
||||||
|
def GetMainWindow(self):
|
||||||
|
# Only TreeListCtrl has a separate main window, return self if we are
|
||||||
|
# mixed in with another tree control.
|
||||||
|
try:
|
||||||
|
return super(TreeAPIHarmonizer, self).GetMainWindow()
|
||||||
|
except AttributeError:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def UnselectAll(self):
|
||||||
|
if self.GetWindowStyle() & wx.TR_MULTIPLE:
|
||||||
|
super(TreeAPIHarmonizer, self).UnselectAll()
|
||||||
|
else:
|
||||||
|
self.Unselect()
|
||||||
|
|
||||||
|
def GetSelections(self):
|
||||||
|
if self.GetWindowStyle() & wx.TR_MULTIPLE:
|
||||||
|
return super(TreeAPIHarmonizer, self).GetSelections()
|
||||||
|
else:
|
||||||
|
selection = self.GetSelection()
|
||||||
|
if selection:
|
||||||
|
return [selection]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def HitTest(self, *args, **kwargs):
|
||||||
|
# Only TreeListCtrl has columns, return 0 for the column if we are
|
||||||
|
# mixed in with another tree control.
|
||||||
|
hitTestResult = super(TreeAPIHarmonizer, self).HitTest(*args, **kwargs)
|
||||||
|
try:
|
||||||
|
item, flags, column = hitTestResult
|
||||||
|
except ValueError:
|
||||||
|
(item, flags), column = hitTestResult, 0
|
||||||
|
return item, flags, column
|
||||||
|
|
||||||
|
def GetItemChildren(self, item, recursively=False):
|
||||||
|
''' Return the children of item as a list. This method is not
|
||||||
|
part of the API of any tree control, but merely convenient to
|
||||||
|
have available. '''
|
||||||
|
children = []
|
||||||
|
child, cookie = self.GetFirstChild(item)
|
||||||
|
while child:
|
||||||
|
children.append(child)
|
||||||
|
if recursively:
|
||||||
|
children.extend(self.GetItemChildren(child, True))
|
||||||
|
child, cookie = self.GetNextChild(item, cookie)
|
||||||
|
return children
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualTree(TreeAPIHarmonizer):
|
||||||
|
''' This is a mixin class that can be used to allow for virtual tree
|
||||||
|
controls. It can be mixed in with wx.TreeCtrl, wx.gizmos.TreeListCtrl,
|
||||||
|
wx.lib.customtree.CustomTreeCtrl.
|
||||||
|
|
||||||
|
To use it derive a new class from this class and one of the tree
|
||||||
|
controls, e.g.:
|
||||||
|
class MyTree(VirtualTree, wx.TreeCtrl):
|
||||||
|
...
|
||||||
|
|
||||||
|
You *must* implement OnGetChildrenCount and OnGetItemText so that
|
||||||
|
this class knows how many items to draw and what text to use. To
|
||||||
|
also draw images, change colours, etc., override one or more of the
|
||||||
|
other OnGetXXX methods.
|
||||||
|
|
||||||
|
About specifying item indices: the VirtualTree uses various item
|
||||||
|
callbacks (such as OnGetItemText) to retrieve information needed
|
||||||
|
to display the items. To specify what item a callback needs
|
||||||
|
information about, the callback passes a list of indices. The first
|
||||||
|
index in the list is the index of the root item. If no more indices
|
||||||
|
are in the list (i.e. the length of the list of indices is exactly
|
||||||
|
1), the callback should return the information about that root item.
|
||||||
|
If the list contains two indices, the first index is the index of
|
||||||
|
the root item, and the second index is the index of the child of
|
||||||
|
that root item. For example, if OnGetItemText is called with
|
||||||
|
indices=[0,2], the callback should return information about that the
|
||||||
|
third child of the first root item. OnGetChildrenCount may also be
|
||||||
|
called with indices == [] to get the number of root items in the
|
||||||
|
tree.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
|
||||||
|
wx.TR_HIDE_ROOT
|
||||||
|
super(VirtualTree, self).__init__(*args, **kwargs)
|
||||||
|
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding)
|
||||||
|
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
|
||||||
|
|
||||||
|
def OnGetChildrenCount(self, indices):
|
||||||
|
''' This function must be overloaded in the derived class. It
|
||||||
|
should return the number of child items of the item with the
|
||||||
|
provided indices. If indices == [] it should return the number
|
||||||
|
of root items. '''
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def OnGetItemText(self, indices, column=0):
|
||||||
|
''' This function must be overloaded in the derived class. It
|
||||||
|
should return the string containing the text of the specified
|
||||||
|
item. '''
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def OnGetItemFont(self, indices):
|
||||||
|
''' This function may be overloaded in the derived class. It
|
||||||
|
should return the wx.Font to be used for the specified item. '''
|
||||||
|
return wx.NullFont
|
||||||
|
|
||||||
|
def OnGetItemTextColour(self, indices):
|
||||||
|
''' This function may be overloaded in the derived class. It
|
||||||
|
should return the wx.Colour to be used as text colour for the
|
||||||
|
specified item. '''
|
||||||
|
return wx.NullColour
|
||||||
|
|
||||||
|
def OnGetItemBackgroundColour(self, indices):
|
||||||
|
''' This function may be overloaded in the derived class. It
|
||||||
|
should return the wx.Colour to be used as background colour for
|
||||||
|
the specified item. '''
|
||||||
|
return wx.NullColour
|
||||||
|
|
||||||
|
def OnGetItemImage(self, indices, which=wx.TreeItemIcon_Normal, column=0):
|
||||||
|
''' This function may be overloaded in the derived class. It
|
||||||
|
should return the index of the image to be used. '''
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def OnGetItemType(self, indices):
|
||||||
|
''' This function may be overloaded in the derived class, but
|
||||||
|
that only makes sense when this class is mixed in with a tree
|
||||||
|
control that supports checkable items, e.g. CustomTreeCtrl.
|
||||||
|
This method should return whether the item is to be normal (0,
|
||||||
|
the default), a checkbox (1) or a radiobutton (2).
|
||||||
|
Note that OnGetItemChecked should return whether the item is
|
||||||
|
actually checked. '''
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def OnGetItemChecked(self, indices):
|
||||||
|
''' This function may be overloaded in the derived class, but
|
||||||
|
that only makes sense when this class is mixed in with a tree
|
||||||
|
control that supports checkable items, e.g. CustomTreeCtrl.
|
||||||
|
This method should return whether the item is to be checked.
|
||||||
|
Note that OnGetItemType should return 1 (checkbox) or 2
|
||||||
|
(radiobutton) for this item. '''
|
||||||
|
return False
|
||||||
|
|
||||||
|
def RefreshItems(self):
|
||||||
|
''' Redraws all visible items. '''
|
||||||
|
rootItem = self.GetRootItem()
|
||||||
|
if not rootItem:
|
||||||
|
rootItem = self.AddRoot('Hidden root')
|
||||||
|
self.RefreshChildrenRecursively(rootItem, [],
|
||||||
|
self.OnGetChildrenCount([]))
|
||||||
|
|
||||||
|
def RefreshChildrenRecursively(self, item, indices, childrenCount):
|
||||||
|
''' Refresh the children of item, reusing as much of the
|
||||||
|
existing items in the tree as possible. '''
|
||||||
|
existingChildren = self.GetItemChildren(item)
|
||||||
|
for childIndex in range(childrenCount):
|
||||||
|
childIndices = indices + [childIndex]
|
||||||
|
if childIndex < len(existingChildren):
|
||||||
|
child = existingChildren[childIndex]
|
||||||
|
else:
|
||||||
|
child = self.AppendItem(item, '')
|
||||||
|
self.RefreshRecursively(child, childIndices)
|
||||||
|
# Delete left-over items:
|
||||||
|
for existingChild in existingChildren[childrenCount:]:
|
||||||
|
self.Delete(existingChild)
|
||||||
|
|
||||||
|
def RefreshRecursively(self, item, indices):
|
||||||
|
childrenCount = self.OnGetChildrenCount(indices)
|
||||||
|
self.RefreshItem(item, indices, bool(childrenCount))
|
||||||
|
# We need to refresh the children when the item is expanded and
|
||||||
|
# when the item has no children, because in the latter case we
|
||||||
|
# might have to delete old children from the tree:
|
||||||
|
if self.IsExpanded(item) or not childrenCount:
|
||||||
|
self.RefreshChildrenRecursively(item, indices, childrenCount)
|
||||||
|
self.SetItemHasChildren(item, bool(childrenCount))
|
||||||
|
|
||||||
|
def RefreshItem(self, item, indices, hasChildren):
|
||||||
|
''' Refresh one item. '''
|
||||||
|
item = self.RefreshItemType(item, indices)
|
||||||
|
self.RefreshItemText(item, indices)
|
||||||
|
self.RefreshColumns(item, indices)
|
||||||
|
self.RefreshFont(item, indices)
|
||||||
|
self.RefreshTextColour(item, indices)
|
||||||
|
self.RefreshBackgroundColour(item, indices)
|
||||||
|
self.RefreshItemImage(item, indices, hasChildren)
|
||||||
|
self.RefreshCheckedState(item, indices)
|
||||||
|
|
||||||
|
def RefreshItemType(self, item, indices):
|
||||||
|
try:
|
||||||
|
currentType = self.GetItemType(item)
|
||||||
|
except AttributeError:
|
||||||
|
return item
|
||||||
|
type = self.OnGetItemType(indices)
|
||||||
|
if type != currentType:
|
||||||
|
# There's no way to change the type so we create a new item
|
||||||
|
# and delete the old one.
|
||||||
|
oldItem = item
|
||||||
|
item = self.InsertItem(self.GetItemParent(oldItem), item, '',
|
||||||
|
ct_type=type)
|
||||||
|
self.Delete(oldItem)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def RefreshItemText(self, item, indices):
|
||||||
|
itemText = self.OnGetItemText(indices)
|
||||||
|
if self.GetItemText(item) != itemText:
|
||||||
|
self.SetItemText(item, itemText)
|
||||||
|
|
||||||
|
def RefreshColumns(self, item, indices):
|
||||||
|
for columnIndex in range(1, self.GetColumnCount()):
|
||||||
|
itemText = self.OnGetItemText(indices, columnIndex)
|
||||||
|
if self.GetItemText(item, columnIndex) != itemText:
|
||||||
|
self.SetItemText(item, itemText, columnIndex)
|
||||||
|
|
||||||
|
def RefreshFont(self, item, indices):
|
||||||
|
font = self.OnGetItemFont(indices)
|
||||||
|
if self.GetItemFont(item) != font:
|
||||||
|
self.SetItemFont(item, font)
|
||||||
|
|
||||||
|
def RefreshTextColour(self, item, indices):
|
||||||
|
colour = self.OnGetItemTextColour(indices)
|
||||||
|
if self.GetItemTextColour(item) != colour:
|
||||||
|
self.SetItemTextColour(item, colour)
|
||||||
|
|
||||||
|
def RefreshBackgroundColour(self, item, indices):
|
||||||
|
# This one only seems to work for wx.TreeCtrl and not for
|
||||||
|
# wx.gizmos.TreeListCtrl and wx.lib.customtreectrl.CustomTreeCtrl...
|
||||||
|
colour = self.OnGetItemBackgroundColour(indices)
|
||||||
|
if self.GetItemBackgroundColour(item) != colour:
|
||||||
|
self.SetItemBackgroundColour(item, colour)
|
||||||
|
|
||||||
|
def RefreshItemImage(self, item, indices, hasChildren):
|
||||||
|
regularIcons = [wx.TreeItemIcon_Normal, wx.TreeItemIcon_Selected]
|
||||||
|
expandedIcons = [wx.TreeItemIcon_Expanded,
|
||||||
|
wx.TreeItemIcon_SelectedExpanded]
|
||||||
|
# Refresh images in first column:
|
||||||
|
for icon in regularIcons:
|
||||||
|
imageIndex = self.OnGetItemImage(indices, icon)
|
||||||
|
if self.GetItemImage(item, icon) != imageIndex:
|
||||||
|
self.SetItemImage(item, imageIndex, icon)
|
||||||
|
for icon in expandedIcons:
|
||||||
|
if hasChildren:
|
||||||
|
imageIndex = self.OnGetItemImage(indices, icon)
|
||||||
|
else:
|
||||||
|
imageIndex = -1
|
||||||
|
if self.GetItemImage(item, icon) != imageIndex or imageIndex == -1:
|
||||||
|
self.SetItemImage(item, imageIndex, icon)
|
||||||
|
# Refresh images in remaining columns, if any:
|
||||||
|
for columnIndex in range(1, self.GetColumnCount()):
|
||||||
|
for icon in regularIcons:
|
||||||
|
imageIndex = self.OnGetItemImage(indices, icon, columnIndex)
|
||||||
|
if self.GetItemImage(item, columnIndex, icon) != imageIndex:
|
||||||
|
self.SetItemImage(item, imageIndex, icon, columnIndex)
|
||||||
|
|
||||||
|
def RefreshCheckedState(self, item, indices):
|
||||||
|
try:
|
||||||
|
isChecked = self.IsItemChecked(item)
|
||||||
|
except AttributeError:
|
||||||
|
return # Checking not supported
|
||||||
|
shouldBeChecked = self.OnGetItemChecked(indices)
|
||||||
|
if isChecked != shouldBeChecked:
|
||||||
|
self.CheckItem(item, shouldBeChecked)
|
||||||
|
|
||||||
|
def OnItemExpanding(self, event):
|
||||||
|
indices = self.ItemIndices(event.GetItem())
|
||||||
|
childrenCount = self.OnGetChildrenCount(indices)
|
||||||
|
self.RefreshChildrenRecursively(event.GetItem(), indices, childrenCount)
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
def OnItemCollapsed(self, event):
|
||||||
|
parent = self.GetItemParent(event.GetItem())
|
||||||
|
if not parent:
|
||||||
|
parent = self.GetRootItem()
|
||||||
|
indices = self.ItemIndices(parent)
|
||||||
|
childrenCount = self.OnGetChildrenCount(indices)
|
||||||
|
self.RefreshChildrenRecursively(parent, indices, childrenCount)
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
def ItemIndices(self, item):
|
||||||
|
''' Construct index list for item. '''
|
||||||
|
parent = self.GetItemParent(item)
|
||||||
|
if parent:
|
||||||
|
parentIndices = self.ItemIndices(parent)
|
||||||
|
ownIndex = self.GetItemChildren(parent).index(item)
|
||||||
|
return parentIndices + [ownIndex]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class DragAndDrop(TreeAPIHarmonizer):
|
||||||
|
''' This is a mixin class that can be used to easily implement
|
||||||
|
dragging and dropping of tree items. It can be mixed in with
|
||||||
|
wx.TreeCtrl, wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
|
||||||
|
|
||||||
|
To use it derive a new class from this class and one of the tree
|
||||||
|
controls, e.g.:
|
||||||
|
class MyTree(DragAndDrop, wx.TreeCtrl):
|
||||||
|
...
|
||||||
|
|
||||||
|
You *must* implement OnDrop. OnDrop is called when the user has
|
||||||
|
dropped an item on top of another item. It's up to you to decide how
|
||||||
|
to handle the drop. If you are using this mixin together with the
|
||||||
|
VirtualTree mixin, it makes sense to rearrange your underlying data
|
||||||
|
and then call RefreshItems to let de virtual tree refresh itself.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
|
||||||
|
wx.TR_HIDE_ROOT
|
||||||
|
super(DragAndDrop, self).__init__(*args, **kwargs)
|
||||||
|
self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
|
||||||
|
|
||||||
|
def OnDrop(self, dropItem, dragItem):
|
||||||
|
''' This function must be overloaded in the derived class.
|
||||||
|
dragItem is the item being dragged by the user. dropItem is the
|
||||||
|
item dragItem is dropped upon. If the user doesn't drop dragItem
|
||||||
|
on another item, dropItem equals the (hidden) root item of the
|
||||||
|
tree control. '''
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def OnBeginDrag(self, event):
|
||||||
|
# We allow only one item to be dragged at a time, to keep it simple
|
||||||
|
self._dragItem = self.GetSelections()[0]
|
||||||
|
if self._dragItem:
|
||||||
|
self.StartDragging()
|
||||||
|
event.Allow()
|
||||||
|
else:
|
||||||
|
event.Veto()
|
||||||
|
|
||||||
|
def OnEndDrag(self, event):
|
||||||
|
self.StopDragging()
|
||||||
|
dropTarget = event.GetItem()
|
||||||
|
if not dropTarget:
|
||||||
|
dropTarget = self.GetRootItem()
|
||||||
|
if self.IsValidDropTarget(dropTarget):
|
||||||
|
self.UnselectAll()
|
||||||
|
if dropTarget != self.GetRootItem():
|
||||||
|
self.SelectItem(dropTarget)
|
||||||
|
self.OnDrop(dropTarget, self._dragItem)
|
||||||
|
|
||||||
|
def OnDragging(self, event):
|
||||||
|
if not event.Dragging():
|
||||||
|
self.StopDragging()
|
||||||
|
return
|
||||||
|
item, flags, column = self.HitTest(wx.Point(event.GetX(), event.GetY()))
|
||||||
|
if not item:
|
||||||
|
item = self.GetRootItem()
|
||||||
|
if self.IsValidDropTarget(item):
|
||||||
|
self.SetCursorToDragging()
|
||||||
|
else:
|
||||||
|
self.SetCursorToDroppingImpossible()
|
||||||
|
if flags & wx.TREE_HITTEST_ONITEMBUTTON:
|
||||||
|
self.Expand(item)
|
||||||
|
selections = self.GetSelections()
|
||||||
|
if selections != [item]:
|
||||||
|
self.UnselectAll()
|
||||||
|
if item != self.GetRootItem():
|
||||||
|
self.SelectItem(item)
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
def StartDragging(self):
|
||||||
|
self.GetMainWindow().Bind(wx.EVT_MOTION, self.OnDragging)
|
||||||
|
self.Bind(wx.EVT_TREE_END_DRAG, self.OnEndDrag)
|
||||||
|
self.SetCursorToDragging()
|
||||||
|
|
||||||
|
def StopDragging(self):
|
||||||
|
self.GetMainWindow().Unbind(wx.EVT_MOTION)
|
||||||
|
self.Unbind(wx.EVT_TREE_END_DRAG)
|
||||||
|
self.ResetCursor()
|
||||||
|
self.UnselectAll()
|
||||||
|
self.SelectItem(self._dragItem)
|
||||||
|
|
||||||
|
def SetCursorToDragging(self):
|
||||||
|
self.GetMainWindow().SetCursor(wx.StockCursor(wx.CURSOR_HAND))
|
||||||
|
|
||||||
|
def SetCursorToDroppingImpossible(self):
|
||||||
|
self.GetMainWindow().SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
|
||||||
|
|
||||||
|
def ResetCursor(self):
|
||||||
|
self.GetMainWindow().SetCursor(wx.NullCursor)
|
||||||
|
|
||||||
|
def IsValidDropTarget(self, dropTarget):
|
||||||
|
if dropTarget:
|
||||||
|
allChildren = self.GetItemChildren(self._dragItem, recursively=True)
|
||||||
|
parent = self.GetItemParent(self._dragItem)
|
||||||
|
return dropTarget not in [self._dragItem, parent] + allChildren
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user