*** empty log message ***

This commit is contained in:
Mark Mine 2000-11-11 01:01:05 +00:00
parent 823f0612f5
commit 8dbea4d196
3 changed files with 841 additions and 437 deletions

View File

@ -4,6 +4,7 @@ from OnscreenText import *
from whrandom import *
from Tkinter import *
from DirectGeometry import *
from SceneGraphExplorer import *
import Pmw
import Dial
import Floater
@ -111,7 +112,7 @@ class LevelEditor(NodePath, PandaObject):
base.cam.node().setNear(5.0)
base.cam.node().setFar(10000)
self.direct.camera.setPos(0,0,10)
self.direct.camera.setPos(0,-10,10)
# Default is to use the toontown central color palette
self.editToontownCentral()
@ -374,6 +375,7 @@ class LevelEditor(NodePath, PandaObject):
self.ignore('createNewLevelGroup')
self.ignore('setNodePathName')
self.ignore('manipulateObjectCleanup')
self.ignore('SGESelectNodePath')
self.ignore('showAll')
self.ignore('p')
self.disableManipulation()
@ -453,6 +455,7 @@ class LevelEditor(NodePath, PandaObject):
self.accept('createNewLevelGroup', self.createNewLevelGroup)
self.accept('setNodePathName', self.setNodePathName)
self.accept('manipulateObjectCleanup', self.updateSelectedPose)
self.accept('SGESelectNodePath', self.selectNodePath)
self.accept('showAll', self.showAll)
self.accept('p',self.plantSelectedNodePath)
self.enableManipulation()
@ -3216,6 +3219,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
# suitBuildingsPage = notebook.add('Suit Buildings')
propsPage = notebook.add('Props')
colorPage = notebook.add('Set Color')
sceneGraphPage = notebook.add('SceneGraph')
self.addStreetButton = Button(
streetsPage,
@ -3228,6 +3232,8 @@ class LevelEditorPanel(Pmw.MegaToplevel):
listheight = 200,
labelpos = W,
label_text = 'Street type:',
label_width = 12,
label_anchor = W,
entry_width = 24,
selectioncommand = self.setStreetModuleType,
scrolledlist_items = map(lambda s: s[7:],
@ -3236,7 +3242,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
)
self.streetModuleType = levelEditor.getCatalogCode('street',0)
self.streetSelector.selectitem(self.streetModuleType[7:])
self.streetSelector.pack(expand = 1, fill = 'x')
self.streetSelector.pack(expand = 1, fill = 'both')
self.addToonBuildingButton = Button(
toonBuildingsPage,
@ -3248,7 +3254,9 @@ class LevelEditorPanel(Pmw.MegaToplevel):
dropdown = 0,
listheight = 200,
labelpos = W,
label_text = 'Toon building type:',
label_width = 12,
label_anchor = W,
label_text = 'Toon bldg type:',
entry_width = 24,
selectioncommand = self.setFlatBuildingType,
scrolledlist_items = ('random20', 'random30',
@ -3258,13 +3266,13 @@ class LevelEditorPanel(Pmw.MegaToplevel):
)
self.toonBuildingType = 'random20'
self.toonBuildingSelector.selectitem(self.toonBuildingType)
self.toonBuildingSelector.pack(expand = 0)
self.toonBuildingSelector.pack(expand = 1, fill = 'both')
self.toonBuildingWidthScale = EntryScale.EntryScale(
toonBuildingsPage, min = 1.0, max = 30.0,
resolution = 0.01, text = 'Wall Width',
command = self.updateSelectedWallWidth)
self.toonBuildingWidthScale.pack(side = TOP, fill = 'x')
self.toonBuildingWidthScale.pack(fill = 'x')
self.addLandmarkBuildingButton = Button(
landmarkBuildingsPage,
@ -3276,6 +3284,8 @@ class LevelEditorPanel(Pmw.MegaToplevel):
dropdown = 0,
listheight = 200,
labelpos = W,
label_width = 12,
label_anchor = W,
label_text = 'Landmark Building type:',
entry_width = 24,
selectioncommand = self.setLandmarkType,
@ -3287,7 +3297,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
'toon_landmark',0)
self.landmarkBuildingSelector.selectitem(
levelEditor.getCatalogCode('toon_landmark',0)[14:])
self.landmarkBuildingSelector.pack(side = 'left', expand = 0)
self.landmarkBuildingSelector.pack(expand = 1, fill = 'both')
self.addPropsButton = Button(
propsPage,
@ -3299,6 +3309,8 @@ class LevelEditorPanel(Pmw.MegaToplevel):
dropdown = 0,
listheight = 200,
labelpos = W,
label_width = 12,
label_anchor = W,
label_text = 'Prop type:',
entry_width = 24,
selectioncommand = self.setPropType,
@ -3308,7 +3320,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
self.propType = levelEditor.getCatalogCode('prop',0)
self.propSelector.selectitem(
levelEditor.getCatalogCode('prop',0)[5:])
self.propSelector.pack(side = 'left', expand = 0)
self.propSelector.pack(expand = 1, fill = 'both')
# Compact down notebook
notebook.setnaturalsize()
@ -3336,7 +3348,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
variable = self.fHprSnap,
command = self.toggleHprSnap)
self.hprSnapButton.pack(side = 'left', fill = 'x')
buttonFrame.pack(fill = 'both')
buttonFrame.pack(expand = 1, fill = 'x')
buttonFrame2 = Frame(hull)
self.groupButton = Button(
@ -3356,12 +3368,17 @@ class LevelEditorPanel(Pmw.MegaToplevel):
variable = self.fMapViz,
command = self.toggleMapViz)
self.mapSnapButton.pack(side = 'left', fill = 'x')
buttonFrame2.pack(fill = 'both')
buttonFrame2.pack(fill = 'x')
self.colorEntry = VectorWidgets.ColorEntry(
colorPage, text = 'Select Color',
command = self.updateSelectedObjColor)
self.colorEntry.pack(fill = X)
self.colorEntry.pack(fill = 'x')
self.sceneGraphExplorer = SceneGraphExplorer(
parent = sceneGraphPage,
root = self.levelEditor.getLevelObjects())
self.sceneGraphExplorer.pack(expand = 1, fill = 'both')
def toggleGrid(self):
if self.fGrid.get():
@ -3442,207 +3459,9 @@ class LevelEditorPanel(Pmw.MegaToplevel):
else:
self.balloon.configure(state = 'none')
"""
def initializePropButtons(self):
# Initialize Hooks and Buttons for the wall type buttons
newPtopTypes = self.getCatalogCodes('prop')
methodArray = []
for
newPtopTypes collect: [ :prop |
{ prop copyFrom: 5 to: prop size . prop asSymbol } ].
hooksSet = self.hooksDictionary['prop ifAbsent: [ Set new. ].
methodArray do: [ :pair | hooksSet add: (pair at: 2) ].
self.hooksDictionary['prop'] = hooksSet.
# Create wall module buttons
# First get rid of any existing buttons
self.clickBoxDictionary['prop ifPresent: [ :clickBoxList | clickBoxList disable ].
buttons = ClickBoxList new table: methodArray x: 0.95 y: 0.90.
buttons addButtonWithText: 'back' event: #mainMenuEnable.
buttons alignRight.
buttons.setScale(0.06.
buttons.setColor(0.6 g: 0.6 b: 0.6 a: 0.8.
self.clickBoxDictionary['prop'] = buttons.
self.categorySet add: #prop.
! !
!Level methodsFor: 'initialization' stamp: 'panda 00/00/0000 00:00'!
initializeStreetButtons
| streetTypes methodArray hooksSet buttons |
# Initialize Hooks and Buttons for the wall type buttons
streetTypes = self.getCatalogCodes(#street.
methodArray =
streetTypes collect: [ :street | { street copyFrom: 8 to: street size . street asSymbol } ].
hooksSet = self.hooksDictionary['street ifAbsent: [ Set new. ].
methodArray do: [ :pair | hooksSet add: (pair at: 2) ].
self.hooksDictionary['street'] = hooksSet.
# Create wall module buttons
# First get rid of any existing buttons
self.clickBoxDictionary['street ifPresent: [ :clickBoxList | clickBoxList disable ].
buttons = ClickBoxList new table: methodArray x: 0.95 y: 0.90.
buttons addButtonWithText: 'back' event: #mainMenuEnable.
buttons alignRight.
buttons.setScale(0.06.
buttons.setColor(0.6 g: 0.6 b: 0.6 a: 0.8.
self.clickBoxDictionary['street'] = buttons.
self.categorySet add: #street.
! !
!Level methodsFor: 'initialization' stamp: 'panda 00/00/0000 00:00'!
initializeWallButtons
| methodArray hooksSet buttons |
# Initialize Hooks and Buttons for the wall type buttons
methodArray = {
{ 'Random 20' . #random20 } .
{ 'Random 30' . #random30 } .
{ '10-10' . #toonTenTen } .
{ '20' . #toonTwenty } .
{ '10-20' . #toonTenTwenty } .
{ '20-10' . #toonTwentyTen } .
{ '10-10-10' . #toonTenTenTen } .
{ '30' . #toonThirty } }.
hooksSet = self.hooksDictionary['wall ifAbsent: [ Set new. ].
methodArray do: [ :pair | hooksSet add: (pair at: 2) ].
self.hooksDictionary['wall'] = hooksSet.
# Create wall module buttons
# First get rid of any existing buttons
self.clickBoxDictionary['wall ifPresent: [ :clickBoxList | clickBoxList disable ].
buttons = ClickBoxList new table: methodArray x: 0.95 y: 0.90.
buttons addButtonWithText: 'back' event: #mainMenuEnable.
buttons.setColor(0.6 g: 0.6 b: 0.6 a: 0.8.
buttons.setScale(0.06.
buttons alignRight.
buttons makeAllWideAsWidest.
self.clickBoxDictionary['wall'] = buttons.
# Initialize Hooks and Buttons for the wall width buttons
methodArray = { { '5 ft' . #fiveFt } .
{ '10 ft' . #tenFt } .
{ '15 ft' . #fifteenFt } .
{ '20 ft' . #twentyFt } .
{ '25 ft' . #twentyFiveFt } }.
hooksSet = self.hooksDictionary['wallWidths ifAbsent: [ Set new. ].
methodArray do: [ :pair | hooksSet add: (pair at: 2) ].
self.hooksDictionary['wallWidths'] = hooksSet.
# Create wall width buttons
# First get rid of any existing buttons
self.clickBoxDictionary['wallWidths ifPresent: [ :clickBoxList | clickBoxList disable ].
buttons = ClickBoxList new table: methodArray x: 0.95 y: -0.40.
buttons.setColor(0.6 g: 0.6 b: 0.6 a: 0.8.
buttons.setScale(0.06.
buttons alignRight.
self.clickBoxDictionary['wallWidths'] = buttons.
self.categorySet add: #wall.
! !
def clearHighlightedObjects(self):
highlightedObjects getChildren forEachPathPerform: #removeNode.! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
followMouse: aNodePath
# Plant target object on grid at cursor projection point
| roundVal |
roundVal = (self.grid gridSpacing roundTo: 1).
(self.grid getMouseIntersectionPoint: self.hitPt ) ifTrue: [
self.grid xyzSnap ifTrue: [
aNodePath setPos: self.grid
x: (((self.hitPt at: 0) + (self.offset at: 0)) roundTo: roundVal)
y: (((self.hitPt at: 1) + (self.offset at: 1)) roundTo: roundVal)
z: (((self.hitPt at: 2) + (self.offset at: 2)) roundTo: roundVal).
]
ifFalse: [
aNodePath setPos: self.grid pos: (self.hitPt + self.offset).
].
]
! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
followMouseStart: aNodePath
| gridToObjectHandles hitPtToObjectHandles |
# Where is the mouse relative to the grid?
self.grid getMouseIntersectionPoint: self.hitPt.
# Record crank origin
self.crankOrigin operatorAssign: (direct selectedNodePath getPos: self.grid).
# Record the offset
self.offset = self.crankOrigin - self.hitPt.
# Init crankDir
self.crankDir operatorAssign: self.offset negated.
self.crankDir normalize.
# Compute crankAngle
startAngle = self.getCrankAngle.
startH = direct selectedNodePath getH.
# Transform hitPt into object handles space to determine manipulation mode
# Where is the mouse relative to the widget?
Don't snap to grid since we want to test hitPt relative to widget
self.grid getMouseIntersectionPoint: self.hitPt xyzSnap: 0.
gridToObjectHandles = self.grid getMat: direct objectHandles.
hitPtToObjectHandles = (Vec3 new: (gridToObjectHandles xformPoint: self.hitPt)) length.
# Are we inside rotation ring?
((hitPtToObjectHandles > 0.8) and: [(hitPtToObjectHandles < 1.2)])
ifTrue: [[[true] taskWhileTrue: [ self.mouseCrank: aNodePath]]
spawnTaskNamed: #levelMouseCrank.]
ifFalse: [[[true] taskWhileTrue: [ self.followMouse: aNodePath]]
spawnTaskNamed: #levelFollowMouse.]
! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
followMouseStop
| selectedNode |
# Stop moving object
Task removeTasksNamed: #levelFollowMouse.
Task removeTasksNamed: #levelMouseCrank.
# Move grid to line up with object
selectedNode = direct selectedNodePath.
selectedNode notNone ifTrue: [
self.updateDNAPosHpr: selectedNode.
# Position grid for placing next object
self.autoPositionGrid.
].
! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
getCrankAngle
| newAngle |
self.crankDir normalize.
# Just look at Y component (equiv to dot product with (0 1 0)
newAngle = (self.crankDir at: 1) arcCos radiansToDegrees.
((self.crankDir at: 0) > 0.0) ifTrue: [ newAngle = newAngle negated. ].
# Force it to 0 to 360.0 range
return newAngle + 180.0.
! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
highlightNodePath: aNodePath
| pose highlightedNode |
# First clear out old highlighted nodes
self.clearHighlightedObjects.
# Place an instance of the object under the highlightedObjects node
highlightedNode = aNodePath instanceTo: highlightedObjects.
pose = aNodePath getMat: self.levelObjects.
highlightedNode setMat: self.levelObjects mat: pose.
! !
!Level methodsFor: 'object operations' stamp: 'panda 00/00/0000 00:00'!
keyboardRotateNodePath: aNodePath key: arrowDirection
| pos hpr scale |
@ -3750,154 +3569,6 @@ mouseCrank: aNodePath
def keyboardXformNodePath(self,x):
pass
def levelHandleMouse1(self):
selectedNodePath = self.direct.selected.last
if selectedNodePath:
self.followMouseStart(selectedNodePath)
def levelHandleMouse1Up(self):
self.followMouseStop()
# MRM
def activateLandmarkButtons(self):
# Switch menus to reveal street menu
self.mainMenuDisable()
self.categoryEnable('toon_landmark')
# MRM
def activatePropButtons(self):
# Switch menus to reveal street menu
self.mainMenuDisable()
self.categoryEnable('prop')
def activateStreetModuleButtons(self):
# Switch menus to reveal street menu
self.mainMenuDisable()
self.categoryEnable('street')
def activateVizObjectsButtons(self):
# Switch menus to reveal viz region menu
#self.mainMenuDisable()
#self.clickBoxDictionary['vizRegionButtons'].enable()
self.accept('addVizRegion', self.addVizRegion)
self.accept('addCollisionSphere', self.addCollisionSphere)
self.grid.setGridSpacing(10.0)
# MRM
def activateWallModuleButtons(self):
# Switch menus to reveal street menu
self.mainMenuDisable()
self.categoryEnable('wall')
def addHook(self, hook, function):
self.accept(hook, function, [hook])
def allMenuDisable(self):
self.mainMenuDisable()
self.gridMenuDisable()
self.subMenuDisable()
self.dnaMenuDisable()
def categoryDisable(self, categoryName):
clickBoxList = self.clickBoxDictionary.set(categoryName,None)
if clickBoxList:
clickBoxList.disable()
hooks = self.hooksDictionary.get(categoryName, None)
if hooks:
for hook in hooks:
self.ignore(hook)
# Do any category specific disabilizaton here
if categoryName == 'wall':
clickBoxList = self.clickBoxDictionary.get('wallWidths',None)
if clickBoxList:
clickBoxList.disable()
hooks = self.hooksDictionary.get('wallWidths', None)
if hooks:
for hook in hooks:
self.ignore(hook)
# Clear out space and insert hooks
self.ignore('space')
self.ignore('insert')
def categoryEnable(self,categoryName):
# First activate this category's main buttons
clickBoxList = self.clickBoxDictionary.get(categoryName,None)
if clickBoxList:
clickBoxList.enable()
# Now activate hooks and any supplemental actions
if categoryName == 'street':
# activate street module hooks
hooks = self.hooksDictionary.get(categoryName,None)
if hooks:
for hook in hooks:
self.addHook(hook,self.addStreetModule)
elif categoryName == 'wall':
# Activate wall module hooks
hooks = self.hooksDictionary.get(categoryName,None)
if hooks:
for hook in hooks:
self.addHook(hook, self.addFlatBuilding)
# Also activate wall width buttons and hooks
clickBoxList = self.clickBoxDictionary.get('wallWidths', None)
if clickBoxList:
clickBoxList.enable()
hooks = self.hooksDictionary.get('wallWidths',None)
if hooks:
for hook in hooks:
self.addHook(hook,self.wallWidthSym)
elif categoryName == 'toon_landmark':
# activate landmark hooks
hooks = self.hooksDictionary.get(categoryName,None)
if hooks:
for hook in hooks:
self.addHook(hook,self.addLandmark)
elif categoryName == 'prop':
# activate prop hooks
hooks = self.hooksDictionary.get(categoryName,None)
if hooks:
for hook in hooks:
self.addHook(hook,self.addProp)
def getClickBoxDictionary(self):
return self.clickBoxDictionary
def dnaMenuDisable(self):
# Disable DNA menu
self.clickBoxDictionary['groupButton'].disable()
self.ignore('createNewLevelGroup')
self.clickBoxDictionary['saveButton'].disable()
self.ignore('outputDNA:')
self.clickBoxDictionary['mapButton'].disable()
self.ignore('toggleMapViz')
def dnaMenuEnable(self):
# Enable DNA menu
self.clickBoxDictionary['groupButton'].enable()
self.accept('createNewLevelGroup', self.createNewLevelGroup)
self.clickBoxDictionary['saveButton'].enable()
self.accept('outputDNA', self.outputDNA)
self.clickBoxDictionary['mapButton'].enable()
self.accept('toggleMapViz', self.toggleMapViz)
def gridMenuDisable(self):
self.clickBoxDictionary['gridMenuButtons'].disable()
self.ignore('showGrid')
self.ignore('xyzSnap')
self.ignore('hprSnap')
def gridMenuEnable(self):
# Enable grid menu
self.clickBoxDictionary['gridMenuButtons'].enable()
self.accept('showGrid', self.showGrid)
self.accept('xyzSnap', self.xyzSnap)
self.accept('hprSnap', self.hprSnap)
def ignoreArrowKeys(self):
# Accept arrow key events for swinging piece around
self.ignore('left')
@ -3905,85 +3576,5 @@ mouseCrank: aNodePath
self.ignore('up')
self.ignore('down')
def mainMenuDisable(self):
self.clickBoxDictionary['mainMenuButtons'].disable()
self.ignore('activateWallModuleButtons')
self.ignore('activateStreetModuleButtons')
self.ignore('activateLandmarkButtons')
self.ignore('activatePropButtons')
def mainMenuEnable(self):
# Make sure all submenus are hidden
self.subMenuDisable()
# Now enable main menu
self.clickBoxDictionary['mainMenuButtons'].enable
self.accept('activateWallModuleButtons', self.activateWallModuleButtons)
self.accept('activateStreetModuleButtons', self.activateStreetModuleButtons)
self.accept('activateLandmarkButtons', self.activateLandmarkButtons)
self.accept('activatePropButtons', self.activatePropButtons)
def subMenuDisable(self):
for category in self.categorySet:
self.categoryDisable(category)
def vizMenuDisable(self):
self.clickBoxDictionary['vizRegionButtons'].disable()
self.ignore('addVizRegion')
self.ignore('addCollisionSphere')
self.grid.setGridSpacing(5.0)
def initializeLevelEditorButtons(self):
newClickBoxObject = ToggleBoxList(
[('Show Grid', self.showGrid, 0)
('XYZ Snap', self.xyzSnap, 1),
('HPR Snap', self.hprSnap, 1)],
-0.95, 0.90)
newClickBoxObject.alignLeft()
newClickBoxObject.setScale(0.06)
newClickBoxObject.makeAllWideAsWidest()
newClickBoxObject.enable()
self.clickBoxDictionary['gridMenuButtons'] = newClickBoxObject
newClickBoxObject = ClickBoxList(
[('Street modules', self.activateStreetModuleButtons),
('Toon Walls', self.activateWallModuleButtons),
('Landmark bldgs', self.activateLandmarkButtons),
('Props', self.activatePropButtons)],
0.95, 0.90)
newClickBoxObject.setColor(0.5, 0.5, 0.5, 0.5)
newClickBoxObject.setScale(0.06)
newClickBoxObject.alignRight()
self.clickBoxDictionary['mainMenuButtons'] = newClickBoxObject
newClickBoxObject = ClickBox(
'New Group', 0.3, 0.3, -0.95, -0.9,
self.createNewLevelGroup, 0)
newClickBoxObject.nodePath().node().setCardColor(Point4(1,1,1,.5))
newClickBoxObject.setScale(0.05)
self.clickBoxDictionary['groupButton'] = newClickBoxObject
newClickBoxObject = ClickBox(
'Save DNA', 0.3, 0.3, -0.7, -0.9,
self.outputDNA, ['toontown.dna'],0)
newClickBoxObject.nodePath().node().setCardColor(Point4(1,1,1,.5))
newClickBoxObject.setScale(0.05)
self.clickBoxDictionary['saveButton'] = newClickBoxObject
newClickBoxObject = ToggleBox(
'Level Map', 0.3, 0.3, -0.47, -0.9,
self.toggleMapViz, [], 0)
newClickBoxObject.nodePath().node().setCardColor(Point4(1,1,1,.5))
newClickBoxObject.setButtonState(0)
newClickBoxObject.setScale(0.05)
self.clickBoxDictionary['mapButton'] = newClickBoxObject
self.dnaMenuEnable()
# Initialize module Dictionary with pointers to module
# node paths and create module buttons
self.initializeStreetButtons()
self.initializeWallButtons()
self.initializeLandmarkButtons()
self.initializePropButtons()
"""

View File

@ -0,0 +1,457 @@
from PandaObject import *
from Tkinter import *
from Tree import *
import Pmw
# Initialize icon directory
f = Filename('icons')
f.resolveFilename(getModelPath())
ICONDIR = f.toOsSpecific()
if not os.path.isdir(ICONDIR):
raise RuntimeError, "can't find DIRECT icon directory (%s)" % `ICONDIR`
class TreeNode:
def __init__(self, canvas, parent, item, menuList = []):
self.canvas = canvas
self.parent = parent
self.item = item
self.state = 'collapsed'
self.selected = 0
self.children = {}
self.kidKeys = []
self.x = self.y = None
self.iconimages = {} # cache of PhotoImage instances for icons
self.menuList = menuList
self.menuVar = IntVar()
self.menuVar.set(0)
self._popupMenu = None
if self.menuList:
self._popupMenu = Menu(self.canvas, tearoff = 0)
for i in range(len(self.menuList)):
item = self.menuList[i]
self._popupMenu.add_radiobutton(
label = item,
variable = self.menuVar,
value = i,
indicatoron = 0,
command = self.popupMenuCommand)
def destroy(self):
for key in self.kidKeys:
c = self.children[key]
del self.children[key]
c.destroy()
self.parent = None
def geticonimage(self, name):
try:
return self.iconimages[name]
except KeyError:
pass
file, ext = os.path.splitext(name)
ext = ext or ".gif"
fullname = os.path.join(ICONDIR, file + ext)
image = PhotoImage(master=self.canvas, file=fullname)
self.iconimages[name] = image
return image
def select(self, event=None):
if self.selected:
return
self.deselectall()
self.selected = 1
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
self.item.OnSelect()
def deselect(self, event=None):
if not self.selected:
return
self.selected = 0
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
def deselectall(self):
if self.parent:
self.parent.deselectall()
else:
self.deselecttree()
def deselecttree(self):
if self.selected:
self.deselect()
for key in self.kidKeys:
child = self.children[key]
child.deselecttree()
def flip(self, event=None):
if self.state == 'expanded':
self.collapse()
else:
self.expand()
self.item.OnDoubleClick()
return "break"
def selectAndPopupMenu(self, event=None):
self.select()
if self._popupMenu:
self._popupMenu.post(event.widget.winfo_pointerx(),
event.widget.winfo_pointery())
return "break"
def popupMenuCommand(self):
self.item.MenuCommand(self.menuList[self.menuVar.get()])
def expand(self, event=None):
if not self.item._IsExpandable():
return
if self.state != 'expanded':
self.state = 'expanded'
self.update()
self.view()
def collapse(self, event=None):
if self.state != 'collapsed':
self.state = 'collapsed'
self.update()
def view(self):
top = self.y - 2
bottom = self.lastvisiblechild().y + 17
height = bottom - top
visible_top = self.canvas.canvasy(0)
visible_height = self.canvas.winfo_height()
visible_bottom = self.canvas.canvasy(visible_height)
if visible_top <= top and bottom <= visible_bottom:
return
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
if top >= visible_top and height <= visible_height:
fraction = top + height - visible_height
else:
fraction = top
fraction = float(fraction) / y1
self.canvas.yview_moveto(fraction)
def lastvisiblechild(self):
if self.kidKeys and self.state == 'expanded':
return self.children[self.kidKeys[-1]].lastvisiblechild()
else:
return self
def update(self):
if self.parent:
self.parent.update()
else:
oldcursor = self.canvas['cursor']
self.canvas['cursor'] = "watch"
self.canvas.update()
self.canvas.delete(ALL) # XXX could be more subtle
self.draw(7, 2)
x0, y0, x1, y1 = self.canvas.bbox(ALL)
self.canvas.configure(scrollregion=(0, 0, x1, y1))
self.canvas['cursor'] = oldcursor
def draw(self, x, y):
# XXX This hard-codes too many geometry constants!
self.x, self.y = x, y
self.drawicon()
self.drawtext()
if self.state != 'expanded':
return y+17
# draw children
#if not self.children:
#self.children = []
sublist = self.item._GetSubList()
if not sublist:
# _IsExpandable() was mistaken; that's allowed
return y+17
self.kidKeys = []
for item in sublist:
key = item.nodePath.id()
if self.children.has_key(key):
child = self.children[key]
else:
child = TreeNode(self.canvas, self, item, self.menuList)
self.children[key] = child
cx = x+20
cy = y+17
cylast = 0
for key in self.kidKeys:
child = self.children[key]
cylast = cy
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
cy = child.draw(cx, cy)
if child.item._IsExpandable():
if child.state == 'expanded':
iconname = "minusnode"
callback = child.collapse
else:
iconname = "plusnode"
callback = child.expand
image = self.geticonimage(iconname)
id = self.canvas.create_image(x+9, cylast+7, image=image)
# XXX This leaks bindings until canvas is deleted:
self.canvas.tag_bind(id, "<1>", callback)
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
fill="gray50")
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
return cy
def drawicon(self):
if self.selected:
imagename = (self.item.GetSelectedIconName() or
self.item.GetIconName() or
"openfolder")
else:
imagename = self.item.GetIconName() or "folder"
image = self.geticonimage(imagename)
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
self.image_id = id
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
self.canvas.tag_bind(id, "<3>", self.selectAndPopupMenu)
def drawtext(self):
textx = self.x+20-1
texty = self.y-1
labeltext = self.item.GetLabelText()
if labeltext:
id = self.canvas.create_text(textx, texty, anchor="nw",
text=labeltext)
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
self.canvas.tag_bind(id, "<3>", self.selectAndPopupMenu)
x0, y0, x1, y1 = self.canvas.bbox(id)
textx = max(x1, 200) + 10
text = self.item.GetText() or "<no text>"
try:
self.entry
except AttributeError:
pass
else:
self.edit_finish()
try:
label = self.label
except AttributeError:
# padding carefully selected (on Windows) to match Entry widget:
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
if self.selected:
self.label.configure(fg="white", bg="darkblue")
else:
self.label.configure(fg="black", bg="white")
id = self.canvas.create_window(textx, texty,
anchor="nw", window=self.label)
self.label.bind("<1>", self.select_or_edit)
self.label.bind("<Double-1>", self.flip)
self.text_id = id
def select_or_edit(self, event=None):
if self.selected and self.item.IsEditable():
self.edit(event)
else:
self.select(event)
def edit(self, event=None):
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
self.entry.insert(0, self.label['text'])
self.entry.selection_range(0, END)
self.entry.pack(ipadx=5)
self.entry.focus_set()
self.entry.bind("<Return>", self.edit_finish)
self.entry.bind("<Escape>", self.edit_cancel)
def edit_finish(self, event=None):
try:
entry = self.entry
del self.entry
except AttributeError:
return
text = entry.get()
entry.destroy()
if text and text != self.item.GetText():
self.item.SetText(text)
text = self.item.GetText()
self.label['text'] = text
self.drawtext()
self.canvas.focus_set()
def edit_cancel(self, event=None):
self.drawtext()
self.canvas.focus_set()
class TreeItem:
"""Abstract class representing tree items.
Methods should typically be overridden, otherwise a default action
is used.
"""
def __init__(self):
"""Constructor. Do whatever you need to do."""
def GetText(self):
"""Return text string to display."""
def GetLabelText(self):
"""Return label text string to display in front of text (if any)."""
expandable = None
def _IsExpandable(self):
"""Do not override! Called by TreeNode."""
if self.expandable is None:
self.expandable = self.IsExpandable()
return self.expandable
def IsExpandable(self):
"""Return whether there are subitems."""
return 1
def _GetSubList(self):
"""Do not override! Called by TreeNode."""
if not self.IsExpandable():
return []
sublist = self.GetSubList()
return sublist
def IsEditable(self):
"""Return whether the item's text may be edited."""
def SetText(self, text):
"""Change the item's text (if it is editable)."""
def GetIconName(self):
"""Return name of icon to be displayed normally."""
def GetSelectedIconName(self):
"""Return name of icon to be displayed when selected."""
def GetSubList(self):
"""Return list of items forming sublist."""
def OnDoubleClick(self):
"""Called on a double-click on the item."""
def OnSelect(self):
"""Called when item selected."""
class SceneGraphExplorer(Pmw.MegaWidget):
"Graphical display of a scene graph"
def __init__(self, root = render, parent = None, **kw):
# Define the megawidget options.
optiondefs = ()
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,
['SGESelect'])
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.getNodePathName()
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 MenuCommand(self, command):
if (command == 'SGESelect'):
messenger.send('SGESelectNodePath', [self.nodePath])

View File

@ -0,0 +1,356 @@
# ADAPTED FROM IDLE TreeWidget.py
# XXX TO DO:
# - popup menu
# - support partial or total redisplay
# - key bindings (instead of quick-n-dirty bindings on Canvas):
# - up/down arrow keys to move focus around
# - ditto for page up/down, home/end
# - left/right arrows to expand/collapse & move out/in
# - more doc strings
# - add icons for "file", "module", "class", "method"; better "python" icon
# - callback for selection???
# - multiple-item selection
# - tooltips
# - redo geometry without magic numbers
# - keep track of object ids to allow more careful cleaning
# - optimize tree redraw after expand of subnode
import os
import sys
import string
from Tkinter import *
from PandaObject import *
# Initialize icon directory
f = Filename('icons')
f.resolveFilename(getModelPath())
ICONDIR = f.toOsSpecific()
if not os.path.isdir(ICONDIR):
raise RuntimeError, "can't find DIRECT icon directory (%s)" % `ICONDIR`
class TreeNode:
def __init__(self, canvas, parent, item, menuList = []):
self.canvas = canvas
self.parent = parent
self.item = item
self.state = 'collapsed'
self.selected = 0
self.children = []
self.x = self.y = None
self.iconimages = {} # cache of PhotoImage instances for icons
self.menuList = menuList
self.menuVar = IntVar()
self.menuVar.set(0)
self._popupMenu = None
if self.menuList:
self._popupMenu = Menu(self.canvas, tearoff = 0)
for i in range(len(self.menuList)):
item = self.menuList[i]
self._popupMenu.add_radiobutton(
label = item,
variable = self.menuVar,
value = i,
indicatoron = 0,
command = self.popupMenuCommand)
def destroy(self):
for c in self.children[:]:
self.children.remove(c)
c.destroy()
self.parent = None
def geticonimage(self, name):
try:
return self.iconimages[name]
except KeyError:
pass
file, ext = os.path.splitext(name)
ext = ext or ".gif"
fullname = os.path.join(ICONDIR, file + ext)
image = PhotoImage(master=self.canvas, file=fullname)
self.iconimages[name] = image
return image
def select(self, event=None):
if self.selected:
return
self.deselectall()
self.selected = 1
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
self.item.OnSelect()
def deselect(self, event=None):
if not self.selected:
return
self.selected = 0
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
def deselectall(self):
if self.parent:
self.parent.deselectall()
else:
self.deselecttree()
def deselecttree(self):
if self.selected:
self.deselect()
for child in self.children:
child.deselecttree()
def flip(self, event=None):
if self.state == 'expanded':
self.collapse()
else:
self.expand()
self.item.OnDoubleClick()
return "break"
def selectAndPopupMenu(self, event=None):
self.select()
if self._popupMenu:
self._popupMenu.post(event.widget.winfo_pointerx(),
event.widget.winfo_pointery())
return "break"
def popupMenuCommand(self):
self.item.MenuCommand(self.menuList[self.menuVar.get()])
def expand(self, event=None):
if not self.item._IsExpandable():
return
if self.state != 'expanded':
self.state = 'expanded'
self.update()
self.view()
def collapse(self, event=None):
if self.state != 'collapsed':
self.state = 'collapsed'
self.update()
def view(self):
top = self.y - 2
bottom = self.lastvisiblechild().y + 17
height = bottom - top
visible_top = self.canvas.canvasy(0)
visible_height = self.canvas.winfo_height()
visible_bottom = self.canvas.canvasy(visible_height)
if visible_top <= top and bottom <= visible_bottom:
return
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
if top >= visible_top and height <= visible_height:
fraction = top + height - visible_height
else:
fraction = top
fraction = float(fraction) / y1
self.canvas.yview_moveto(fraction)
def lastvisiblechild(self):
if self.children and self.state == 'expanded':
return self.children[-1].lastvisiblechild()
else:
return self
def update(self):
if self.parent:
self.parent.update()
else:
oldcursor = self.canvas['cursor']
self.canvas['cursor'] = "watch"
self.canvas.update()
self.canvas.delete(ALL) # XXX could be more subtle
self.draw(7, 2)
x0, y0, x1, y1 = self.canvas.bbox(ALL)
self.canvas.configure(scrollregion=(0, 0, x1, y1))
self.canvas['cursor'] = oldcursor
def draw(self, x, y):
# XXX This hard-codes too many geometry constants!
self.x, self.y = x, y
self.drawicon()
self.drawtext()
if self.state != 'expanded':
return y+17
# draw children
#if not self.children:
sublist = self.item._GetSubList()
if not sublist:
# _IsExpandable() was mistaken; that's allowed
return y+17
#self.children = []
for item in sublist:
child = TreeNode(self.canvas, self, item, self.menuList)
self.children.append(child)
cx = x+20
cy = y+17
cylast = 0
for child in self.children:
cylast = cy
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
cy = child.draw(cx, cy)
if child.item._IsExpandable():
if child.state == 'expanded':
iconname = "minusnode"
callback = child.collapse
else:
iconname = "plusnode"
callback = child.expand
image = self.geticonimage(iconname)
id = self.canvas.create_image(x+9, cylast+7, image=image)
# XXX This leaks bindings until canvas is deleted:
self.canvas.tag_bind(id, "<1>", callback)
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
fill="gray50")
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
return cy
def drawicon(self):
if self.selected:
imagename = (self.item.GetSelectedIconName() or
self.item.GetIconName() or
"openfolder")
else:
imagename = self.item.GetIconName() or "folder"
image = self.geticonimage(imagename)
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
self.image_id = id
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
self.canvas.tag_bind(id, "<3>", self.selectAndPopupMenu)
def drawtext(self):
textx = self.x+20-1
texty = self.y-1
labeltext = self.item.GetLabelText()
if labeltext:
id = self.canvas.create_text(textx, texty, anchor="nw",
text=labeltext)
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
self.canvas.tag_bind(id, "<3>", self.selectAndPopupMenu)
x0, y0, x1, y1 = self.canvas.bbox(id)
textx = max(x1, 200) + 10
text = self.item.GetText() or "<no text>"
try:
self.entry
except AttributeError:
pass
else:
self.edit_finish()
try:
label = self.label
except AttributeError:
# padding carefully selected (on Windows) to match Entry widget:
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
if self.selected:
self.label.configure(fg="white", bg="darkblue")
else:
self.label.configure(fg="black", bg="white")
id = self.canvas.create_window(textx, texty,
anchor="nw", window=self.label)
self.label.bind("<1>", self.select_or_edit)
self.label.bind("<Double-1>", self.flip)
self.text_id = id
def select_or_edit(self, event=None):
if self.selected and self.item.IsEditable():
self.edit(event)
else:
self.select(event)
def edit(self, event=None):
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
self.entry.insert(0, self.label['text'])
self.entry.selection_range(0, END)
self.entry.pack(ipadx=5)
self.entry.focus_set()
self.entry.bind("<Return>", self.edit_finish)
self.entry.bind("<Escape>", self.edit_cancel)
def edit_finish(self, event=None):
try:
entry = self.entry
del self.entry
except AttributeError:
return
text = entry.get()
entry.destroy()
if text and text != self.item.GetText():
self.item.SetText(text)
text = self.item.GetText()
self.label['text'] = text
self.drawtext()
self.canvas.focus_set()
def edit_cancel(self, event=None):
self.drawtext()
self.canvas.focus_set()
class TreeItem:
"""Abstract class representing tree items.
Methods should typically be overridden, otherwise a default action
is used.
"""
def __init__(self):
"""Constructor. Do whatever you need to do."""
def GetText(self):
"""Return text string to display."""
def GetLabelText(self):
"""Return label text string to display in front of text (if any)."""
expandable = None
def _IsExpandable(self):
"""Do not override! Called by TreeNode."""
if self.expandable is None:
self.expandable = self.IsExpandable()
return self.expandable
def IsExpandable(self):
"""Return whether there are subitems."""
return 1
def _GetSubList(self):
"""Do not override! Called by TreeNode."""
if not self.IsExpandable():
return []
sublist = self.GetSubList()
if not sublist:
self.expandable = 0
return sublist
def IsEditable(self):
"""Return whether the item's text may be edited."""
def SetText(self, text):
"""Change the item's text (if it is editable)."""
def GetIconName(self):
"""Return name of icon to be displayed normally."""
def GetSelectedIconName(self):
"""Return name of icon to be displayed when selected."""
def GetSubList(self):
"""Return list of items forming sublist."""
def OnDoubleClick(self):
"""Called on a double-click on the item."""
def OnSelect(self):
"""Called when item selected."""