mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 08:44:19 -04:00
*** empty log message ***
This commit is contained in:
parent
823f0612f5
commit
8dbea4d196
@ -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()
|
||||
|
||||
"""
|
||||
|
457
direct/src/tkwidgets/SceneGraphExplorer.py
Normal file
457
direct/src/tkwidgets/SceneGraphExplorer.py
Normal 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])
|
||||
|
||||
|
356
direct/src/tkwidgets/Tree.py
Normal file
356
direct/src/tkwidgets/Tree.py
Normal 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."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user