""" DIRECT Nine DoF Manipulation Panel """ __all__ = ['Placer', 'place'] # Import Tkinter, Pmw, and the dial code from this directory tree. from panda3d.core import * from direct.showbase.TkGlobal import * from direct.tkwidgets.AppShell import * from direct.tkwidgets import Dial from direct.tkwidgets import Floater from direct.directtools.DirectGlobals import ZERO_VEC, UNIT_VEC import Pmw """ TODO: Task to monitor pose """ class Placer(AppShell): # Override class variables here appname = 'Placer Panel' frameWidth = 625 frameHeight = 215 usecommandarea = 0 usestatusarea = 0 def __init__(self, parent = None, **kw): INITOPT = Pmw.INITOPT optiondefs = ( ('title', self.appname, None), ('nodePath', base.direct.camera, None), ) self.defineoptions(kw, optiondefs) # Call superclass initialization function AppShell.__init__(self) self.initialiseoptions(Placer) def appInit(self): # Initialize state self.tempCS = base.direct.group.attachNewNode('placerTempCS') self.orbitFromCS = base.direct.group.attachNewNode( 'placerOrbitFromCS') self.orbitToCS = base.direct.group.attachNewNode('placerOrbitToCS') self.refCS = self.tempCS # Dictionary keeping track of all node paths manipulated so far self.nodePathDict = {} self.nodePathDict['camera'] = base.direct.camera self.nodePathDict['widget'] = base.direct.widget self.nodePathNames = ['camera', 'widget', 'selected'] self.refNodePathDict = {} self.refNodePathDict['parent'] = self['nodePath'].getParent() self.refNodePathDict['render'] = render self.refNodePathDict['camera'] = base.direct.camera self.refNodePathDict['widget'] = base.direct.widget self.refNodePathNames = ['parent', 'self', 'render', 'camera', 'widget', 'selected'] # Initial state self.initPos = Vec3(0) self.initHpr = Vec3(0) self.initScale = Vec3(1) self.deltaHpr = Vec3(0) # Offset for orbital mode self.posOffset = Vec3(0) # Set up event hooks self.undoEvents = [('DIRECT_undo', self.undoHook), ('DIRECT_pushUndo', self.pushUndoHook), ('DIRECT_undoListEmpty', self.undoListEmptyHook), ('DIRECT_redo', self.redoHook), ('DIRECT_pushRedo', self.pushRedoHook), ('DIRECT_redoListEmpty', self.redoListEmptyHook)] for event, method in self.undoEvents: self.accept(event, method) # Init movement mode self.movementMode = 'Relative To:' def createInterface(self): # The interior of the toplevel panel interior = self.interior() interior['relief'] = FLAT # Add placer commands to menubar self.menuBar.addmenu('Placer', 'Placer Panel Operations') self.menuBar.addmenuitem('Placer', 'command', 'Zero Node Path', label = 'Zero All', command = self.zeroAll) self.menuBar.addmenuitem('Placer', 'command', 'Reset Node Path', label = 'Reset All', command = self.resetAll) self.menuBar.addmenuitem('Placer', 'command', 'Print Node Path Info', label = 'Print Info', command = self.printNodePathInfo) self.menuBar.addmenuitem( 'Placer', 'command', 'Toggle widget visability', label = 'Toggle Widget Vis', command = base.direct.toggleWidgetVis) self.menuBar.addmenuitem( 'Placer', 'command', 'Toggle widget manipulation mode', label = 'Toggle Widget Mode', command = base.direct.manipulationControl.toggleObjectHandlesMode) # Get a handle to the menu frame menuFrame = self.menuFrame self.nodePathMenu = Pmw.ComboBox( menuFrame, labelpos = W, label_text = 'Node Path:', entry_width = 20, selectioncommand = self.selectNodePathNamed, scrolledlist_items = self.nodePathNames) self.nodePathMenu.selectitem('selected') self.nodePathMenuEntry = ( self.nodePathMenu.component('entryfield_entry')) self.nodePathMenuBG = ( self.nodePathMenuEntry.configure('background')[3]) self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1) self.bind(self.nodePathMenu, 'Select node path to manipulate') modeMenu = Pmw.OptionMenu(menuFrame, items = ('Relative To:', 'Orbit:'), initialitem = 'Relative To:', command = self.setMovementMode, menubutton_width = 8) modeMenu.pack(side = 'left', expand = 0) self.bind(modeMenu, 'Select manipulation mode') self.refNodePathMenu = Pmw.ComboBox( menuFrame, entry_width = 16, selectioncommand = self.selectRefNodePathNamed, scrolledlist_items = self.refNodePathNames) self.refNodePathMenu.selectitem('parent') self.refNodePathMenuEntry = ( self.refNodePathMenu.component('entryfield_entry')) self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1) self.bind(self.refNodePathMenu, 'Select relative node path') self.undoButton = Button(menuFrame, text = 'Undo', command = base.direct.undo) if base.direct.undoList: self.undoButton['state'] = 'normal' else: self.undoButton['state'] = 'disabled' self.undoButton.pack(side = 'left', expand = 0) self.bind(self.undoButton, 'Undo last operation') self.redoButton = Button(menuFrame, text = 'Redo', command = base.direct.redo) if base.direct.redoList: self.redoButton['state'] = 'normal' else: self.redoButton['state'] = 'disabled' self.redoButton.pack(side = 'left', expand = 0) self.bind(self.redoButton, 'Redo last operation') # Create and pack the Pos Controls posGroup = Pmw.Group(interior, tag_pyclass = Menubutton, tag_text = 'Position', tag_font=('MSSansSerif', 14), tag_activebackground = '#909090', ring_relief = RIDGE) posMenubutton = posGroup.component('tag') self.bind(posMenubutton, 'Position menu operations') posMenu = Menu(posMenubutton, tearoff = 0) posMenu.add_command(label = 'Set to zero', command = self.zeroPos) posMenu.add_command(label = 'Reset initial', command = self.resetPos) posMenubutton['menu'] = posMenu posGroup.pack(side='left', fill = 'both', expand = 1) posInterior = posGroup.interior() # Create the dials self.posX = self.createcomponent('posX', (), None, Floater.Floater, (posInterior,), text = 'X', relief = FLAT, value = 0.0, label_foreground = 'Red') self.posX['commandData'] = ['x'] self.posX['preCallback'] = self.xformStart self.posX['postCallback'] = self.xformStop self.posX['callbackData'] = ['x'] self.posX.pack(expand=1, fill='both') self.posY = self.createcomponent('posY', (), None, Floater.Floater, (posInterior,), text = 'Y', relief = FLAT, value = 0.0, label_foreground = '#00A000') self.posY['commandData'] = ['y'] self.posY['preCallback'] = self.xformStart self.posY['postCallback'] = self.xformStop self.posY['callbackData'] = ['y'] self.posY.pack(expand=1, fill='both') self.posZ = self.createcomponent('posZ', (), None, Floater.Floater, (posInterior,), text = 'Z', relief = FLAT, value = 0.0, label_foreground = 'Blue') self.posZ['commandData'] = ['z'] self.posZ['preCallback'] = self.xformStart self.posZ['postCallback'] = self.xformStop self.posZ['callbackData'] = ['z'] self.posZ.pack(expand=1, fill='both') # Create and pack the Hpr Controls hprGroup = Pmw.Group(interior, tag_pyclass = Menubutton, tag_text = 'Orientation', tag_font=('MSSansSerif', 14), tag_activebackground = '#909090', ring_relief = RIDGE) hprMenubutton = hprGroup.component('tag') self.bind(hprMenubutton, 'Orientation menu operations') hprMenu = Menu(hprMenubutton, tearoff = 0) hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr) hprMenu.add_command(label = 'Reset initial', command = self.resetHpr) hprMenubutton['menu'] = hprMenu hprGroup.pack(side='left', fill = 'both', expand = 1) hprInterior = hprGroup.interior() # Create the dials self.hprH = self.createcomponent('hprH', (), None, Dial.AngleDial, (hprInterior,), style = 'mini', text = 'H', value = 0.0, relief = FLAT, label_foreground = 'blue') self.hprH['commandData'] = ['h'] self.hprH['preCallback'] = self.xformStart self.hprH['postCallback'] = self.xformStop self.hprH['callbackData'] = ['h'] self.hprH.pack(expand=1, fill='both') self.hprP = self.createcomponent('hprP', (), None, Dial.AngleDial, (hprInterior,), style = 'mini', text = 'P', value = 0.0, relief = FLAT, label_foreground = 'red') self.hprP['commandData'] = ['p'] self.hprP['preCallback'] = self.xformStart self.hprP['postCallback'] = self.xformStop self.hprP['callbackData'] = ['p'] self.hprP.pack(expand=1, fill='both') self.hprR = self.createcomponent('hprR', (), None, Dial.AngleDial, (hprInterior,), style = 'mini', text = 'R', value = 0.0, relief = FLAT, label_foreground = '#00A000') self.hprR['commandData'] = ['r'] self.hprR['preCallback'] = self.xformStart self.hprR['postCallback'] = self.xformStop self.hprR['callbackData'] = ['r'] self.hprR.pack(expand=1, fill='both') # Create and pack the Scale Controls # The available scaling modes self.scalingMode = StringVar() self.scalingMode.set('Scale Uniform') # The scaling widgets scaleGroup = Pmw.Group(interior, tag_text = 'Scale Uniform', tag_pyclass = Menubutton, tag_font=('MSSansSerif', 14), tag_activebackground = '#909090', ring_relief = RIDGE) self.scaleMenubutton = scaleGroup.component('tag') self.bind(self.scaleMenubutton, 'Scale menu operations') self.scaleMenubutton['textvariable'] = self.scalingMode # Scaling menu scaleMenu = Menu(self.scaleMenubutton, tearoff = 0) scaleMenu.add_command(label = 'Set to unity', command = self.unitScale) scaleMenu.add_command(label = 'Reset initial', command = self.resetScale) scaleMenu.add_radiobutton(label = 'Scale Free', variable = self.scalingMode) scaleMenu.add_radiobutton(label = 'Scale Uniform', variable = self.scalingMode) scaleMenu.add_radiobutton(label = 'Scale Proportional', variable = self.scalingMode) self.scaleMenubutton['menu'] = scaleMenu # Pack group widgets scaleGroup.pack(side='left', fill = 'both', expand = 1) scaleInterior = scaleGroup.interior() # Create the dials self.scaleX = self.createcomponent('scaleX', (), None, Floater.Floater, (scaleInterior,), text = 'X Scale', relief = FLAT, min = 0.0001, value = 1.0, resetValue = 1.0, label_foreground = 'Red') self.scaleX['commandData'] = ['sx'] self.scaleX['callbackData'] = ['sx'] self.scaleX['preCallback'] = self.xformStart self.scaleX['postCallback'] = self.xformStop self.scaleX.pack(expand=1, fill='both') self.scaleY = self.createcomponent('scaleY', (), None, Floater.Floater, (scaleInterior,), text = 'Y Scale', relief = FLAT, min = 0.0001, value = 1.0, resetValue = 1.0, label_foreground = '#00A000') self.scaleY['commandData'] = ['sy'] self.scaleY['callbackData'] = ['sy'] self.scaleY['preCallback'] = self.xformStart self.scaleY['postCallback'] = self.xformStop self.scaleY.pack(expand=1, fill='both') self.scaleZ = self.createcomponent('scaleZ', (), None, Floater.Floater, (scaleInterior,), text = 'Z Scale', relief = FLAT, min = 0.0001, value = 1.0, resetValue = 1.0, label_foreground = 'Blue') self.scaleZ['commandData'] = ['sz'] self.scaleZ['callbackData'] = ['sz'] self.scaleZ['preCallback'] = self.xformStart self.scaleZ['postCallback'] = self.xformStop self.scaleZ.pack(expand=1, fill='both') # Make sure appropriate labels are showing self.setMovementMode('Relative To:') # Set up placer for inital node path self.selectNodePathNamed('init') self.selectRefNodePathNamed('parent') # Update place to reflect initial state self.updatePlacer() # Now that you're done setting up, attach commands self.posX['command'] = self.xform self.posY['command'] = self.xform self.posZ['command'] = self.xform self.hprH['command'] = self.xform self.hprP['command'] = self.xform self.hprR['command'] = self.xform self.scaleX['command'] = self.xform self.scaleY['command'] = self.xform self.scaleZ['command'] = self.xform ### WIDGET OPERATIONS ### def setMovementMode(self, movementMode): # Set prefix namePrefix = '' self.movementMode = movementMode if (movementMode == 'Relative To:'): namePrefix = 'Relative ' elif (movementMode == 'Orbit:'): namePrefix = 'Orbit ' # Update pos widgets self.posX['text'] = namePrefix + 'X' self.posY['text'] = namePrefix + 'Y' self.posZ['text'] = namePrefix + 'Z' # Update hpr widgets if (movementMode == 'Orbit:'): namePrefix = 'Orbit delta ' self.hprH['text'] = namePrefix + 'H' self.hprP['text'] = namePrefix + 'P' self.hprR['text'] = namePrefix + 'R' # Update temp cs and initialize widgets self.updatePlacer() def setScalingMode(self): if self['nodePath']: scale = self['nodePath'].getScale() if ((scale[0] != scale[1]) or (scale[0] != scale[2]) or (scale[1] != scale[2])): self.scalingMode.set('Scale Free') def selectNodePathNamed(self, name): nodePath = None if name == 'init': nodePath = self['nodePath'] # Add Combo box entry for the initial node path self.addNodePath(nodePath) elif name == 'selected': nodePath = base.direct.selected.last # Add Combo box entry for this selected object self.addNodePath(nodePath) else: nodePath = self.nodePathDict.get(name, None) if (nodePath == None): # See if this evaluates into a node path try: nodePath = eval(name) if isinstance(nodePath, NodePath): self.addNodePath(nodePath) else: # Good eval but not a node path, give up nodePath = None except: # Bogus eval nodePath = None # Clear bogus entry from listbox listbox = self.nodePathMenu.component('scrolledlist') listbox.setlist(self.nodePathNames) else: if name == 'widget': # Record relationship between selected nodes and widget base.direct.selected.getWrtAll() # Update active node path self.setActiveNodePath(nodePath) def setActiveNodePath(self, nodePath): self['nodePath'] = nodePath if self['nodePath']: self.nodePathMenuEntry.configure( background = self.nodePathMenuBG) # Check to see if node path and ref node path are the same if ((self.refCS != None) and (self.refCS == self['nodePath'])): # Yes they are, use temp CS as ref # This calls updatePlacer self.setReferenceNodePath(self.tempCS) # update listbox accordingly self.refNodePathMenu.selectitem('parent') else: # Record initial value and initialize the widgets self.updatePlacer() # Record initial position self.updateResetValues(self['nodePath']) # Set scaling mode based on node path's current scale self.setScalingMode() else: # Flash entry self.nodePathMenuEntry.configure(background = 'Pink') def selectRefNodePathNamed(self, name): nodePath = None if name == 'self': nodePath = self.tempCS elif name == 'selected': nodePath = base.direct.selected.last # Add Combo box entry for this selected object self.addRefNodePath(nodePath) elif name == 'parent': nodePath = self['nodePath'].getParent() else: nodePath = self.refNodePathDict.get(name, None) if (nodePath == None): # See if this evaluates into a node path try: nodePath = eval(name) if isinstance(nodePath, NodePath): self.addRefNodePath(nodePath) else: # Good eval but not a node path, give up nodePath = None except: # Bogus eval nodePath = None # Clear bogus entry from listbox listbox = self.refNodePathMenu.component('scrolledlist') listbox.setlist(self.refNodePathNames) # Check to see if node path and ref node path are the same if (nodePath != None) and (nodePath == self['nodePath']): # Yes they are, use temp CS and update listbox accordingly nodePath = self.tempCS self.refNodePathMenu.selectitem('parent') # Update ref node path self.setReferenceNodePath(nodePath) def setReferenceNodePath(self, nodePath): self.refCS = nodePath if self.refCS: self.refNodePathMenuEntry.configure( background = self.nodePathMenuBG) # Update placer to reflect new state self.updatePlacer() else: # Flash entry self.refNodePathMenuEntry.configure(background = 'Pink') def addNodePath(self, nodePath): self.addNodePathToDict(nodePath, self.nodePathNames, self.nodePathMenu, self.nodePathDict) def addRefNodePath(self, nodePath): self.addNodePathToDict(nodePath, self.refNodePathNames, self.refNodePathMenu, self.refNodePathDict) def addNodePathToDict(self, nodePath, names, menu, dict): if not nodePath: return # Get node path's name name = nodePath.getName() if name in ['parent', 'render', 'camera']: dictName = name else: # Generate a unique name for the dict dictName = name + '-' + repr(hash(nodePath)) if dictName not in dict: # Update combo box to include new item names.append(dictName) listbox = menu.component('scrolledlist') listbox.setlist(names) # Add new item to dictionary dict[dictName] = nodePath menu.selectitem(dictName) def updatePlacer(self): pos = Vec3(0) hpr = Vec3(0) scale = Vec3(1) np = self['nodePath'] if (np != None) and isinstance(np, NodePath): # Update temp CS self.updateAuxiliaryCoordinateSystems() # Update widgets if self.movementMode == 'Orbit:': pos.assign(self.posOffset) hpr.assign(ZERO_VEC) scale.assign(np.getScale()) elif self.refCS: pos.assign(np.getPos(self.refCS)) hpr.assign(np.getHpr(self.refCS)) scale.assign(np.getScale()) self.updatePosWidgets(pos) self.updateHprWidgets(hpr) self.updateScaleWidgets(scale) def updateAuxiliaryCoordinateSystems(self): # Temp CS self.tempCS.setPosHpr(self['nodePath'], 0, 0, 0, 0, 0, 0) # Orbit CS # At reference self.orbitFromCS.setPos(self.refCS, 0, 0, 0) # But aligned with target self.orbitFromCS.setHpr(self['nodePath'], 0, 0, 0) # Also update to CS self.orbitToCS.setPosHpr(self.orbitFromCS, 0, 0, 0, 0, 0, 0) # Get offset from origin self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS)) ### NODE PATH TRANSFORMATION OPERATIONS ### def xform(self, value, axis): if axis in ['sx', 'sy', 'sz']: self.xformScale(value, axis) elif self.movementMode == 'Relative To:': self.xformRelative(value, axis) elif self.movementMode == 'Orbit:': self.xformOrbit(value, axis) if self.nodePathMenu.get() == 'widget': if base.direct.manipulationControl.fSetCoa: # Update coa based on current widget position base.direct.selected.last.mCoa2Dnp.assign( base.direct.widget.getMat(base.direct.selected.last)) else: # Move the objects with the widget base.direct.selected.moveWrtWidgetAll() def xformStart(self, data): # Record undo point self.pushUndo() # If moving widget kill follow task and update wrts if self.nodePathMenu.get() == 'widget': taskMgr.remove('followSelectedNodePath') # Record relationship between selected nodes and widget base.direct.selected.getWrtAll() # Record initial state self.deltaHpr = self['nodePath'].getHpr(self.refCS) # Update placer to reflect new state self.updatePlacer() def xformStop(self, data): # Throw event to signal manipulation done # Send nodepath as a list messenger.send('DIRECT_manipulateObjectCleanup', [[self['nodePath']]]) # Update placer to reflect new state self.updatePlacer() # If moving widget restart follow task if self.nodePathMenu.get() == 'widget': # Restart followSelectedNodePath task base.direct.manipulationControl.spawnFollowSelectedNodePathTask() def xformRelative(self, value, axis): nodePath = self['nodePath'] if (nodePath != None) and (self.refCS != None): if axis == 'x': nodePath.setX(self.refCS, value) elif axis == 'y': nodePath.setY(self.refCS, value) elif axis == 'z': nodePath.setZ(self.refCS, value) else: if axis == 'h': self.deltaHpr.setX(value) elif axis == 'p': self.deltaHpr.setY(value) elif axis == 'r': self.deltaHpr.setZ(value) # Put node path at new hpr nodePath.setHpr(self.refCS, self.deltaHpr) def xformOrbit(self, value, axis): nodePath = self['nodePath'] if ((nodePath != None) and (self.refCS != None) and (self.orbitFromCS != None) and (self.orbitToCS != None)): if axis == 'x': self.posOffset.setX(value) elif axis == 'y': self.posOffset.setY(value) elif axis == 'z': self.posOffset.setZ(value) elif axis == 'h': self.orbitToCS.setH(self.orbitFromCS, value) elif axis == 'p': self.orbitToCS.setP(self.orbitFromCS, value) elif axis == 'r': self.orbitToCS.setR(self.orbitFromCS, value) nodePath.setPosHpr(self.orbitToCS, self.posOffset, ZERO_VEC) def xformScale(self, value, axis): if self['nodePath']: mode = self.scalingMode.get() scale = self['nodePath'].getScale() if mode == 'Scale Free': if axis == 'sx': scale.setX(value) elif axis == 'sy': scale.setY(value) elif axis == 'sz': scale.setZ(value) elif mode == 'Scale Uniform': scale.set(value, value, value) elif mode == 'Scale Proportional': if axis == 'sx': sf = value/scale[0] elif axis == 'sy': sf = value/scale[1] elif axis == 'sz': sf = value/scale[2] scale = scale * sf self['nodePath'].setScale(scale) def updatePosWidgets(self, pos): self.posX.set(pos[0]) self.posY.set(pos[1]) self.posZ.set(pos[2]) def updateHprWidgets(self, hpr): self.hprH.set(hpr[0]) self.hprP.set(hpr[1]) self.hprR.set(hpr[2]) def updateScaleWidgets(self, scale): self.scaleX.set(scale[0]) self.scaleY.set(scale[1]) self.scaleZ.set(scale[2]) def zeroAll(self): self.xformStart(None) self.updatePosWidgets(ZERO_VEC) self.updateHprWidgets(ZERO_VEC) self.updateScaleWidgets(UNIT_VEC) self.xformStop(None) def zeroPos(self): self.xformStart(None) self.updatePosWidgets(ZERO_VEC) self.xformStop(None) def zeroHpr(self): self.xformStart(None) self.updateHprWidgets(ZERO_VEC) self.xformStop(None) def unitScale(self): self.xformStart(None) self.updateScaleWidgets(UNIT_VEC) self.xformStop(None) def updateResetValues(self, nodePath): self.initPos.assign(nodePath.getPos()) self.posX['resetValue'] = self.initPos[0] self.posY['resetValue'] = self.initPos[1] self.posZ['resetValue'] = self.initPos[2] self.initHpr.assign(nodePath.getHpr()) self.hprH['resetValue'] = self.initHpr[0] self.hprP['resetValue'] = self.initHpr[1] self.hprR['resetValue'] = self.initHpr[2] self.initScale.assign(nodePath.getScale()) self.scaleX['resetValue'] = self.initScale[0] self.scaleY['resetValue'] = self.initScale[1] self.scaleZ['resetValue'] = self.initScale[2] def resetAll(self): if self['nodePath']: self.xformStart(None) self['nodePath'].setPosHprScale( self.initPos, self.initHpr, self.initScale) self.xformStop(None) def resetPos(self): if self['nodePath']: self.xformStart(None) self['nodePath'].setPos(self.initPos) self.xformStop(None) def resetHpr(self): if self['nodePath']: self.xformStart(None) self['nodePath'].setHpr(self.initHpr) self.xformStop(None) def resetScale(self): if self['nodePath']: self.xformStart(None) self['nodePath'].setScale(self.initScale) self.xformStop(None) def pushUndo(self, fResetRedo = 1): base.direct.pushUndo([self['nodePath']]) def undoHook(self, nodePathList = []): # Reflect new changes self.updatePlacer() def pushUndoHook(self): # Make sure button is reactivated self.undoButton.configure(state = 'normal') def undoListEmptyHook(self): # Make sure button is deactivated self.undoButton.configure(state = 'disabled') def pushRedo(self): base.direct.pushRedo([self['nodePath']]) def redoHook(self, nodePathList = []): # Reflect new changes self.updatePlacer() def pushRedoHook(self): # Make sure button is reactivated self.redoButton.configure(state = 'normal') def redoListEmptyHook(self): # Make sure button is deactivated self.redoButton.configure(state = 'disabled') def printNodePathInfo(self): np = self['nodePath'] if np: name = np.getName() pos = np.getPos() hpr = np.getHpr() scale = np.getScale() posString = '%.2f, %.2f, %.2f' % (pos[0], pos[1], pos[2]) hprString = '%.2f, %.2f, %.2f' % (hpr[0], hpr[1], hpr[2]) scaleString = '%.2f, %.2f, %.2f' % (scale[0], scale[1], scale[2]) print('NodePath: %s' % name) print('Pos: %s' % posString) print('Hpr: %s' % hprString) print('Scale: %s' % scaleString) print(('%s.setPosHprScale(%s, %s, %s)' % (name, posString, hprString, scaleString))) def onDestroy(self, event): # Remove hooks for event, method in self.undoEvents: self.ignore(event) self.tempCS.removeNode() self.orbitFromCS.removeNode() self.orbitToCS.removeNode() def place(nodePath): return Placer(nodePath = nodePath) ###################################################################### # Create demo in root window for testing. if __name__ == '__main__': root = Pmw.initialise() widget = Placer()