Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
98eda01daa

+ 88 - 86
direct/src/directutil/DirectGeometry.py

@@ -1,86 +1,88 @@
-from PandaModules import *
-from PandaObject import *
-
-X_AXIS = Vec3(1,0,0)
-Y_AXIS = Vec3(0,1,0)
-Z_AXIS = Vec3(0,0,1)
-NEG_X_AXIS = Vec3(-1,0,0)
-NEG_Y_AXIS = Vec3(0,-1,0)
-NEG_Z_AXIS = Vec3(0,0,-1)
-ZERO_VEC = ORIGIN = Vec3(0)
-UNIT_VEC = Vec3(1)
-ZERO_POINT = Point3(0)
-
-class LineNodePath(NodePath):
-    def __init__(self, parent = None, **kw):
-
-        # Initialize the superclass
-        NodePath.__init__(self)
-
-        if parent is None:
-            parent = hidden
-
-        # Attach a geomNode to the parent and set self to be
-        # the resulting node path
-        self.lineNode = GeomNode()
-        self.assign(parent.attachNewNode( self.lineNode ))
-
-        # Create a lineSegs object to hold the line
-        ls = self.lineSegs = LineSegs()
-        # Initialize the lineSegs parameters
-        ls.setThickness( kw.get('thickness', 1.0) )
-        ls.setColor( kw.get('colorVec', VBase4(1.0)) )
-
-    def moveTo( self, *_args ):
-        apply( self.lineSegs.moveTo, _args )
-
-    def drawTo( self, *_args ):
-        apply( self.lineSegs.drawTo, _args )
-
-    def create( self, frameAccurate = 0 ):
-        self.lineSegs.create( self.lineNode, frameAccurate )
-
-    def reset( self ):
-        self.lineSegs.reset()
-        self.lineNode.clear()
-
-    def isEmpty( self ):
-        return self.lineSegs.isEmpty()
-
-    def setThickness( self, thickness ):
-        self.lineSegs.setThickness( thickness )
-
-    def setColor( self, *_args ):
-        apply( self.lineSegs.setColor, _args )
-
-    def setVertex( self, *_args):
-        apply( self.lineSegs.setVertex, _args )
-
-    def setVertexColor( self, vertex, *_args ):
-        apply( self.lineSegs.setVertexColor, (vertex,) + _args )
-
-    def getCurrentPosition( self ):
-        return self.lineSegs.getCurrentPosition()
-
-    def getNumVertices( self ):
-        return self.lineSegs.getNumVertices()
-
-    def getVertex( self, index ):
-        return self.lineSegs.getVertex(index)
-
-    def getVertexColor( self ):
-        return self.lineSegs.getVertexColor()
-
-
-##
-## Given a point in space, and a direction, find the point of intersection
-## of that ray with a plane at the specified origin, with the specified normal
-def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
-    t = 0
-    offset = planeOrigin - lineOrigin
-    t = offset.dot(normal) / lineDir.dot(normal)
-    hitPt = lineDir * t
-    return hitPt + lineOrigin
-
-def roundTo(value, divisor):
-    return round(value/float(divisor)) * divisor
+from PandaModules import *
+from PandaObject import *
+
+X_AXIS = Vec3(1,0,0)
+Y_AXIS = Vec3(0,1,0)
+Z_AXIS = Vec3(0,0,1)
+NEG_X_AXIS = Vec3(-1,0,0)
+NEG_Y_AXIS = Vec3(0,-1,0)
+NEG_Z_AXIS = Vec3(0,0,-1)
+ZERO_VEC = ORIGIN = Vec3(0)
+UNIT_VEC = Vec3(1)
+ZERO_POINT = Point3(0)
+
+class LineNodePath(NodePath):
+    def __init__(self, parent = None, **kw):
+
+        # Initialize the superclass
+        NodePath.__init__(self)
+
+        if parent is None:
+            parent = hidden
+
+        # Attach a geomNode to the parent and set self to be
+        # the resulting node path
+        self.lineNode = GeomNode()
+        self.assign(parent.attachNewNode( self.lineNode ))
+
+        # Create a lineSegs object to hold the line
+        ls = self.lineSegs = LineSegs()
+        # Initialize the lineSegs parameters
+        ls.setThickness( kw.get('thickness', 1.0) )
+        ls.setColor( kw.get('colorVec', VBase4(1.0)) )
+
+    def moveTo( self, *_args ):
+        apply( self.lineSegs.moveTo, _args )
+
+    def drawTo( self, *_args ):
+        apply( self.lineSegs.drawTo, _args )
+
+    def create( self, frameAccurate = 0 ):
+        self.lineSegs.create( self.lineNode, frameAccurate )
+
+    def reset( self ):
+        self.lineSegs.reset()
+        self.lineNode.clear()
+
+    def isEmpty( self ):
+        return self.lineSegs.isEmpty()
+
+    def setThickness( self, thickness ):
+        self.lineSegs.setThickness( thickness )
+
+    def setColor( self, *_args ):
+        apply( self.lineSegs.setColor, _args )
+
+    def setVertex( self, *_args):
+        apply( self.lineSegs.setVertex, _args )
+
+    def setVertexColor( self, vertex, *_args ):
+        apply( self.lineSegs.setVertexColor, (vertex,) + _args )
+
+    def getCurrentPosition( self ):
+        return self.lineSegs.getCurrentPosition()
+
+    def getNumVertices( self ):
+        return self.lineSegs.getNumVertices()
+
+    def getVertex( self, index ):
+        return self.lineSegs.getVertex(index)
+
+    def getVertexColor( self ):
+        return self.lineSegs.getVertexColor()
+
+
+##
+## Given a point in space, and a direction, find the point of intersection
+## of that ray with a plane at the specified origin, with the specified normal
+def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
+    t = 0
+    offset = planeOrigin - lineOrigin
+    t = offset.dot(normal) / lineDir.dot(normal)
+    hitPt = lineDir * t
+    return hitPt + lineOrigin
+
+def ROUND_TO(value, divisor):
+    return round(value/float(divisor)) * divisor
+def ROUND_INT(val):
+    return int(round(val))

+ 4 - 4
direct/src/directutil/DirectGrid.py

@@ -114,9 +114,9 @@ class DirectGrid(NodePath,PandaObject):
         # Snap if necessary
         if self.fXyzSnap:
             self.snapPos.set(
-                roundTo(self.snapPos[0], self.gridSpacing),
-                roundTo(self.snapPos[1], self.gridSpacing),
-                roundTo(self.snapPos[2], self.gridSpacing))
+                ROUND_TO(self.snapPos[0], self.gridSpacing),
+                ROUND_TO(self.snapPos[1], self.gridSpacing),
+                ROUND_TO(self.snapPos[2], self.gridSpacing))
             
 	# Move snap marker to this point
 	self.snapMarker.setPos(self.snapPos)
@@ -125,7 +125,7 @@ class DirectGrid(NodePath,PandaObject):
 	return self.snapPos
 
     def computeSnapAngle(self, angle):
-        return roundTo(angle, self.snapAngle)
+        return ROUND_TO(angle, self.snapAngle)
 
     def setSnapAngle(self, angle):
         self.snapAngle = angle

+ 21 - 8
direct/src/directutil/DirectManipulation.py

@@ -2,7 +2,8 @@ from PandaObject import *
 from DirectGeometry import *
 
 MANIPULATION_MOVE_DELAY = 0.65
-VISIBLE_DISCS = ['x-disc-visible', 'y-disc-visible', 'z-disc-visible']
+UNPICKABLE = ['x-disc-visible', 'y-disc-visible', 'z-disc-visible',
+              'GridBack']
 
 class DirectManipulationControl(PandaObject):
     def __init__(self):
@@ -24,6 +25,7 @@ class DirectManipulationControl(PandaObject):
         self.fWidgetTop = 0
         self.fFreeManip = 1
         self.fScaling = 1
+        self.unpickable = UNPICKABLE
         self.mode = None
 
     def manipulationStart(self, chan):
@@ -102,7 +104,7 @@ class DirectManipulationControl(PandaObject):
                 # Is it a named node?, If so, see if it has a name
                 elif issubclass(node.__class__, NamedNode):
                     name = node.getName()
-                    if name in VISIBLE_DISCS:
+                    if name in self.unpickable:
                         pass
                     else:
                         index = i
@@ -119,7 +121,7 @@ class DirectManipulationControl(PandaObject):
                 # Find the node path from the node found above
                 nodePath = render.findPathDownTo(node)
                 # Select it
-                direct.select(nodePath)
+                direct.select(nodePath, direct.fShift)
             else:
                 direct.deselectAll()
         else:
@@ -136,11 +138,15 @@ class DirectManipulationControl(PandaObject):
         self.objectHandles.showAllHandles()
         self.objectHandles.hideGuides()
         # Restart followSelectedNodePath task
-        if direct.selected.last:
-            self.spawnFollowSelectedNodePathTask()
+        self.spawnFollowSelectedNodePathTask()
         messenger.send('manipulateObjectCleanup')
 
     def spawnFollowSelectedNodePathTask(self):
+        # If nothing selected, just return
+        if not direct.selected.last:
+            return
+        # Clear out old task to make sure
+        taskMgr.removeTasksNamed('followSelectedNodePath')
         # Where are the object handles relative to the selected object
         pos = VBase3(0)
         hpr = VBase3(0)
@@ -183,6 +189,14 @@ class DirectManipulationControl(PandaObject):
         self.ignore(',')
         self.ignore('F')
 
+    def addUnpickable(self, item):
+        if item not in self.unpickable:
+            self.unpickable.append(item)
+
+    def removeUnpickable(self, item):
+        if item in self.unpickable:
+            self.unpickable.remove(item)
+
     def removeManipulateObjectTask(self):
         taskMgr.removeTasksNamed('manipulateObject')
 
@@ -435,9 +449,8 @@ class DirectManipulationControl(PandaObject):
         # Compute widget2newWidget relative to base coordinate system
         mWidget2Base = direct.widget.getMat(base)
         mBase2NewBase = Mat4()
-        mBase2NewBase.composeMatrix(
-            UNIT_VEC, VBase3(h,p,r), ZERO_VEC,
-            CSDefault)
+        composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h,p,r), ZERO_VEC,
+                      CSDefault)
         mBase2Widget = base.getMat(direct.widget)
         mWidget2Parent = direct.widget.getMat()
         # Compose the result

+ 81 - 27
direct/src/directutil/DirectSelection.py

@@ -2,7 +2,7 @@ from PandaObject import *
 from DirectGeometry import *
 from DirectSelection import *
 
-
+# MRM: To do: handle broken node paths in selected and deselected dicts
 class DirectNodePath(NodePath):
     # A node path augmented with info, bounding box, and utility methods
     def __init__(self, nodePath):
@@ -47,11 +47,15 @@ class DirectNodePath(NodePath):
 
 class SelectedNodePaths(PandaObject):
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.selectedDict = {}
         self.deselectedDict = {}
         self.last = None
 
     def select(self, nodePath, fMultiSelect = 0):
+        """ Select the specified node path.  Multiselect as required """
 	# Do nothing if nothing selected
         if not nodePath:
             print 'Nothing selected!!'
@@ -60,21 +64,20 @@ class SelectedNodePaths(PandaObject):
 	# Reset selected objects and highlight if multiSelect is false
         if not fMultiSelect:
             self.deselectAll()
-
+        
         # Get this pointer
         id = nodePath.id()
         # First see if its already in the selected dictionary
-        dnp = self.selectedDict.get(id, None)
+        dnp = self.getSelectedDict(id)
         # If so, we're done
         if not dnp:
             # See if it is in the deselected dictionary
-            dnp = self.deselectedDict.get(id, None)
+            dnp = self.getDeselectedDict(id)
             if dnp:
-                # It has been previously selected:
-                # Show its bounding box
-                dnp.highlight()
                 # Remove it from the deselected dictionary
                 del(self.deselectedDict[id])
+                # Show its bounding box
+                dnp.highlight()
             else:
                 # Didn't find it, create a new selectedNodePath instance
                 dnp = DirectNodePath(nodePath)
@@ -87,10 +90,11 @@ class SelectedNodePaths(PandaObject):
         return dnp
 
     def deselect(self, nodePath):
+        """ Deselect the specified node path """
         # Get this pointer
         id = nodePath.id()
         # See if it is in the selected dictionary
-        dnp = self.selectedDict.get(id, None)
+        dnp = self.getSelectedDict(id)
         if dnp:
             # It was selected:
             # Hide its bounding box
@@ -101,30 +105,80 @@ class SelectedNodePaths(PandaObject):
             self.deselectedDict[id] = dnp
         return dnp
 
-    def selectedAsList(self):
-        list = []
-        for key in self.selectedDict.keys():
-            list.append(self.selectedDict[key])
-        return list
+    def getSelectedAsList(self):
+        """
+        Return a list of all selected node paths.  No verification of
+        connectivity is performed on the members of the list
+        """
+        return self.selectedDict.values()[:]
 
     def __getitem__(self,index):
-        return self.selectedAsList()[index]
+        return self.getSelectedAsList()[index]
+
+    def getSelectedDict(self, id):
+        """
+        Search selectedDict for node path, try to repair broken node paths.
+        """
+        dnp = self.selectedDict.get(id, None)
+        if dnp:
+            # Found item in selected Dictionary, is it still valid?
+            if dnp.verifyConnectivity():
+                # Yes
+                return dnp
+            else:
+                # Not valid anymore, try to repair
+                if dnp.repairConnectivity(render):
+                    # Fixed, return node path
+                    return dnp
+                else:
+                    del(self.selectedDict[id])
+                    return None
+        else:
+            # Not in selected dictionary
+            return None
 
-    def deselectedAsList(self):
-        list = []
-        for key in self.deselectedDict.keys():
-            list.append(self.deselectedDict[key])
-        return list
+    def getDeselectedAsList(self):
+        return self.deselectedDict.values()[:]
+
+    def getDeselectedDict(self, id):
+        """
+        Search deselectedDict for node path, try to repair broken node paths.
+        """
+        dnp = self.deselectedDict.get(id, None)
+        if dnp:
+            # Found item in deselected Dictionary, is it still valid?
+            if dnp.verifyConnectivity():
+                # Yes
+                return dnp
+            else:
+                # Not valid anymore, try to repair
+                if dnp.repairConnectivity(render):
+                    # Fixed, return node path
+                    return dnp
+                else:
+                    del(self.deselectedDict[id])
+                    return None
+        else:
+            # Not in deselected dictionary
+            return None
 
     def forEachSelectedNodePathDo(self, func):
-        duplicateKeys = self.selectedDict.keys()[:]
-        for key in duplicateKeys:
-            func(self.selectedDict[key])
+        """
+        Perform given func on selected node paths.  No node path
+        connectivity verification performed
+        """
+        selectedNodePaths = self.getSelectedAsList()
+        for nodePath in selectedNodePaths:
+            func(nodePath)
 
     def forEachDeselectedNodePathDo(self, func):
-        duplicateKeys = self.deselectedDict.keys()[:]
-        for key in duplicateKeys:
-            func(self.deselectedDict[key])
+        """
+        Perform given func on deselected node paths.  No node path
+        connectivity verification performed
+        """
+        deselectedNodePaths = self.getDeselectedAsList()
+        for nodePath in deselectedNodePaths:
+            func(nodePath)
 
     def getWrtAll(self):
         self.forEachSelectedNodePathDo(self.getWrt)
@@ -175,11 +229,11 @@ class SelectedNodePaths(PandaObject):
         # Get this pointer
         id = nodePath.id()
         # First check selected dict
-        dnp = self.selectedDict.get(id, None)
+        dnp = self.getSelectedDict(id)
         if dnp:
             return dnp
         # Otherwise return result of deselected search
-        return self.selectedDict.get(id, None)
+        return self.getDeselectedDict(id)
 
     def getNumSelected(self):
         return len(self.selectedDict.keys())

+ 5 - 3
direct/src/directutil/DirectSession.py

@@ -63,8 +63,8 @@ class DirectSession(PandaObject):
                             'mouse2', 'mouse2-up',
                             'mouse3', 'mouse3-up']
 
-    def select(self, nodePath):
-        dnp = self.selected.select(nodePath)
+    def select(self, nodePath, fMultiselect = 0):
+        dnp = self.selected.select(nodePath, fMultiselect)
         if dnp:
             messenger.send('preSelectNodePath', [dnp])
             # Update the readout
@@ -127,6 +127,8 @@ class DirectSession(PandaObject):
 	self.cameraControl.enableMouseFly()
         # Turn on object manipulation
         self.manipulationControl.enableManipulation()
+        # Make sure list of selected items is reset
+        self.selected.reset()
 	# Accept appropriate hooks
 	self.enableKeyEvents()
 	self.enableMouseEvents()
@@ -159,7 +161,7 @@ class DirectSession(PandaObject):
     def destroy(self):
 	self.disable()
 
-    def restart(self):
+    def reset(self):
 	self.enable()
 
     def enableActionEvents(self):

+ 9 - 0
direct/src/extensions/NodePath-extensions.py

@@ -23,6 +23,15 @@
                 name = namedNodeName
         return name
 
+    def setName(self, name = '<noname>'):
+        """Returns the name of the bottom node if it exists, or <noname>"""
+        from PandaModules import *
+        # Get the bottom node
+        node = self.node()
+        # Is it a named node?, If so, see if it has a name
+        if issubclass(node.__class__, NamedNode):
+            node.setName(name)
+
     # For iterating over children
     def getChildrenAsList(self):
         """Converts a node path's child NodePathCollection into a list"""

File diff suppressed because it is too large
+ 1304 - 1930
direct/src/leveleditor/LevelEditor.py


+ 20 - 24
direct/src/leveleditor/PieMenu.py

@@ -2,28 +2,29 @@ from PandaObject import *
 from DirectGeometry import *
 
 class PieMenu(NodePath, PandaObject):
-    def __init__(self, direct, menu, action = None, fUpdateOnlyOnChange = 1):
+    def __init__(self, visibleMenu, menuItems,
+                 action = None, fUpdateOnlyOnChange = 1):
         NodePath.__init__(self)
         # Create a toplevel node for aspect ratio scaling
         self.assign(hidden.attachNewNode(NamedNode('PieMenu')))
         # Attach the menu
-        self.menu = menu
-        # Try to flatten the menu (note, flattenStrong is too strong
+        self.visibleMenu = visibleMenu
+        # Try to flatten the visibleMenu (note, flattenStrong is too strong
         # for texture text
-        menu.flattenMedium()
-        self.menu.reparentTo(self)
+        self.visibleMenu.flattenMedium()
+        self.visibleMenu.reparentTo(self)
         # Initialize instance variables
-        self.direct = direct
-        self.numItems = self.menu.getNumChildren()
+        self.menuItems = menuItems
+        self.numItems = len(self.menuItems)
         self.degreesPerItem = 360.0/self.numItems
         self.itemOffset = self.degreesPerItem / 2.0
-        self.sfx = self.menu.getSx()
-        self.sfz = self.menu.getSz()
+        self.sfx = self.visibleMenu.getSx()
+        self.sfz = self.visibleMenu.getSz()
         # Record target and action
         self.action = action
         self.initialState = None
         # Marking lines
-        self.lines = LineNodePath(self.menu)
+        self.lines = LineNodePath(self.visibleMenu)
         self.lines.setColor(VBase4(1))
         self.lines.setThickness(1)
         # Set flags
@@ -43,15 +44,15 @@ class PieMenu(NodePath, PandaObject):
 	taskMgr.removeTasksNamed('pieMenuTask')
 
 	# Where did the user press the button?
-	self.originX = self.direct.chan.mouseX
-	self.originY = self.direct.chan.mouseY
+	self.originX = direct.chan.mouseX
+	self.originY = direct.chan.mouseY
 
 	# Pop up menu
 	self.reparentTo(render2d)
 	self.setPos(self.originX,0.0,self.originY)
         # Compensate for window aspect ratio
         self.setScale(1.0, 1.0,1.0)
-        #self.direct.chan.width/float(self.direct.chan.height))
+        #direct.chan.width/float(direct.chan.height))
 	# Start drawing the selection line
 	self.lines.reset()
 	self.lines.moveTo(0,0,0)
@@ -64,8 +65,8 @@ class PieMenu(NodePath, PandaObject):
         taskMgr.spawnTaskNamed(t, 'pieMenuTask')
 
     def pieMenuTask(self,state):
-        mouseX = self.direct.chan.mouseX
-        mouseY = self.direct.chan.mouseY
+        mouseX = direct.chan.mouseX
+        mouseY = direct.chan.mouseY
         deltaX = mouseX - self.originX
         deltaY = mouseY - self.originY
 
@@ -78,10 +79,10 @@ class PieMenu(NodePath, PandaObject):
             if self.fUpdateOnlyOnChange:
                 # Only do this when things change
                 if (self.currItem != -1):
-                    self.performAction(-1)
+                    self.performAction(self.initialState)
             else:
                 # Alway let use know mouse is in the center
-                self.performAction(-1)
+                self.performAction(self.initialState)
             self.currItem = -1
         else:
             # Outside of the center
@@ -95,9 +96,9 @@ class PieMenu(NodePath, PandaObject):
 
             if self.fUpdateOnlyOnChange:
                 if (self.currItem != newItem):
-                    self.performAction(newItem)
+                    self.performAction(self.menuItems[newItem])
             else:
-                self.performAction(newItem)
+                self.performAction(self.menuItems[newItem])
             self.currItem = newItem
         # Continue task
         return Task.cont
@@ -111,11 +112,6 @@ class PieMenu(NodePath, PandaObject):
     def setItemOffset(self,newOffset):
 	self.itemOffset = newOffset
 
-    def setNumItems(self,num):
-	self.numItems = num
-	self.degreesPerItem = 360.0 / self.numItems
-	self.itemOffset = self.degreesPerItem / 2.0
-
     def setUpdateOnlyOnChange(self,flag):
 	self.fUpdateOnlyOnChange = flag
 

+ 3 - 1
direct/src/showbase/TkGlobal.py

@@ -9,7 +9,7 @@ def tkloop(self):
     # dooneevent will return 0 if there are no more events
     # waiting or 1 if there are still more.
     # DONT_WAIT tells tkinter not to block waiting for events
-    while tkinter.dooneevent(tkinter.DONT_WAIT):
+    while tkinter.dooneevent(tkinter.ALL_EVENTS | tkinter.DONT_WAIT):
         pass
     # Run forever
     return Task.cont
@@ -19,3 +19,5 @@ from TaskManagerGlobal import *
 
 # Spawn this task
 taskMgr.spawnTaskNamed(Task.Task(tkloop), "tkloop")
+
+

+ 129 - 120
direct/src/tkwidgets/SceneGraphExplorer.py

@@ -1,120 +1,129 @@
-from PandaObject import *
-from Tkinter import *
-from Tree import *
-import Pmw
-
-
-class SceneGraphExplorer(Pmw.MegaWidget):
-    "Graphical display of a scene graph"
-    def __init__(self, root = render, parent = None, **kw):
-        # Define the megawidget options.
-        optiondefs = (
-            ('menuItems',   ['Select'],   None),
-            )
-        self.defineoptions(kw, optiondefs)
- 
-        # Initialise superclass
-        Pmw.MegaWidget.__init__(self, parent)
-        
-        # Initialize some class variables
-        self.root = root
-
-        # Create the components.
-        
-        # Setup up container
-        interior = self.interior()
-        interior.configure(relief = GROOVE, borderwidth = 2)
-        
-        # Create a label and an entry
-        self._scrolledCanvas = self.createcomponent(
-            'scrolledCanvas',
-            (), None,
-            Pmw.ScrolledCanvas, (interior,),
-            hull_width = 200, hull_height = 400,
-            usehullsize = 1)
-        self._canvas = self._scrolledCanvas.component('canvas')
-        self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i')
-        self._scrolledCanvas.resizescrollregion()
-        self._scrolledCanvas.pack(padx = 5, pady = 5, expand=1, fill = BOTH)
-        
-        self._canvas.bind('<ButtonPress-2>', self.mouse2Down)
-        self._canvas.bind('<B2-Motion>', self.mouse2Motion)
-        self._canvas.bind('<Configure>',
-                          lambda e, sc = self._scrolledCanvas:
-                          sc.resizescrollregion())
-
-        # Create the contents
-        self._treeItem = SceneGraphExplorerItem(self.root)
-
-        self._node = TreeNode(self._canvas, None, self._treeItem,
-                              self['menuItems'])
-        self._node.expand()
-
-        # Check keywords and initialise options based on input values.
-        self.initialiseoptions(SceneGraphExplorer)
-
-    def mouse2Down(self, event):
-        self._width = 1.0 * self._canvas.winfo_width()
-        self._height = 1.0 * self._canvas.winfo_height()
-        xview = self._canvas.xview()
-        yview = self._canvas.yview()        
-        self._left = xview[0]
-        self._top = yview[0]
-        self._dxview = xview[1] - xview[0]
-        self._dyview = yview[1] - yview[0]
-        self._2lx = event.x
-        self._2ly = event.y
-
-    def mouse2Motion(self,event):
-        newx = self._left - ((event.x - self._2lx)/self._width) * self._dxview
-        self._canvas.xview_moveto(newx)
-        newy = self._top - ((event.y - self._2ly)/self._height) * self._dyview
-        self._canvas.yview_moveto(newy)
-        self._2lx = event.x
-        self._2ly = event.y
-        self._left = self._canvas.xview()[0]
-        self._top = self._canvas.yview()[0]
-
-
-class SceneGraphExplorerItem(TreeItem):
-
-    """Example TreeItem subclass -- browse the file system."""
-
-    def __init__(self, nodePath):
-        self.nodePath = nodePath
-
-    def GetText(self):
-        type = self.nodePath.node().getType().getName()
-        name = self.nodePath.getName()
-        return type + "  " + name
-
-    def IsEditable(self):
-        return issubclass(self.nodePath.node().__class__, NamedNode)
-
-    def SetText(self, text):
-        try:
-            self.nodePath.node().setName(text)
-        except AttributeError:
-            pass
-
-    def GetIconName(self):
-        if not self.IsExpandable():
-            return "sphere2" # XXX wish there was a "file" icon
-
-    def IsExpandable(self):
-        return self.nodePath.getNumChildren() != 0
-
-    def GetSubList(self):
-        sublist = []
-        for nodePath in self.nodePath.getChildrenAsList():
-            item = SceneGraphExplorerItem(nodePath)
-            sublist.append(item)
-        return sublist
-
-    def OnSelect(self):
-        messenger.send('SGEFlashNodePath', [self.nodePath])
-
-    def MenuCommand(self, command):
-        messenger.send('SGE' + command + 'NodePath', [self.nodePath])
-
-
+from PandaObject import *
+from Tkinter import *
+from Tree import *
+import Pmw
+
+class SceneGraphExplorer(Pmw.MegaWidget):
+    "Graphical display of a scene graph"
+    def __init__(self, root = render, parent = None, **kw):
+        # Define the megawidget options.
+        optiondefs = (
+            ('menuItems',   ['Select'],   None),
+            )
+        self.defineoptions(kw, optiondefs)
+ 
+        # Initialise superclass
+        Pmw.MegaWidget.__init__(self, parent)
+        
+        # Initialize some class variables
+        self.root = root
+
+        # Create the components.
+        
+        # Setup up container
+        interior = self.interior()
+        interior.configure(relief = GROOVE, borderwidth = 2)
+        
+        # Create a label and an entry
+        self._scrolledCanvas = self.createcomponent(
+            'scrolledCanvas',
+            (), None,
+            Pmw.ScrolledCanvas, (interior,),
+            hull_width = 200, hull_height = 400,
+            usehullsize = 1)
+        self._canvas = self._scrolledCanvas.component('canvas')
+        self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i')
+        self._scrolledCanvas.resizescrollregion()
+        self._scrolledCanvas.pack(padx = 5, pady = 5, expand=1, fill = BOTH)
+        
+        self._canvas.bind('<ButtonPress-2>', self.mouse2Down)
+        self._canvas.bind('<B2-Motion>', self.mouse2Motion)
+        self._canvas.bind('<Configure>',
+                          lambda e, sc = self._scrolledCanvas:
+                          sc.resizescrollregion())
+
+        # Create the contents
+        self._treeItem = SceneGraphExplorerItem(self.root)
+
+        self._node = TreeNode(self._canvas, None, self._treeItem,
+                              self['menuItems'])
+        self._node.expand()
+
+        # Check keywords and initialise options based on input values.
+        self.initialiseoptions(SceneGraphExplorer)
+
+    def update(self):
+        """ Refresh scene graph explorer """
+        self._node.update()
+
+    def mouse2Down(self, event):
+        self._width = 1.0 * self._canvas.winfo_width()
+        self._height = 1.0 * self._canvas.winfo_height()
+        xview = self._canvas.xview()
+        yview = self._canvas.yview()        
+        self._left = xview[0]
+        self._top = yview[0]
+        self._dxview = xview[1] - xview[0]
+        self._dyview = yview[1] - yview[0]
+        self._2lx = event.x
+        self._2ly = event.y
+
+    def mouse2Motion(self,event):
+        newx = self._left - ((event.x - self._2lx)/self._width) * self._dxview
+        self._canvas.xview_moveto(newx)
+        newy = self._top - ((event.y - self._2ly)/self._height) * self._dyview
+        self._canvas.yview_moveto(newy)
+        self._2lx = event.x
+        self._2ly = event.y
+        self._left = self._canvas.xview()[0]
+        self._top = self._canvas.yview()[0]
+
+
+class SceneGraphExplorerItem(TreeItem):
+
+    """Example TreeItem subclass -- browse the file system."""
+
+    def __init__(self, nodePath):
+        self.nodePath = nodePath
+
+    def GetText(self):
+        type = self.nodePath.node().getType().getName()
+        name = self.nodePath.getName()
+        return type + "  " + name
+
+    def IsEditable(self):
+        return issubclass(self.nodePath.node().__class__, NamedNode)
+
+    def SetText(self, text):
+        try:
+            self.nodePath.node().setName(text)
+        except AttributeError:
+            pass
+
+    def GetIconName(self):
+        if not self.IsExpandable():
+            return "sphere2" # XXX wish there was a "file" icon
+
+    def IsExpandable(self):
+        return self.nodePath.getNumChildren() != 0
+
+    def GetSubList(self):
+        sublist = []
+        for nodePath in self.nodePath.getChildrenAsList():
+            item = SceneGraphExplorerItem(nodePath)
+            sublist.append(item)
+        return sublist
+
+    def OnSelect(self):
+        messenger.send('SGENodePath_Flash', [self.nodePath])
+
+    def MenuCommand(self, command):
+        messenger.send('SGENodePath_' + command, [self.nodePath])
+
+
+def explore(nodePath):
+    tl = Toplevel()
+    tl.title('Explore: ' + nodePath.getName())
+    sge = SceneGraphExplorer(parent = tl, root = nodePath)
+    sge.pack(expand = 1, fill = 'both')
+    return sge

Some files were not shown because too many files changed in this diff