Added undo for selection and object transform

This commit is contained in:
Gyedo Jeon 2010-02-06 02:24:34 +00:00
parent c3b70b120d
commit fd7bf04577
5 changed files with 190 additions and 43 deletions

View File

@ -1,24 +1,6 @@
from pandac.PandaModules import *
import ObjectGlobals as OG
class ActionBase(Functor):
""" Base class for user actions """
def __init__(self, function, *args, **kargs):
Functor.__init__(self, function, *args, **kargs)
self.result = None
def _do__call__(self, *args, **kargs):
self.saveStatus()
self.result = Functor._do__call__(self, *args, **kargs)
return self.result
def saveStatus(self):
# save object status for undo here
pass
def undo(self):
print "undo method is not defined for this action"
class ActionMgr:
def __init__(self):
self.undoList = []
@ -35,6 +17,7 @@ class ActionMgr:
def push(self, action):
self.undoList.append(action)
print 'current undoList', self.undoList
def undo(self):
if len(self.undoList) < 1:
@ -43,6 +26,7 @@ class ActionMgr:
action = self.undoList.pop()
self.redoList.append(action)
action.undo()
print 'current redoList', self.redoList
def redo(self):
if len(self.redoList) < 1:
@ -50,7 +34,31 @@ class ActionMgr:
else:
action = self.redoList.pop()
self.undoList.append(action)
action()
action.redo()
print 'current undoList', self.undoList
class ActionBase(Functor):
""" Base class for user actions """
def __init__(self, function, *args, **kargs):
Functor.__init__(self, function, *args, **kargs)
self.result = None
def _do__call__(self, *args, **kargs):
self.saveStatus()
self.result = Functor._do__call__(self, *args, **kargs)
return self.result
def redo(self):
self.result = self._do__call__()
return self.result
def saveStatus(self):
# save object status for undo here
pass
def undo(self):
print "undo method is not defined for this action"
class ActionAddNewObj(ActionBase):
""" Action class for adding new object """
@ -59,11 +67,23 @@ class ActionAddNewObj(ActionBase):
self.editor = editor
function = self.editor.objectMgr.addNewObject
ActionBase.__init__(self, function, *args, **kargs)
self.uid = None
def redo(self):
if self.uid is None:
print "Can't redo this add"
else:
self.result = self._do__call__(uid=self.uid)
return self.result
def undo(self):
if self.result is None:
print "Can't undo this"
print "Can't undo this add"
else:
print "Undo: addNewObject"
obj = self.editor.objectMgr.findObjectByNodePath(self.result)
self.uid = obj[OG.OBJ_UID]
self.editor.ui.sceneGraphUI.delete(self.uid)
base.direct.deselect(self.result)
base.direct.removeNodePath(self.result)
self.result = None
@ -78,6 +98,7 @@ class ActionDeleteObj(ActionBase):
self.selectedUIDs = []
self.hierarchy = {}
self.objInfos = {}
self.objTransforms = {}
def saveStatus(self):
selectedNPs = base.direct.selected.getSelectedAsList()
@ -89,6 +110,7 @@ class ActionDeleteObj(ActionBase):
self.selectedUIDs.append(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
@ -107,10 +129,12 @@ class ActionDeleteObj(ActionBase):
def undo(self):
if len(self.hierarchy.keys()) == 0 or\
len(self.objInfos.keys()) == 0:
print "Can't undo this"
print "Can't undo this deletion"
else:
print "Undo: deleteObject"
def restoreObject(uid, parentNP):
obj = self.objInfos[uid]
objNP = obj[OG.OBJ_NP]
objDef = obj[OG.OBJ_DEF]
objModel = obj[OG.OBJ_MODEL]
objProp = obj[OG.OBJ_PROP]
@ -121,6 +145,7 @@ class ActionDeleteObj(ActionBase):
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():
@ -135,12 +160,98 @@ class ActionDeleteObj(ActionBase):
restoreObject(uid, parentNP)
del self.hierarchy[uid]
base.direct.deselectAll()
base.direct.deselectAllCB()
for uid in self.selectedUIDs:
obj = self.editor.objectMgr.findObjectById(uid)
if obj:
base.direct.select(obj[OG.OBJ_NP], fMultiSelect=1)
self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
self.selecteUIDs = []
self.hierarchy = {}
self.objInfos = {}
class ActionSelectObj(ActionBase):
""" Action class for adding new object """
def __init__(self, editor, *args, **kargs):
self.editor = editor
function = base.direct.selectCB
ActionBase.__init__(self, function, *args, **kargs)
self.selectedUIDs = []
def saveStatus(self):
selectedNPs = base.direct.selected.getSelectedAsList()
for np in selectedNPs:
obj = self.editor.objectMgr.findObjectByNodePath(np)
if obj:
uid = obj[OG.OBJ_UID]
self.selectedUIDs.append(uid)
def undo(self):
print "Undo : selectObject"
base.direct.deselectAllCB()
for uid in self.selectedUIDs:
obj = self.editor.objectMgr.findObjectById(uid)
if obj:
self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
self.selectedUIDs = []
class ActionTransformObj(ActionBase):
""" Action class for object transformation """
def __init__(self, editor, *args, **kargs):
self.editor = editor
function = self.editor.objectMgr.setObjectTransform
ActionBase.__init__(self, function, *args, **kargs)
self.uid = args[0]
#self.xformMat = Mat4(args[1])
self.origMat = None
def saveStatus(self):
obj = self.editor.objectMgr.findObjectById(self.uid)
if obj:
self.origMat = Mat4(obj[OG.OBJ_NP].getMat())
def redo(self):
if self.uid is None:
print "Can't redo this add"
else:
self.result = self._do__call__()#uid=self.uid, xformMat=self.xformMat)
return self.result
def undo(self):
if self.origMat is None:
print "Can't undo this transform"
else:
print "Undo: transformObject"
obj = self.editor.objectMgr.findObjectById(self.uid)
if obj:
obj[OG.OBJ_NP].setMat(self.origMat)
del self.origMat
self.origMat = None
class ActionDeselectAll(ActionBase):
""" Action class for adding new object """
def __init__(self, editor, *args, **kargs):
self.editor = editor
function = base.direct.deselectAllCB
ActionBase.__init__(self, function, *args, **kargs)
self.selectedUIDs = []
def saveStatus(self):
selectedNPs = base.direct.selected.getSelectedAsList()
for np in selectedNPs:
obj = self.editor.objectMgr.findObjectByNodePath(np)
if obj:
uid = obj[OG.OBJ_UID]
self.selectedUIDs.append(uid)
def undo(self):
print "Undo : deselectAll"
base.direct.deselectAllCB()
for uid in self.selectedUIDs:
obj = self.editor.objectMgr.findObjectById(uid)
if obj:
self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
self.selectedUIDs = []

View File

@ -34,7 +34,7 @@ class LevelEditorBase(DirectObject):
def initialize(self):
""" You should call this in your __init__ method of inherited LevelEditor class """
fTk = base.config.GetBool('want-tk', 0)
fTk = 0
fWx = 0
base.startDirect(fWantTk = fTk, fWantWx = fWx)
base.closeWindow(base.win)
@ -97,8 +97,10 @@ class LevelEditorBase(DirectObject):
widget.setBin('gui-popup', 0)
widget.setDepthTest(0)
# [gjeon] to handle delete here first
# [gjeon] to intercept messages here
base.direct.ignore('DIRECT-delete')
base.direct.ignore('DIRECT-select')
base.direct.ignore('DIRECT-preDeselectAll')
# [gjeon] do not use the old way of finding current DR
base.direct.drList.tryToGetCurrentDr = False
@ -108,10 +110,13 @@ class LevelEditorBase(DirectObject):
self.actionEvents.extend([
# Node path events
('DIRECT-select', self.select),
('DIRECT-delete', self.handleDelete),
('DIRECT-preDeselectAll', self.deselectAll),
('DIRECT_deselectAll', self.deselectAllCB),
('preRemoveNodePath', self.removeNodePathHook),
('DIRECT_deselectedNodePath', self.deselectAllCB),
('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', self.selectedNodePathHook),
('DIRECT_deselectedNodePath', self.deselectAll),
('DIRECT_deselectAll', self.deselectAll),
('LE-Undo', self.actionMgr.undo),
('LE-Redo', self.actionMgr.redo),
@ -165,6 +170,20 @@ class LevelEditorBase(DirectObject):
## coa = Vec3(row[0], row[1], row[2])
## base.direct.cameraControl.updateCoa(coa)
def select(self, nodePath, fMultiSelect=0, fSelectTag=1, fResetAncestry=1, fLEPane=0, fUndo=1):
if fUndo:
# Select tagged object if present
if fSelectTag:
for tag in base.direct.selected.tagList:
if nodePath.hasNetTag(tag):
nodePath = nodePath.findNetTag(tag)
break
action = ActionSelectObj(self, nodePath, fMultiSelect)
self.actionMgr.push(action)
action()
else:
base.direct.selectCB(nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo)
def selectedNodePathHook(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fLEPane = 0):
# handle unpickable nodepath
if nodePath.getName() in base.direct.iRay.unpickable:
@ -180,6 +199,13 @@ class LevelEditorBase(DirectObject):
self.objectMgr.selectObject(nodePath, fLEPane)
def deselectAll(self, np=None):
if len(base.direct.selected.getSelectedAsList()) ==0:
return
action = ActionDeselectAll(self)
self.actionMgr.push(action)
action()
def deselectAllCB(self, dnp=None):
self.objectMgr.deselectAll()
def reset(self):

View File

@ -46,7 +46,7 @@ class ObjectHandler:
def updateSmiley(self, val, obj):
objNP = obj[OG.OBJ_NP]
if base.direct:
base.direct.deselectAll()
base.direct.deselectAllCB()
for child in objNP.findAllMatches("+GeomNode"):
child.removeNode()

View File

@ -7,7 +7,7 @@ import os, time, wx
from direct.task import Task
from direct.actor.Actor import Actor
from pandac.PandaModules import *
from ActionMgr import *
import ObjectGlobals as OG
class ObjectMgr:
@ -26,7 +26,7 @@ class ObjectMgr:
self.currNodePath = None
def reset(self):
self.deselectAll()
self.deselectAllCB()
for id in self.objects.keys():
self.objects[id][OG.OBJ_NP].removeNode()
@ -130,8 +130,6 @@ class ObjectMgr:
if uid is None:
uid = self.genUniqueId()
else:
fSelectObject = False
# populate obj data using default values
properties = {}
@ -144,7 +142,7 @@ class ObjectMgr:
if self.editor:
if fSelectObject:
base.direct.select(newobj)
self.editor.select(newobj, fUndo=0)
self.editor.ui.sceneGraphUI.add(newobj)
return newobj
@ -249,7 +247,7 @@ class ObjectMgr:
if self.currNodePath is None:
return
np = self.currNodePath
np = hidden.attachNewNode('temp')
np.setX(float(self.editor.ui.objectPropertyUI.propX.getValue()))
np.setY(float(self.editor.ui.objectPropertyUI.propY.getValue()))
np.setZ(float(self.editor.ui.objectPropertyUI.propZ.getValue()))
@ -282,6 +280,18 @@ class ObjectMgr:
np.setSx(float(self.editor.ui.objectPropertyUI.propSX.getValue()))
np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue()))
np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue()))
obj = self.findObjectByNodePath(self.currNodePath)
action = ActionTransformObj(self.editor, obj[OG.OBJ_UID], Mat4(np.getMat()))
self.editor.actionMgr.push(action)
np.remove()
action()
def setObjectTransform(self, uid, xformMat):
obj = self.findObjectById(uid)
if obj:
print obj[OG.OBJ_NP], xformMat
obj[OG.OBJ_NP].setMat(xformMat)
def updateObjectColor(self, r, g, b, a, np=None):
if np is None:
@ -300,7 +310,7 @@ class ObjectMgr:
def updateObjectModel(self, model, obj, fSelectObject=True):
""" replace object's model """
if obj[OG.OBJ_MODEL] != model:
base.direct.deselectAll()
base.direct.deselectAllCB()
objNP = obj[OG.OBJ_NP]
objRGBA = obj[OG.OBJ_RGBA]
@ -333,12 +343,12 @@ class ObjectMgr:
self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID]
if fSelectObject:
base.direct.select(newobj)
base.direct.select(newobj, fUndo=0)
def updateObjectAnim(self, anim, obj, fSelectObject=True):
""" replace object's anim """
if obj[OG.OBJ_ANIM] != anim:
base.direct.deselectAll()
base.direct.deselectAllCB()
objNP = obj[OG.OBJ_NP]
# load new anim
@ -347,7 +357,7 @@ class ObjectMgr:
objNP.loop(animName)
obj[OG.OBJ_ANIM] = anim
if fSelectObject:
base.direct.select(objNP)
base.direct.select(objNP, fUndo=0)
def updateObjectModelFromUI(self, event, obj):
""" replace object's model with one selected from UI """
@ -463,7 +473,7 @@ class ObjectMgr:
func(**kwargs)
if self.editor and fSelectObject:
base.direct.select(obj[OG.OBJ_NP])
base.direct.select(obj[OG.OBJ_NP], fUndo=0)
def updateObjectProperties(self, nodePath, propValues):
"""
@ -510,7 +520,7 @@ class ObjectMgr:
else:
animStr = "None"
self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s)"%(uid, objDef.name, uid, modelStr, parentStr, animStr))
self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, fSelectObject=False)"%(uid, objDef.name, uid, modelStr, parentStr, animStr))
self.saveData.append("if objects['%s']:"%uid)
self.saveData.append(" objects['%s'].setPos(%s)"%(uid, np.getPos()))
self.saveData.append(" objects['%s'].setHpr(%s)"%(uid, np.getHpr()))
@ -575,7 +585,7 @@ class ObjectMgr:
self.duplicateChild(nodePath, newObjNP)
duplicatedNPs.append(newObjNP)
base.direct.deselectAll()
base.direct.deselectAllCB()
print duplicatedNPs
for newNodePath in duplicatedNPs:
base.direct.select(newNodePath, fMultiSelect = 1)
base.direct.select(newNodePath, fMultiSelect = 1, fUndo=0)

View File

@ -72,7 +72,7 @@ class ObjectPaletteUI(wx.Panel):
def onSelected(self, event):
pass
def onBeginDrag(self, event):
item = event.GetItem()