From 5f662a84ef9a074929a419955e0926e73aa4355c Mon Sep 17 00:00:00 2001 From: Gyedo Jeon Date: Thu, 4 Mar 2010 18:36:16 +0000 Subject: [PATCH] Added popup menu to sceneGraphUI --- direct/src/leveleditor/ActionMgr.py | 73 +++++ direct/src/leveleditor/ObjectMgr.py | 14 + direct/src/leveleditor/SceneGraphUI.py | 282 +----------------- direct/src/leveleditor/SceneGraphUIBase.py | 321 +++++++++++++++++++++ 4 files changed, 413 insertions(+), 277 deletions(-) create mode 100755 direct/src/leveleditor/SceneGraphUIBase.py diff --git a/direct/src/leveleditor/ActionMgr.py b/direct/src/leveleditor/ActionMgr.py index 04901cfd7d..94ec5621f4 100755 --- a/direct/src/leveleditor/ActionMgr.py +++ b/direct/src/leveleditor/ActionMgr.py @@ -173,6 +173,79 @@ class ActionDeleteObj(ActionBase): self.hierarchy = {} self.objInfos = {} +class ActionDeleteObjById(ActionBase): + """ Action class for deleting object """ + + def __init__(self, editor, uid): + self.editor = editor + function = self.editor.objectMgr.removeObjectById + self.uid = uid + ActionBase.__init__(self, function, self.uid) + self.hierarchy = {} + self.objInfos = {} + self.objTransforms = {} + + def saveStatus(self): + def saveObjStatus(uid_np, isUID=False): + if isUID: + obj = self.editor.objectMgr.findObjectById(uid_np) + else: + obj = self.editor.objectMgr.findObjectByNodePath(uid_np) + if obj: + uid = obj[OG.OBJ_UID] + objNP = obj[OG.OBJ_NP] + self.objInfos[uid] = obj + self.objTransforms[uid] = objNP.getMat() + parentNP = objNP.getParent() + if parentNP == render: + self.hierarchy[uid] = None + else: + parentObj = self.editor.objectMgr.findObjectByNodePath(parentNP) + if parentObj: + self.hierarchy[uid] = parentObj[OG.OBJ_UID] + + for child in objNP.getChildren(): + if child.hasTag('OBJRoot'): + saveObjStatus(child) + + saveObjStatus(self.uid, True) + + def undo(self): + if len(self.hierarchy.keys()) == 0 or\ + len(self.objInfos.keys()) == 0: + print "Can't undo this deletion" + else: + print "Undo: deleteObjectById" + def restoreObject(uid, parentNP): + obj = self.objInfos[uid] + objDef = obj[OG.OBJ_DEF] + objModel = obj[OG.OBJ_MODEL] + objProp = obj[OG.OBJ_PROP] + objRGBA = obj[OG.OBJ_RGBA] + objNP = self.editor.objectMgr.addNewObject(objDef.name, + uid, + obj[OG.OBJ_MODEL], + parentNP) + self.editor.objectMgr.updateObjectColor(objRGBA[0], objRGBA[1], objRGBA[2], objRGBA[3], uid) + self.editor.objectMgr.updateObjectProperties(uid, objProp) + objNP.setMat(self.objTransforms[uid]) + + while (len(self.hierarchy.keys()) > 0): + for uid in self.hierarchy.keys(): + if self.hierarchy[uid] is None: + parentNP = None + restoreObject(uid, parentNP) + del self.hierarchy[uid] + else: + parentObj = self.editor.objectMgr.findObjectById(self.hierarchy[uid]) + if parentObj: + parentNP = parentObj[OG.OBJ_NP] + restoreObject(uid, parentNP) + del self.hierarchy[uid] + + self.hierarchy = {} + self.objInfos = {} + class ActionChangeHierarchy(ActionBase): """ Action class for changing Scene Graph Hierarchy """ diff --git a/direct/src/leveleditor/ObjectMgr.py b/direct/src/leveleditor/ObjectMgr.py index a6c588294a..bce9c6f527 100755 --- a/direct/src/leveleditor/ObjectMgr.py +++ b/direct/src/leveleditor/ObjectMgr.py @@ -153,6 +153,20 @@ class ObjectMgr: self.editor.fNeedToSave = True return newobj + def removeObjectById(self, uid): + obj = self.findObjectById(uid) + nodePath = obj[OG.OBJ_NP] + del self.objects[uid] + del self.npIndex[nodePath] + + # remove children also + for child in nodePath.getChildren(): + if child.hasTag('OBJRoot'): + self.removeObjectByNodePath(child) + nodePath.remove() + + self.editor.fNeedToSave = True + def removeObjectByNodePath(self, nodePath): uid = self.npIndex.get(nodePath) if uid: diff --git a/direct/src/leveleditor/SceneGraphUI.py b/direct/src/leveleditor/SceneGraphUI.py index f99a4c4c76..7555d07045 100755 --- a/direct/src/leveleditor/SceneGraphUI.py +++ b/direct/src/leveleditor/SceneGraphUI.py @@ -1,283 +1,11 @@ """ Defines Scene Graph tree UI """ -import wx -import cPickle as pickle -from pandac.PandaModules import * -from ActionMgr import * - -import ObjectGlobals as OG - -class SceneGraphUIDropTarget(wx.TextDropTarget): - def __init__(self, editor): - print "in SceneGraphUIDropTarget::init..." - wx.TextDropTarget.__init__(self) - self.editor = editor - - def OnDropText(self, x, y, text): - print "in SceneGraphUIDropTarget::OnDropText..." - self.editor.ui.sceneGraphUI.changeHierarchy(text, x, y) +from SceneGraphUIBase import * -class SceneGraphUI(wx.Panel): +class SceneGraphUI(SceneGraphUIBase): def __init__(self, parent, editor): - wx.Panel.__init__(self, parent) + SceneGraphUIBase.__init__(self, parent, editor) - self.editor = editor - self.tree = wx.TreeCtrl(self, id=-1, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE, - validator=wx.DefaultValidator, name="treeCtrl") - self.root = self.tree.AddRoot('render') - self.tree.SetItemPyData(self.root, "render") - - self.shouldShowPandaObjChildren = False - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.tree, 1, wx.EXPAND, 0) - self.SetSizer(sizer); self.Layout() - - parentSizer = wx.BoxSizer(wx.VERTICAL) - parentSizer.Add(self, 1, wx.EXPAND, 0) - parent.SetSizer(parentSizer); parent.Layout() - - parent.SetDropTarget(SceneGraphUIDropTarget(self.editor)) - - self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onSelected) - self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.onBeginDrag) - - def reset(self): - #import pdb;set_trace() - itemList = list() - item, cookie = self.tree.GetFirstChild(self.root) - while item: - itemList.append(item) - item, cookie = self.tree.GetNextChild(self.root, cookie) - - for item in itemList: - self.tree.Delete(item) - - def traversePandaObjects(self, parent, objNodePath): - itemId = self.tree.GetItemPyData(parent) - i = 0 - for child in objNodePath.getChildren(): - namestr = "%s.%s"%(child.node().getType(), child.node().getName()) - newItem = self.tree.PrependItem(parent, namestr) - newItemId = "%s.%s"%(itemId, i) - self.tree.SetItemPyData(newItem, newItemId) - - # recursing... - self.traversePandaObjects(newItem, child) - i = i + 1 - - def addPandaObjectChildren(self, parent): - # first, find Panda Object's NodePath of the item - itemId = self.tree.GetItemPyData(parent) - if itemId == "render": - return - obj = self.editor.objectMgr.findObjectById(itemId) - if obj is None: - return - - objNodePath = obj[OG.OBJ_NP] - self.traversePandaObjects(parent, objNodePath) - - item, cookie = self.tree.GetFirstChild(parent) - while item: - # recursing... - self.addPandaObjectChildren(item) - item, cookie = self.tree.GetNextChild(parent, cookie) - - def removePandaObjectChildren(self, parent): - # first, find Panda Object's NodePath of the item - itemId = self.tree.GetItemPyData(parent) - if itemId == "render": - return - obj = self.editor.objectMgr.findObjectById(itemId) - if obj is None: - self.tree.Delete(parent) - return - item, cookie = self.tree.GetFirstChild(parent) - while item: - # recurse... - itemToRemove = item - # continue iteration to the next child - item, cookie = self.tree.GetNextChild(parent, cookie) - self.removePandaObjectChildren(itemToRemove) - - def add(self, item): - #import pdb;pdb.set_trace() - if item is None: - return - obj = self.editor.objectMgr.findObjectByNodePath(NodePath(item)) - if obj is None: - return - - parentNodePath = obj[OG.OBJ_NP].getParent() - parentObj = self.editor.objectMgr.findObjectByNodePath(parentNodePath) - - #import pdb;pdb.set_trace() - if parentObj is None: - parent = self.root - else: - parent = self.traverse(self.root, parentObj[OG.OBJ_UID]) - - namestr = "%s_%s"%(obj[OG.OBJ_DEF].name, obj[OG.OBJ_UID]) - newItem = self.tree.AppendItem(parent, namestr) - self.tree.SetItemPyData(newItem, obj[OG.OBJ_UID]) - - # adding children of PandaObj - if self.shouldShowPandaObjChildren: - self.addPandaObjectChildren(newItem) - self.tree.Expand(self.root) - - def traverse(self, parent, itemId): - # prevent from traversing into self - if itemId == self.tree.GetItemPyData(parent): - return None - - # main loop - serching for an item with an itemId - item, cookie = self.tree.GetFirstChild(parent) - while item: - # if the item was found - return it - if itemId == self.tree.GetItemPyData(item): - return item - - # the tem was not found - checking if it has children - if self.tree.ItemHasChildren(item): - # item has children - delving into it - child = self.traverse(item, itemId) - if child is not None: - return child - - # continue iteration to the next child - item, cookie = self.tree.GetNextChild(parent, cookie) - return None - - def reParentTree(self, parent, newParent): - # main loop - iterating over item's children - item, cookie = self.tree.GetFirstChild(parent) - while item: - data = self.tree.GetItemText(item) - itemId = self.tree.GetItemPyData(item) - newItem = self.tree.AppendItem(newParent, data) - self.tree.SetItemPyData(newItem, itemId) - - # if an item had children, we need to re-parent them as well - if self.tree.ItemHasChildren(item): - # recursing... - self.reParentTree(item, newItem) - - # continue iteration to the next child - item, cookie = self.tree.GetNextChild(parent, cookie) - - def reParentData(self, parent, child): - child.wrtReparentTo(parent) - - def reParent(self, oldParent, newParent, child): - if newParent is None: - newParent = self.root - itemId = self.tree.GetItemPyData(oldParent) - newItem = self.tree.AppendItem(newParent, child) - self.tree.SetItemPyData(newItem, itemId) - self.reParentTree(oldParent, newItem) - - obj = self.editor.objectMgr.findObjectById(itemId) - itemId = self.tree.GetItemPyData(newParent) - if itemId != "render": - newParentObj = self.editor.objectMgr.findObjectById(itemId) - self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP]) - else: - self.reParentData(render, obj[OG.OBJ_NP]) - - self.tree.Delete(oldParent) - if self.shouldShowPandaObjChildren: - self.removePandaObjectChildren(oldParent) - self.addPandaObjectChildren(oldParent) - self.removePandaObjectChildren(newParent) - self.addPandaObjectChildren(newpParent) - - def isChildOrGrandChild(self, parent, child): - childId = self.tree.GetItemPyData(child) - return self.traverse(parent, childId) - - def changeHierarchy(self, data, x, y): - itemText = data.split('_') - itemId = itemText[-1] # uid is the last token - item = self.traverse(self.tree.GetRootItem(), itemId) - if item is None: - return - - dragToItem, flags = self.tree.HitTest(wx.Point(x, y)) - if dragToItem.IsOk(): - # prevent draging into itself - if dragToItem == item: - return - if self.isChildOrGrandChild(item, dragToItem): - return - - # undo function setup... - action = ActionChangeHierarchy(self.editor, self.tree.GetItemPyData(self.tree.GetItemParent(item)), self.tree.GetItemPyData(item), self.tree.GetItemPyData(dragToItem), data) - self.editor.actionMgr.push(action) - action() - - def parent(self, oldParentId, newParentId, childName): - oldParent = self.traverse(self.tree.GetRootItem(), oldParentId) - newParent = self.traverse(self.tree.GetRootItem(), newParentId) - self.reParent(oldParent, newParent, childName) - - def showPandaObjectChildren(self): - itemList = list() - self.shouldShowPandaObjChildren = not self.shouldShowPandaObjChildren - - item, cookie = self.tree.GetFirstChild(self.root) - while item: - itemList.append(item) - item, cookie = self.tree.GetNextChild(self.root, cookie) - - #import pdb;set_trace() - for item in itemList: - if self.shouldShowPandaObjChildren: - self.addPandaObjectChildren(item) - else: - self.removePandaObjectChildren(item) - # continue iteration to the next child - - def delete(self, itemId): - item = self.traverse(self.root, itemId) - if item: - self.tree.Delete(item) - - def select(self, itemId): - item = self.traverse(self.root, itemId) - if item: - if not self.tree.IsSelected(item): - self.tree.SelectItem(item) - - def deSelect(self, itemId): - item = self.traverse(self.root, itemId) - if item is not None: - self.tree.UnselectItem(item) - - def onSelected(self, event): - item = event.GetItem(); - if item: - itemId = self.tree.GetItemPyData(item) - if itemId: - obj = self.editor.objectMgr.findObjectById(itemId); - if obj: - selections = self.tree.GetSelections() - if len(selections) > 1: - base.direct.select(obj[OG.OBJ_NP], fMultiSelect = 1, fLEPane = 0) - else: - base.direct.select(obj[OG.OBJ_NP], fMultiSelect = 0, fLEPane = 0) - - def onBeginDrag(self, event): - item = event.GetItem() - - if item != self.tree.GetRootItem(): # prevent dragging root item - text = self.tree.GetItemText(item) - print "Starting SceneGraphUI drag'n'drop with %s..." % repr(text) - - tdo = wx.TextDataObject(text) - tds = wx.DropSource(self.tree) - tds.SetData(tdo) - tds.DoDragDrop(True) + def populateExtraMenu(self): + pass diff --git a/direct/src/leveleditor/SceneGraphUIBase.py b/direct/src/leveleditor/SceneGraphUIBase.py new file mode 100755 index 0000000000..35d8887b25 --- /dev/null +++ b/direct/src/leveleditor/SceneGraphUIBase.py @@ -0,0 +1,321 @@ +""" +Defines Scene Graph tree UI Base +""" +import wx +import cPickle as pickle +from pandac.PandaModules import * +from ActionMgr import * + +import ObjectGlobals as OG + +class SceneGraphUIDropTarget(wx.TextDropTarget): + def __init__(self, editor): + print "in SceneGraphUIDropTarget::init..." + wx.TextDropTarget.__init__(self) + self.editor = editor + + def OnDropText(self, x, y, text): + print "in SceneGraphUIDropTarget::OnDropText..." + self.editor.ui.sceneGraphUI.changeHierarchy(text, x, y) + +class SceneGraphUIBase(wx.Panel): + def __init__(self, parent, editor): + wx.Panel.__init__(self, parent) + + self.editor = editor + self.tree = wx.TreeCtrl(self, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE, + validator=wx.DefaultValidator, name="treeCtrl") + self.root = self.tree.AddRoot('render') + self.tree.SetItemPyData(self.root, "render") + + self.shouldShowPandaObjChildren = False + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.tree, 1, wx.EXPAND, 0) + self.SetSizer(sizer); self.Layout() + + parentSizer = wx.BoxSizer(wx.VERTICAL) + parentSizer.Add(self, 1, wx.EXPAND, 0) + parent.SetSizer(parentSizer); parent.Layout() + + parent.SetDropTarget(SceneGraphUIDropTarget(self.editor)) + + self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onSelected) + self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.onBeginDrag) + + self.currObj = None + self.menu = wx.Menu() + self.populateMenu() + self.Bind(wx.EVT_CONTEXT_MENU, self.onShowPopup) + + def reset(self): + #import pdb;set_trace() + itemList = list() + item, cookie = self.tree.GetFirstChild(self.root) + while item: + itemList.append(item) + item, cookie = self.tree.GetNextChild(self.root, cookie) + + for item in itemList: + self.tree.Delete(item) + + def traversePandaObjects(self, parent, objNodePath): + itemId = self.tree.GetItemPyData(parent) + i = 0 + for child in objNodePath.getChildren(): + namestr = "%s.%s"%(child.node().getType(), child.node().getName()) + newItem = self.tree.PrependItem(parent, namestr) + newItemId = "%s.%s"%(itemId, i) + self.tree.SetItemPyData(newItem, newItemId) + + # recursing... + self.traversePandaObjects(newItem, child) + i = i + 1 + + def addPandaObjectChildren(self, parent): + # first, find Panda Object's NodePath of the item + itemId = self.tree.GetItemPyData(parent) + if itemId == "render": + return + obj = self.editor.objectMgr.findObjectById(itemId) + if obj is None: + return + + objNodePath = obj[OG.OBJ_NP] + self.traversePandaObjects(parent, objNodePath) + + item, cookie = self.tree.GetFirstChild(parent) + while item: + # recursing... + self.addPandaObjectChildren(item) + item, cookie = self.tree.GetNextChild(parent, cookie) + + def removePandaObjectChildren(self, parent): + # first, find Panda Object's NodePath of the item + itemId = self.tree.GetItemPyData(parent) + if itemId == "render": + return + obj = self.editor.objectMgr.findObjectById(itemId) + if obj is None: + self.tree.Delete(parent) + return + item, cookie = self.tree.GetFirstChild(parent) + while item: + # recurse... + itemToRemove = item + # continue iteration to the next child + item, cookie = self.tree.GetNextChild(parent, cookie) + self.removePandaObjectChildren(itemToRemove) + + def add(self, item): + #import pdb;pdb.set_trace() + if item is None: + return + obj = self.editor.objectMgr.findObjectByNodePath(NodePath(item)) + if obj is None: + return + + parentNodePath = obj[OG.OBJ_NP].getParent() + parentObj = self.editor.objectMgr.findObjectByNodePath(parentNodePath) + + #import pdb;pdb.set_trace() + if parentObj is None: + parent = self.root + else: + parent = self.traverse(self.root, parentObj[OG.OBJ_UID]) + + namestr = "%s_%s"%(obj[OG.OBJ_DEF].name, obj[OG.OBJ_UID]) + newItem = self.tree.AppendItem(parent, namestr) + self.tree.SetItemPyData(newItem, obj[OG.OBJ_UID]) + + # adding children of PandaObj + if self.shouldShowPandaObjChildren: + self.addPandaObjectChildren(newItem) + self.tree.Expand(self.root) + + def traverse(self, parent, itemId): + # prevent from traversing into self + if itemId == self.tree.GetItemPyData(parent): + return None + + # main loop - serching for an item with an itemId + item, cookie = self.tree.GetFirstChild(parent) + while item: + # if the item was found - return it + if itemId == self.tree.GetItemPyData(item): + return item + + # the tem was not found - checking if it has children + if self.tree.ItemHasChildren(item): + # item has children - delving into it + child = self.traverse(item, itemId) + if child is not None: + return child + + # continue iteration to the next child + item, cookie = self.tree.GetNextChild(parent, cookie) + return None + + def reParentTree(self, parent, newParent): + # main loop - iterating over item's children + item, cookie = self.tree.GetFirstChild(parent) + while item: + data = self.tree.GetItemText(item) + itemId = self.tree.GetItemPyData(item) + newItem = self.tree.AppendItem(newParent, data) + self.tree.SetItemPyData(newItem, itemId) + + # if an item had children, we need to re-parent them as well + if self.tree.ItemHasChildren(item): + # recursing... + self.reParentTree(item, newItem) + + # continue iteration to the next child + item, cookie = self.tree.GetNextChild(parent, cookie) + + def reParentData(self, parent, child): + child.wrtReparentTo(parent) + + def reParent(self, oldParent, newParent, child): + if newParent is None: + newParent = self.root + itemId = self.tree.GetItemPyData(oldParent) + newItem = self.tree.AppendItem(newParent, child) + self.tree.SetItemPyData(newItem, itemId) + self.reParentTree(oldParent, newItem) + + obj = self.editor.objectMgr.findObjectById(itemId) + itemId = self.tree.GetItemPyData(newParent) + if itemId != "render": + newParentObj = self.editor.objectMgr.findObjectById(itemId) + self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP]) + else: + self.reParentData(render, obj[OG.OBJ_NP]) + + self.tree.Delete(oldParent) + if self.shouldShowPandaObjChildren: + self.removePandaObjectChildren(oldParent) + self.addPandaObjectChildren(oldParent) + self.removePandaObjectChildren(newParent) + self.addPandaObjectChildren(newpParent) + + def isChildOrGrandChild(self, parent, child): + childId = self.tree.GetItemPyData(child) + return self.traverse(parent, childId) + + def changeHierarchy(self, data, x, y): + itemText = data.split('_') + itemId = itemText[-1] # uid is the last token + item = self.traverse(self.tree.GetRootItem(), itemId) + if item is None: + return + + dragToItem, flags = self.tree.HitTest(wx.Point(x, y)) + if dragToItem.IsOk(): + # prevent draging into itself + if dragToItem == item: + return + if self.isChildOrGrandChild(item, dragToItem): + return + + # undo function setup... + action = ActionChangeHierarchy(self.editor, self.tree.GetItemPyData(self.tree.GetItemParent(item)), self.tree.GetItemPyData(item), self.tree.GetItemPyData(dragToItem), data) + self.editor.actionMgr.push(action) + action() + + def parent(self, oldParentId, newParentId, childName): + oldParent = self.traverse(self.tree.GetRootItem(), oldParentId) + newParent = self.traverse(self.tree.GetRootItem(), newParentId) + self.reParent(oldParent, newParent, childName) + + def showPandaObjectChildren(self): + itemList = list() + self.shouldShowPandaObjChildren = not self.shouldShowPandaObjChildren + + item, cookie = self.tree.GetFirstChild(self.root) + while item: + itemList.append(item) + item, cookie = self.tree.GetNextChild(self.root, cookie) + + #import pdb;set_trace() + for item in itemList: + if self.shouldShowPandaObjChildren: + self.addPandaObjectChildren(item) + else: + self.removePandaObjectChildren(item) + # continue iteration to the next child + + def delete(self, itemId): + item = self.traverse(self.root, itemId) + if item: + self.tree.Delete(item) + + def select(self, itemId): + item = self.traverse(self.root, itemId) + if item: + if not self.tree.IsSelected(item): + self.tree.SelectItem(item) + + def deSelect(self, itemId): + item = self.traverse(self.root, itemId) + if item is not None: + self.tree.UnselectItem(item) + + def onSelected(self, event): + item = event.GetItem(); + if item: + itemId = self.tree.GetItemPyData(item) + if itemId: + obj = self.editor.objectMgr.findObjectById(itemId); + if obj: + selections = self.tree.GetSelections() + if len(selections) > 1: + base.direct.select(obj[OG.OBJ_NP], fMultiSelect = 1, fLEPane = 0) + else: + base.direct.select(obj[OG.OBJ_NP], fMultiSelect = 0, fLEPane = 0) + + def onBeginDrag(self, event): + item = event.GetItem() + + if item != self.tree.GetRootItem(): # prevent dragging root item + text = self.tree.GetItemText(item) + print "Starting SceneGraphUI drag'n'drop with %s..." % repr(text) + + tdo = wx.TextDataObject(text) + tds = wx.DropSource(self.tree) + tds.SetData(tdo) + tds.DoDragDrop(True) + + def onShowPopup(self, event): + pos = event.GetPosition() + pos = self.ScreenToClient(pos) + + item, flags = self.tree.HitTest(pos) + if not item.IsOk(): + return + itemId = self.tree.GetItemPyData(item) + if not itemId: + return + self.currObj = self.editor.objectMgr.findObjectById(itemId); + if self.currObj: + self.PopupMenu(self.menu, pos) + + def populateMenu(self): + menuitem = self.menu.Append(-1, 'Delete') + self.Bind(wx.EVT_MENU, self.onDelete, menuitem) + self.populateExtraMenu() + + def populateExtraMenu(self): + # You should implement this in subclass + raise NotImplementedError('populateExtraMenu() must be implemented in subclass') + + def onDelete(self, evt=None): + if self.currObj is None: + return + + uid = self.currObj[OG.OBJ_UID] + action = ActionDeleteObjById(self.editor, uid) + self.editor.actionMgr.push(action) + action() + self.delete(uid)