""" 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.currItem = None 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(): if child.hasTag('OBJRoot'): # since they are already shown in scene graph tree continue 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, parentNP = None): #import pdb;pdb.set_trace() if item is None: return obj = self.editor.objectMgr.findObjectByNodePath(NodePath(item)) if obj is None: return if parentNP is None : parentNP = obj[OG.OBJ_NP].getParent() parentObj = self.editor.objectMgr.findObjectByNodePath(parentNP) if parentObj is None: parent = self.root else: parent = self.traverse(self.root, parentObj[OG.OBJ_UID]) name = NodePath(item).getName() if not name: name = ' ' namestr = "%s_%s_%s"%(obj[OG.OBJ_DEF].name, 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) self.tree.EnsureVisible(item) def changeLabel(self, itemId, newName): item = self.traverse(self.root, itemId) if item: obj = self.editor.objectMgr.findObjectById(itemId) if obj is None: return obj[OG.OBJ_NP].setName(newName) namestr = "%s_%s_%s"%(obj[OG.OBJ_DEF].name, newName, obj[OG.OBJ_UID]) self.tree.SetItemText(item, namestr) 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 self.currItem = item 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, 'Expand All') self.Bind(wx.EVT_MENU, self.onExpandAllChildren, menuitem) menuitem = self.menu.Append(-1, 'Collapse All') self.Bind(wx.EVT_MENU, self.onCollapseAllChildren, menuitem) menuitem = self.menu.Append(-1, 'Delete') self.Bind(wx.EVT_MENU, self.onDelete, menuitem) menuitem = self.menu.Append(-1, 'Rename') self.Bind(wx.EVT_MENU, self.onRename, menuitem) self.populateExtraMenu() def populateExtraMenu(self): # You should implement this in subclass raise NotImplementedError('populateExtraMenu() must be implemented in subclass') def onCollapseAllChildren(self, evt=None): if self.currItem: self.tree.CollapseAllChildren(self.currItem) def onExpandAllChildren(self, evt=None): if self.currItem: self.tree.ExpandAllChildren(self.currItem) 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) def onRename(self, evt=None): if self.currObj is None: return self.editor.ui.bindKeyEvents(False) dialog = wx.TextEntryDialog(None, '', 'Input new name', defaultValue=self.currObj[OG.OBJ_NP].getName()) if dialog.ShowModal() == wx.ID_OK: newName = dialog.GetValue() dialog.Destroy() self.editor.ui.bindKeyEvents(True) self.currObj[OG.OBJ_NP].setName(newName) self.changeLabel(self.currObj[OG.OBJ_UID], newName)