1801d2b9fb
UD/AI + Client boots up.
1054 lines
44 KiB
Python
1054 lines
44 KiB
Python
from panda3d.core import Point3, Vec3, VBase3
|
|
from direct.tkwidgets.AppShell import *
|
|
from direct.showbase.TkGlobal import *
|
|
from direct.tkwidgets.Tree import *
|
|
from direct.tkwidgets import Slider, Floater
|
|
from tkinter.simpledialog import askstring
|
|
from tkinter.messagebox import showwarning, askyesno
|
|
from tkinter import *
|
|
from direct.showbase.PythonUtil import Functor, list2dict
|
|
from direct.gui.DirectGui import DGG
|
|
import tkinter.filedialog
|
|
from direct.showbase import DirectObject
|
|
import math
|
|
import operator
|
|
from direct.tkwidgets import Valuator
|
|
from direct.tkwidgets import VectorWidgets
|
|
from otp.level import LevelConstants
|
|
from direct.directtools import DirectSession
|
|
import Pmw
|
|
|
|
class InGameEditor(AppShell):
|
|
appname = 'In-Game Editor'
|
|
frameWidth = 900
|
|
frameHeight = 475
|
|
usecommandarea = 1
|
|
usestatusarea = 1
|
|
contactname = 'Darren Ranalli'
|
|
contactphone = '(818) 623-3904'
|
|
contactemail = 'darren.ranalli@disney.com'
|
|
WantUndo = False
|
|
|
|
def __init__(self, level, doneEvent, requestSaveEvent, saveAsEvent, undoEvent, redoEvent, wireframeEvent, oobeEvent, csEvent, runEvent, texEvent, **kw):
|
|
DGG.INITOPT = Pmw.INITOPT
|
|
optiondefs = (('title', 'In-Game ' + level.getName() + ' Editor', None),)
|
|
self.defineoptions(kw, optiondefs)
|
|
self.level = level
|
|
self.doneEvent = doneEvent
|
|
self.requestSaveEvent = requestSaveEvent
|
|
self.saveAsEvent = saveAsEvent
|
|
self.undoEvent = undoEvent
|
|
self.redoEvent = redoEvent
|
|
self.wireframeEvent = wireframeEvent
|
|
self.oobeEvent = oobeEvent
|
|
self.csEvent = csEvent
|
|
self.runEvent = runEvent
|
|
self.texEvent = texEvent
|
|
self.entityCopy = None
|
|
self.curEntId = None
|
|
self.curEntWidgets = {}
|
|
self.visZonesEditor = None
|
|
AppShell.__init__(self)
|
|
self.initialiseoptions(self.__class__)
|
|
self.accept(self.level.getAttribChangeEventName(), self.handleAttribChange)
|
|
self.accept('DIRECT_selectedNodePath', self.selectedNodePathHook)
|
|
self.accept('DIRECT_manipulateObjectCleanup', self.manipCleanupHook)
|
|
self.accept('DIRECT_undo', self.manipCleanupHook)
|
|
self.accept('DIRECT_redo', self.manipCleanupHook)
|
|
return
|
|
|
|
def getEventMsgName(self, event):
|
|
return 'InGameEditor%s_%s' % (self.level.getLevelId(), event)
|
|
|
|
def getEntityName(self, entId):
|
|
return self.level.levelSpec.getEntitySpec(entId)['name']
|
|
|
|
def appInit(self):
|
|
from direct.directtools import DirectSession
|
|
direct.enableLight()
|
|
direct.disable()
|
|
bboard.post(DirectSession.DirectSession.DIRECTdisablePost)
|
|
|
|
def createMenuBar(self):
|
|
menuBar = self.menuBar
|
|
menuBar.addmenuitem('File', 'command', 'Save the level spec on the AI', label='Save on AI', command=self.handleRequestSave)
|
|
menuBar.addmenuitem('File', 'command', 'Save the level spec locally', label='Save Locally As...', command=self.handleSaveAs)
|
|
menuBar.addmenuitem('File', 'separator')
|
|
menuBar.addmenuitem('File', 'command', 'Export a single entity', label='Export Entity...', command=self.level.handleExportEntity)
|
|
menuBar.addmenuitem('File', 'command', 'Export a tree of entities', label='Export Entity Tree...', command=self.level.handleExportEntityTree)
|
|
menuBar.addmenuitem('File', 'separator')
|
|
menuBar.addmenuitem('File', 'command', 'Import a tree of entities', label='Import Entities...', command=self.level.handleImportEntities)
|
|
menuBar.addmenuitem('File', 'separator')
|
|
if InGameEditor.WantUndo:
|
|
menuBar.addmenu('Edit', 'Edit Operations')
|
|
menuBar.addmenuitem('Edit', 'command', 'Undo the last edit', label='Undo', command=self.doUndo)
|
|
menuBar.addmenuitem('Edit', 'command', 'Redo the last undo', label='Redo', command=self.doRedo)
|
|
menuBar.addmenu('Entity', 'Entity Operations')
|
|
menuBar.addmenuitem('Entity', 'command', 'Duplicate Selected Entity', label='Duplicate Selected Entity', command=self.level.duplicateSelectedEntity)
|
|
menuBar.addmenuitem('Entity', 'separator')
|
|
permanentTypes = self.level.entTypeReg.getPermanentTypeNames()
|
|
entTypes = list(self.level.entTypes)
|
|
for permanentType in permanentTypes:
|
|
entTypes.remove(permanentType)
|
|
entTypes.sort()
|
|
numEntities = len(entTypes)
|
|
cascadeMenu = ''
|
|
for index in range(numEntities):
|
|
type = entTypes[index]
|
|
if index % 10 == 0:
|
|
lastIndex = min(index + 9, numEntities - 1)
|
|
cascadeMenu = '%s->%s' % (entTypes[index], entTypes[lastIndex])
|
|
menuBar.addcascademenu('Entity', cascadeMenu)
|
|
|
|
def doInsert(self = self, type = type):
|
|
self.level.insertEntity(type)
|
|
self.explorer.update(fUseCachedChildren=0)
|
|
|
|
menuBar.addmenuitem(cascadeMenu, 'command', 'Insert %s' % type, label='Insert %s' % type, command=doInsert)
|
|
|
|
menuBar.addmenuitem('Entity', 'separator')
|
|
menuBar.addmenuitem('Entity', 'command', 'Remove Selected Entity', label='Remove Selected Entity', command=self.removeSelectedEntity)
|
|
menuBar.addmenuitem('Entity', 'command', 'Remove Selected EntityTree', label='Remove Selected Entity Tree', command=self.removeSelectedEntityTree)
|
|
menuBar.addmenuitem('Entity', 'separator')
|
|
menuBar.addmenuitem('Entity', 'command', 'Go To Selected Entity', label='Go To Selected Entity', command=self.level.moveAvToSelected)
|
|
menuBar.addmenuitem('Entity', 'command', 'Refresh Entity Explorer', label='Refresh Entity Explorer', command=self.refreshExplorer)
|
|
self.menuBar.addmenu('DIRECT', 'Direct Session Panel Operations')
|
|
self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Enabled', label='Enable', variable=direct.panel.directEnabled, command=direct.panel.toggleDirect)
|
|
self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Grid Enabled', label='Enable Grid', variable=direct.panel.directGridEnabled, command=direct.panel.toggleDirectGrid)
|
|
self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Object Handles Visability', label='Toggle Widget Viz', command=direct.toggleWidgetVis)
|
|
self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Widget Move/COA Mode', label='Toggle Widget Mode', command=direct.manipulationControl.toggleObjectHandlesMode)
|
|
self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Widget On Top', label='Widget On Top', variable=direct.panel.directWidgetOnTop, command=direct.panel.toggleWidgetOnTop)
|
|
self.menuBar.addmenuitem('DIRECT', 'command', 'Deselect All', label='Deselect All', command=direct.deselectAll)
|
|
if InGameEditor.WantUndo:
|
|
undoFrame = Frame(self.menuFrame)
|
|
self.redoButton = Button(undoFrame, text='Redo', command=self.doRedo)
|
|
self.redoButton.pack(side=RIGHT, expand=0)
|
|
self.undoButton = Button(undoFrame, text='Undo', command=self.doUndo)
|
|
self.undoButton.pack(side=RIGHT, expand=0)
|
|
undoFrame.pack(side=RIGHT, expand=0)
|
|
toggleFrame = Frame(self.menuFrame)
|
|
self.wireframeButton = Button(toggleFrame, text='Wire', command=self.doWireframe)
|
|
self.wireframeButton.pack(side=RIGHT, expand=0)
|
|
self.texButton = Button(toggleFrame, text='Tex', command=self.doTex)
|
|
self.texButton.pack(side=RIGHT, expand=0)
|
|
self.csButton = Button(toggleFrame, text='Cs', command=self.doCs)
|
|
self.csButton.pack(side=RIGHT, expand=0)
|
|
self.runButton = Button(toggleFrame, text='Run', command=self.doRun)
|
|
self.runButton.pack(side=RIGHT, expand=0)
|
|
self.oobeButton = Button(toggleFrame, text='Oobe', command=self.doOobe)
|
|
self.oobeButton.pack(side=RIGHT, expand=0)
|
|
toggleFrame.pack(side=RIGHT, expand=0, padx=5)
|
|
AppShell.createMenuBar(self)
|
|
|
|
def createInterface(self):
|
|
interior = self.interior()
|
|
mainFrame = Frame(interior)
|
|
self.framePane = Pmw.PanedWidget(mainFrame, orient=DGG.HORIZONTAL)
|
|
self.explorerFrame = self.framePane.add('left', min=250)
|
|
self.widgetFrame = self.framePane.add('right', min=300)
|
|
self.explorer = LevelExplorer(parent=self.explorerFrame, editor=self)
|
|
self.explorer.pack(fill=BOTH, expand=1)
|
|
self.explorerFrame.pack(fill=BOTH, expand=1)
|
|
self.pageOneFrame = Pmw.ScrolledFrame(self.widgetFrame, borderframe_relief=GROOVE, horizflex='elastic')
|
|
self.pageOneFrame.pack(expand=1, fill=BOTH)
|
|
self.widgetFrame.pack(fill=BOTH, expand=1)
|
|
self.framePane.pack(fill=BOTH, expand=1)
|
|
mainFrame.pack(fill=BOTH, expand=1)
|
|
self.createButtons()
|
|
self.initialiseoptions(self.__class__)
|
|
self.attribWidgets = []
|
|
|
|
def selectedNodePathHook(self, nodePath):
|
|
np = nodePath.findNetTag('entity')
|
|
if not np.isEmpty():
|
|
if np.id() != nodePath.id():
|
|
np.select()
|
|
else:
|
|
self.findEntityFromNP(np)
|
|
|
|
def findEntityFromNP(self, nodePath):
|
|
entId = self.level.nodePathId2EntId.get(nodePath.id())
|
|
if entId:
|
|
self.selectEntity(entId)
|
|
else:
|
|
for entId in self.level.levelSpec.getAllEntIds():
|
|
np = self.level.getEntInstanceNP(entId)
|
|
if np:
|
|
if np.id() == nodePath.id():
|
|
self.selectEntity(entId)
|
|
return
|
|
|
|
def manipCleanupHook(self, nodePathList):
|
|
if not nodePathList:
|
|
return
|
|
entId = self.level.nodePathId2EntId.get(nodePathList[0].id())
|
|
if entId:
|
|
t = nodePathList[0].getTransform()
|
|
entSpec = self.level.levelSpec.getEntitySpec(entId)
|
|
entPos = entSpec.get('pos')
|
|
if entPos and t.getPos() != entPos:
|
|
self.level.setAttribEdit(entId, 'pos', Point3(t.getPos()))
|
|
entHpr = entSpec.get('hpr')
|
|
if entHpr and t.getHpr() != entHpr:
|
|
self.level.setAttribEdit(entId, 'hpr', Vec3(t.getHpr()))
|
|
entScale = entSpec.get('scale')
|
|
if entScale and t.getScale() != entScale:
|
|
self.level.setAttribEdit(entId, 'scale', Vec3(t.getScale()))
|
|
|
|
def refreshExplorer(self):
|
|
self.explorer.update()
|
|
|
|
def selectEntity(self, entId):
|
|
node = self.explorer._node.find(entId)
|
|
if node:
|
|
node.reveal()
|
|
self.explorer._node.deselecttree()
|
|
node.select()
|
|
|
|
def removeSelectedEntity(self):
|
|
if self.level.selectedEntity:
|
|
parentEntId = self.level.selectedEntity.parentEntId
|
|
else:
|
|
parentEntId = None
|
|
if self.level.removeSelectedEntity() != -1:
|
|
self.explorer.update(fUseCachedChildren=0)
|
|
if parentEntId:
|
|
self.selectEntity(parentEntId)
|
|
return
|
|
|
|
def removeSelectedEntityTree(self):
|
|
if self.level.selectedEntity:
|
|
parentEntId = self.level.selectedEntity.parentEntId
|
|
else:
|
|
parentEntId = None
|
|
if self.level.removeSelectedEntityTree() != -1:
|
|
self.explorer.update(fUseCachedChildren=0)
|
|
if parentEntId:
|
|
self.selectEntity(parentEntId)
|
|
return
|
|
|
|
def clearAttribEditPane(self):
|
|
for widg in self.attribWidgets:
|
|
widg.destroy()
|
|
|
|
if self.visZonesEditor:
|
|
self.visZonesEditor.destroy()
|
|
self.attribWidgets = []
|
|
self.curEntId = None
|
|
self.curEntWidgets = {}
|
|
self.cbDict = {}
|
|
return
|
|
|
|
def updateEntityCopy(self, entId):
|
|
if self.entityCopy == None:
|
|
self.entityCopy = self.level.getEntInstanceNPCopy(entId)
|
|
if self.entityCopy is not None:
|
|
self.entityCopy.setRenderModeWireframe()
|
|
self.entityCopy.setTextureOff(1)
|
|
self.entityCopy.setColor(1, 0, 0)
|
|
return
|
|
|
|
def updateAttribEditPane(self, entId, levelSpec, entTypeReg):
|
|
self.clearAttribEditPane()
|
|
self.curEntId = entId
|
|
widgetSetter = None
|
|
entSpec = levelSpec.getEntitySpec(entId)
|
|
typeDesc = entTypeReg.getTypeDesc(entSpec['type'])
|
|
attribNames = typeDesc.getAttribNames()
|
|
attribDescs = typeDesc.getAttribDescDict()
|
|
for attribName in attribNames:
|
|
desc = attribDescs[attribName]
|
|
params = desc.getParams()
|
|
datatype = desc.getDatatype()
|
|
if datatype == 'int':
|
|
self.addIntWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'float':
|
|
self.addFloatWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'bool':
|
|
self.addBoolWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'choice':
|
|
self.addChoiceWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'multiChoice':
|
|
self.addMultiChoiceWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype in ['pos', 'hpr', 'scale']:
|
|
self.addVec3Widget(levelSpec, entSpec, entId, attribName, params, datatype)
|
|
elif datatype == 'color':
|
|
self.addColorWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'bamfilename':
|
|
self.addFileWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'visZoneList':
|
|
self.addVisZoneWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'entId':
|
|
self.addEntIdWidget(levelSpec, entSpec, entId, attribName, params, entTypeReg)
|
|
elif datatype == 'string':
|
|
self.addStringWidget(levelSpec, entSpec, entId, attribName, params)
|
|
elif datatype == 'const':
|
|
self.addConstWidget(levelSpec, entSpec, entId, attribName, params)
|
|
else:
|
|
self.addPythonWidget(levelSpec, entSpec, entId, attribName, params)
|
|
|
|
return
|
|
|
|
def addIntWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
minVal = params.get('min', None)
|
|
maxVal = params.get('max', None)
|
|
if minVal is not None and maxVal is not None:
|
|
widgClass = Slider.Slider
|
|
else:
|
|
widgClass = Floater.Floater
|
|
widg = widgClass(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], numDigits=0, label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal, resolution=1.0)
|
|
|
|
def clientIntCommand(val):
|
|
entity = self.level.getEntInstance(entId)
|
|
if entity:
|
|
entity.handleAttribChange(attribName, int(val))
|
|
|
|
def finalIntCommand():
|
|
self.level.setAttribEdit(entId, attribName, int(widg.get()))
|
|
|
|
widg['command'] = clientIntCommand
|
|
widg['postCallback'] = finalIntCommand
|
|
widg.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(widg)
|
|
self.curEntWidgets[attribName] = lambda x: widg.set(x, 0)
|
|
return
|
|
|
|
def addFloatWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
minVal = params.get('min', None)
|
|
maxVal = params.get('max', None)
|
|
widg = Floater.Floater(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal)
|
|
|
|
def clientFloatCommand(val):
|
|
entity = self.level.getEntInstance(entId)
|
|
if entity:
|
|
entity.handleAttribChange(attribName, val)
|
|
|
|
def finalFloatCommand():
|
|
self.level.setAttribEdit(entId, attribName, widg.get())
|
|
|
|
widg['command'] = clientFloatCommand
|
|
widg['postCallback'] = finalFloatCommand
|
|
widg.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(widg)
|
|
self.curEntWidgets[attribName] = lambda x: widg.set(x, 0)
|
|
return
|
|
|
|
def addBoolWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
flag = BooleanVar()
|
|
flag.set(entSpec[attribName])
|
|
|
|
def booleanCommand(booleanVar = flag):
|
|
self.level.setAttribEdit(entId, attribName, flag.get())
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
trueButton = Radiobutton(frame, text='True', value=1, variable=flag, command=booleanCommand)
|
|
trueButton.pack(side=LEFT, expand=0)
|
|
falseButton = Radiobutton(frame, text='False', value=0, variable=flag, command=booleanCommand)
|
|
falseButton.pack(side=LEFT, expand=0)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
self.curEntWidgets[attribName] = flag.set
|
|
|
|
def addChoiceWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
if attribName in entSpec:
|
|
attributeValue = entSpec.get(attribName)
|
|
else:
|
|
typeDesc = self.level.entTypeReg.getTypeDesc(entSpec['type'])
|
|
attribDesc = typeDesc.getAttribDescDict()[attribName]
|
|
attributeValue = attribDesc.getDefaultValue()
|
|
valueDict = params.get('valueDict', {})
|
|
for key, value in list(valueDict.items()):
|
|
if value == attributeValue:
|
|
attributeValue = key
|
|
break
|
|
|
|
radioVar = StringVar()
|
|
radioVar.set(attributeValue)
|
|
|
|
def radioCommand(radioVar = radioVar):
|
|
value = valueDict.get(radioVar.get(), radioVar.get())
|
|
self.level.setAttribEdit(entId, attribName, value)
|
|
|
|
frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2)
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
for choice in params.get('choiceSet', []):
|
|
if type(choice) is str:
|
|
choiceStr = choice
|
|
else:
|
|
choiceStr = repr(choice)
|
|
if choiceStr not in valueDict:
|
|
valueDict[choiceStr] = choice
|
|
choiceButton = Radiobutton(frame, text=choiceStr, value=choiceStr, variable=radioVar, command=radioCommand)
|
|
choiceButton.pack(side=LEFT, expand=0)
|
|
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
|
|
def setRadioVar(attributeValue):
|
|
for key, value in list(valueDict.items()):
|
|
if value == attributeValue:
|
|
attributeValue = key
|
|
break
|
|
|
|
radioVar.set(attributeValue)
|
|
|
|
self.curEntWidgets[attribName] = setRadioVar
|
|
|
|
def addMultiChoiceWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2)
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
valueDict = params.get('valueDict', {})
|
|
self.cbDict[attribName] = list2dict(entSpec[attribName], value=1)
|
|
checkbuttonDict = {}
|
|
base.cbButtons = []
|
|
base.checkbuttonDict = checkbuttonDict
|
|
for choice in params.get('choiceSet', []):
|
|
trueValue = valueDict.get(choice, choice)
|
|
cbVar = IntVar()
|
|
cbVar.set(trueValue in self.cbDict[attribName])
|
|
checkbuttonDict[trueValue] = cbVar
|
|
|
|
def cbCommand(var, trueValue = trueValue):
|
|
vd = self.cbDict[attribName]
|
|
print(vd)
|
|
if var.get():
|
|
print('got it', trueValue, vd)
|
|
vd[trueValue] = 1
|
|
else:
|
|
print('not it', trueValue, vd)
|
|
if trueValue in vd:
|
|
del vd[trueValue]
|
|
value = list(vd.keys())
|
|
print('SENDING', value)
|
|
self.level.setAttribEdit(entId, attribName, value)
|
|
|
|
if type(choice) is str:
|
|
labelStr = choice
|
|
else:
|
|
labelStr = repr(choice)
|
|
func = Functor(cbCommand, cbVar)
|
|
choiceButton = Checkbutton(frame, text=labelStr, variable=cbVar, command=lambda : func())
|
|
choiceButton.pack(side=LEFT, expand=0)
|
|
base.cbButtons.append(choiceButton)
|
|
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
|
|
def setCheckbuttonVar(attributeValueList):
|
|
print('COMING BACK', attributeValueList)
|
|
for attributeValue, cb in list(checkbuttonDict.items()):
|
|
if attributeValue in attributeValueList:
|
|
cb.set(1)
|
|
else:
|
|
cb.set(0)
|
|
|
|
self.curEntWidgets[attribName] = setCheckbuttonVar
|
|
|
|
def addVec3Widget(self, levelSpec, entSpec, entId, attribName, params, datatype):
|
|
|
|
def veCommand(poslist):
|
|
self.level.setAttribEdit(entId, attribName, Point3(*poslist))
|
|
|
|
vec = entSpec[attribName]
|
|
if not isinstance(vec, VBase3):
|
|
vec = Vec3(vec)
|
|
value = [vec[0], vec[1], vec[2]]
|
|
minVal = maxVal = None
|
|
if datatype == 'pos':
|
|
floaterLabels = ['x', 'y', 'z']
|
|
floaterType = 'floater'
|
|
elif datatype == 'hpr':
|
|
floaterLabels = ['h', 'p', 'r']
|
|
floaterType = 'angledial'
|
|
else:
|
|
floaterLabels = ['sx', 'sy', 'sz']
|
|
floaterType = 'slider'
|
|
minVal = 0
|
|
maxVal = 1000
|
|
widg = VectorWidgets.VectorEntry(self.pageOneFrame.interior(), text=attribName, value=value, type=floaterType, bd=0, relief=None, min=minVal, max=maxVal, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels)
|
|
widg['command'] = veCommand
|
|
widg.pack(fill=X, expand=1)
|
|
if attribName in ('pos', 'hpr', 'scale'):
|
|
|
|
def placeEntity():
|
|
selectedEntityNP = self.level.getEntInstanceNP(entId)
|
|
if selectedEntityNP is not None:
|
|
selectedEntityNP.place()
|
|
return
|
|
|
|
widg.menu.add_command(label='Place...', command=placeEntity)
|
|
|
|
def adjustCopy(vec):
|
|
self.updateEntityCopy(entId)
|
|
if self.entityCopy is not None:
|
|
if datatype == 'pos':
|
|
self.entityCopy.setPos(vec[0], vec[1], vec[2])
|
|
elif datatype == 'hpr':
|
|
self.entityCopy.setHpr(vec[0], vec[1], vec[2])
|
|
elif datatype == 'scale':
|
|
self.entityCopy.setScale(vec[0], vec[1], vec[2])
|
|
widg.set(vec, 0)
|
|
return
|
|
|
|
widg._floaters['command'] = adjustCopy
|
|
widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z: veCommand([x, y, z])
|
|
self.attribWidgets.append(widg)
|
|
self.curEntWidgets[attribName] = lambda x: widg.set(x, 0)
|
|
return
|
|
|
|
def addColorWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
|
|
def veCommand(colorlist):
|
|
self.level.setAttribEdit(entId, attribName, Vec4(*colorlist) / 255.0)
|
|
|
|
vec = entSpec[attribName]
|
|
value = [vec[0] * 255.0,
|
|
vec[1] * 255.0,
|
|
vec[2] * 255.0,
|
|
vec[3] * 255.0]
|
|
floaterLabels = ['r',
|
|
'g',
|
|
'b',
|
|
'a']
|
|
widg = VectorWidgets.ColorEntry(self.pageOneFrame.interior(), text=attribName, type='slider', relief=None, bd=0, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels, value=value, floaterGroup_value=value)
|
|
widg['command'] = veCommand
|
|
widg.pack(fill=X, expand=1)
|
|
|
|
def adjustEntity(vec):
|
|
entity = self.level.getEntInstance(entId)
|
|
if entity is not None:
|
|
entity.setColor(vec[0] / 255.0, vec[1] / 255.0, vec[2] / 255.0, vec[3] / 255.0)
|
|
widg.set(vec, 0)
|
|
return
|
|
|
|
widg._floaters['command'] = adjustEntity
|
|
widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z, a: veCommand([x,
|
|
y,
|
|
z,
|
|
a])
|
|
self.attribWidgets.append(widg)
|
|
self.curEntWidgets[attribName] = lambda x: widg.set(x * 255.0, 0)
|
|
return
|
|
|
|
def addFileWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
text = StringVar()
|
|
text.set(repr(entSpec[attribName]))
|
|
|
|
def handleReturn(event):
|
|
self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec)
|
|
|
|
def askFilename(callback = handleReturn):
|
|
if text.get() == 'None':
|
|
initialDir = Filename.expandFrom('$TTMODELS/built/').toOsSpecific()
|
|
else:
|
|
initialDir = Filename.expandFrom('$TTMODELS/built/%s' % text.get()[1:-1]).toOsSpecific()
|
|
print(text, text.get()[1:-1], initialDir)
|
|
rawFilename = askopenfilename(defaultextension='*', initialdir=initialDir, filetypes=(('Bam Files', '*.bam'),
|
|
('Egg Files', '*.egg'),
|
|
('Maya Binaries', '*.mb'),
|
|
('All files', '*')), title='Load Model File', parent=self.interior())
|
|
if rawFilename != '':
|
|
filename = Filename.fromOsSpecific(rawFilename)
|
|
filename.findOnSearchpath(getModelPath().getValue())
|
|
text.set("'%s'" % repr(filename))
|
|
handleReturn(None)
|
|
return
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT)
|
|
label['command'] = askFilename
|
|
label.pack(side=LEFT, expand=0)
|
|
|
|
def enterWidget(event, widg = label):
|
|
widg['background'] = '#909090'
|
|
|
|
def leaveWidget(event, widg = label):
|
|
widg['background'] = 'SystemButtonFace'
|
|
|
|
label.bind('<Enter>', enterWidget)
|
|
label.bind('<Leave>', leaveWidget)
|
|
widg = Entry(frame, textvariable=text)
|
|
widg.bind('<Return>', handleReturn)
|
|
widg.pack(side=LEFT, fill=X, expand=1)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
self.curEntWidgets[attribName] = lambda x: text.set(repr(x))
|
|
|
|
def addVisZoneWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
text = StringVar()
|
|
text.set(repr(entSpec[attribName]))
|
|
|
|
def handleReturn(event):
|
|
self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec)
|
|
|
|
def handleUpdate(visZoneList):
|
|
self.level.setAttribEdit(entId, attribName, visZoneList)
|
|
|
|
def getZoneList(callback = handleReturn):
|
|
zoneEntIds = list(self.level.entType2ids['zone'])
|
|
zoneEntIds.remove(LevelConstants.UberZoneEntId)
|
|
zoneEntIds.sort()
|
|
self.visZonesEditor = LevelVisZonesEditor(self, entId, entSpec[attribName], modelZones=zoneEntIds, updateCommand=handleUpdate)
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT)
|
|
label['command'] = getZoneList
|
|
label.pack(side=LEFT, expand=0)
|
|
|
|
def enterWidget(event, widg = label):
|
|
widg['background'] = '#909090'
|
|
|
|
def leaveWidget(event, widg = label):
|
|
widg['background'] = 'SystemButtonFace'
|
|
|
|
label.bind('<Enter>', enterWidget)
|
|
label.bind('<Leave>', leaveWidget)
|
|
widg = Entry(frame, textvariable=text)
|
|
widg.bind('<Return>', handleReturn)
|
|
widg.pack(side=LEFT, fill=X, expand=1)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
|
|
def refreshWidget(visZoneList):
|
|
text.set(repr(visZoneList))
|
|
if self.visZonesEditor:
|
|
self.visZonesEditor.setVisible(visZoneList)
|
|
|
|
self.curEntWidgets[attribName] = refreshWidget
|
|
|
|
def addEntIdWidget(self, levelSpec, entSpec, entId, attribName, params, entTypeReg):
|
|
text = StringVar()
|
|
text.set(repr(entSpec[attribName]))
|
|
|
|
def handleReturn(event):
|
|
self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec)
|
|
|
|
def handleMenu(id):
|
|
text.set(id)
|
|
self.level.setAttribEdit(entId, attribName, id)
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
widg = Entry(frame, textvariable=text)
|
|
widg.bind('<Return>', handleReturn)
|
|
widg.pack(side=LEFT, fill=X, expand=1)
|
|
if attribName is 'parentEntId':
|
|
buttonText = 'Reparent To'
|
|
else:
|
|
buttonText = 'Select Entity'
|
|
entButton = Menubutton(frame, width=8, text=buttonText, relief=RAISED, borderwidth=2)
|
|
entMenu = Menu(entButton, tearoff=0)
|
|
entButton['menu'] = entMenu
|
|
entButton.pack(side=LEFT, fill='x', expand=1)
|
|
entType = params.get('type')
|
|
entOutput = params.get('output')
|
|
idDict = {}
|
|
if entType == 'grid':
|
|
for eType in entTypeReg.getDerivedTypeNames('grid'):
|
|
idDict[eType] = self.level.entType2ids.get(eType, [])
|
|
|
|
elif entType == 'nodepath':
|
|
for eType in entTypeReg.getDerivedTypeNames('nodepath'):
|
|
idDict[eType] = self.level.entType2ids.get(eType, [])
|
|
|
|
elif entOutput == 'bool':
|
|
for eType in entTypeReg.getTypeNamesFromOutputType('bool'):
|
|
idDict[eType] = self.level.entType2ids.get(eType, [])
|
|
|
|
else:
|
|
for eType in list(self.level.entType2ids.keys()):
|
|
idDict[eType] = self.level.entType2ids.get(eType, [])
|
|
|
|
typeKeys = list(idDict.keys())
|
|
typeKeys.sort()
|
|
|
|
def getChildEntIds(entity):
|
|
entIds = []
|
|
for child in entity.getChildren():
|
|
entIds.append(child.entId)
|
|
entIds.extend(getChildEntIds(child))
|
|
|
|
return entIds
|
|
|
|
thisEntity = self.level.getEntity(entId)
|
|
forbiddenEntIds = [entId, thisEntity.parentEntId]
|
|
forbiddenEntIds.extend(getChildEntIds(thisEntity))
|
|
for eType in typeKeys:
|
|
idList = list(idDict[eType])
|
|
for forbiddenId in forbiddenEntIds:
|
|
if forbiddenId in idList:
|
|
idList.remove(forbiddenId)
|
|
|
|
if len(idList) == 0:
|
|
continue
|
|
subMenu = Menu(entMenu, tearoff=0)
|
|
entMenu.add_cascade(label=eType, menu=subMenu)
|
|
idList.sort()
|
|
numIds = len(idList)
|
|
idIndex = 0
|
|
for id in idList:
|
|
if idIndex % 10 == 0:
|
|
if numIds > 10:
|
|
m = Menu(subMenu, tearoff=0)
|
|
firstId = idList[idIndex]
|
|
lastIndex = min(idIndex + 9, numIds - 1)
|
|
lastId = idList[lastIndex]
|
|
subMenu.add_cascade(label='Ids %d-%d' % (firstId, lastId), menu=m)
|
|
else:
|
|
m = subMenu
|
|
m.add_command(label='%d: %s' % (id, self.getEntityName(id)), command=lambda id = id, h = handleMenu: h(id))
|
|
idIndex += 1
|
|
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
self.curEntWidgets[attribName] = text.set
|
|
|
|
def addStringWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
text = StringVar()
|
|
text.set(str(entSpec[attribName]))
|
|
|
|
def handleReturn(event):
|
|
self.level.setAttribEdit(entId, attribName, text.get())
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
widg = Entry(frame, textvariable=text)
|
|
widg.bind('<Return>', handleReturn)
|
|
widg.pack(side=LEFT, fill=X, expand=1)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
self.curEntWidgets[attribName] = text.set
|
|
|
|
def addConstWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
text = str(entSpec[attribName])
|
|
valueLabel = Label(frame, text=text, anchor=W, justify=LEFT, relief=GROOVE)
|
|
valueLabel.pack(side=LEFT, fill=X, expand=1)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
|
|
def addPythonWidget(self, levelSpec, entSpec, entId, attribName, params):
|
|
text = StringVar()
|
|
text.set(repr(entSpec[attribName]))
|
|
|
|
def handleReturn(event):
|
|
self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec)
|
|
|
|
frame = Frame(self.pageOneFrame.interior())
|
|
label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT)
|
|
label.pack(side=LEFT, expand=0)
|
|
widg = Entry(frame, textvariable=text)
|
|
widg.bind('<Return>', handleReturn)
|
|
widg.pack(side=LEFT, fill=X, expand=1)
|
|
frame.pack(fill=X, expand=1)
|
|
self.attribWidgets.append(frame)
|
|
self.curEntWidgets[attribName] = lambda x: text.set(repr(x))
|
|
|
|
def handleAttributeChangeSubmit(self, attribName, textObj, entId, levelSpec):
|
|
newText = textObj.get()
|
|
try:
|
|
value = eval(newText)
|
|
except:
|
|
showwarning('ERROR', 'that is not a valid Python object', parent=self.parent)
|
|
return
|
|
|
|
self.level.setAttribEdit(entId, attribName, value)
|
|
|
|
def handleAttribChange(self, entId, attribName, value, username):
|
|
if username == self.level.editUsername:
|
|
if self.entityCopy:
|
|
self.entityCopy.removeNode()
|
|
self.entityCopy = None
|
|
if entId == self.curEntId:
|
|
widgetSetter = self.curEntWidgets[attribName]
|
|
if widgetSetter is not None:
|
|
widgetSetter(value)
|
|
return
|
|
|
|
def createButtons(self):
|
|
pass
|
|
|
|
def toggleBalloon(self):
|
|
if self.toggleBalloonVar.get():
|
|
self.balloon().configure(state='both')
|
|
else:
|
|
self.balloon().configure(state='none')
|
|
|
|
def onDestroy(self, event):
|
|
from direct.directtools import DirectSession
|
|
direct.disable()
|
|
bboard.remove(DirectSession.DirectSession.DIRECTdisablePost)
|
|
self.ignore(self.level.getAttribChangeEventName())
|
|
self.ignore('DIRECT_selectedNodePath')
|
|
self.ignore('DIRECT_manipulateObjectCleanup')
|
|
self.ignore('DIRECT_undo')
|
|
self.ignore('DIRECT_redo')
|
|
print('InGameEditor.onDestroy()')
|
|
if self.visZonesEditor:
|
|
self.visZonesEditor.destroy()
|
|
self.explorer._node.destroy()
|
|
del self.level
|
|
messenger.send(self.doneEvent)
|
|
|
|
def handleRequestSave(self):
|
|
messenger.send(self.requestSaveEvent)
|
|
|
|
def handleSaveAs(self):
|
|
filename = tkinter.filedialog.asksaveasfilename(parent=self.parent, defaultextension='.py', filetypes=[('Python Source Files', '.py'), ('All Files', '*')])
|
|
if len(filename) > 0:
|
|
messenger.send(self.saveAsEvent, [filename])
|
|
|
|
def doUndo(self):
|
|
messenger.send(self.undoEvent)
|
|
|
|
def doRedo(self):
|
|
messenger.send(self.redoEvent)
|
|
|
|
def doWireframe(self):
|
|
messenger.send(self.wireframeEvent)
|
|
|
|
def doOobe(self):
|
|
messenger.send(self.oobeEvent)
|
|
|
|
def doCs(self):
|
|
messenger.send(self.csEvent)
|
|
|
|
def doRun(self):
|
|
messenger.send(self.runEvent)
|
|
|
|
def doTex(self):
|
|
messenger.send(self.texEvent)
|
|
|
|
def resetLevel(self):
|
|
self.showTodo(what='resetLevel')
|
|
|
|
def showTodo(self, what = ''):
|
|
self.showWarning('%s\nThis is not yet implemented.' % what, 'TODO')
|
|
|
|
def showWarning(self, msg, title = 'Warning'):
|
|
showwarning(title, msg, parent=self.parent)
|
|
|
|
def askYesNo(self, msg, title = 'Query'):
|
|
return askyesno(title, msg, parent=self.parent)
|
|
|
|
def popupLevelDialog(self):
|
|
data = askstring('Input Level Data', 'Level Data:', parent=self)
|
|
if data:
|
|
self.messageBar().helpmessage(data)
|
|
|
|
|
|
class LevelVisZonesEditor(Pmw.MegaToplevel):
|
|
|
|
def __init__(self, editor, entId, visible, modelZones = [], updateCommand = None, parent = None, **kw):
|
|
DGG.INITOPT = Pmw_INITOPT
|
|
optiondefs = (('title', 'Level Vis-Zone Editor', None),)
|
|
self.defineoptions(kw, optiondefs)
|
|
Pmw.MegaToplevel.__init__(self, parent, title=self['title'])
|
|
self.editor = editor
|
|
self.entId = entId
|
|
self.modelZones = modelZones
|
|
self.modelZones.sort()
|
|
self.updateCommand = updateCommand
|
|
hull = self.component('hull')
|
|
balloon = self.balloon = Pmw.Balloon(hull)
|
|
self.balloon.configure(state='none')
|
|
menuFrame = Frame(hull, relief=GROOVE, bd=2)
|
|
menuFrame.pack(fill=X, expand=1)
|
|
menuBar = Pmw.MenuBar(menuFrame, hotkeys=1, balloon=balloon)
|
|
menuBar.pack(side=LEFT, expand=1, fill=X)
|
|
menuBar.addmenu('Vis Zones Editor', 'Visability Zones Editor Operations')
|
|
menuBar.addmenuitem('Vis Zones Editor', 'command', 'Exit Visability Zones Editor', label='Exit', command=self.destroy)
|
|
menuBar.addmenu('Help', 'Visibility Zones Editor Help Operations')
|
|
self.toggleBalloonVar = IntVar()
|
|
self.toggleBalloonVar.set(0)
|
|
menuBar.addmenuitem('Help', 'checkbutton', 'Toggle balloon help', label='Balloon Help', variable=self.toggleBalloonVar, command=self.toggleBalloon)
|
|
sf = Pmw.ScrolledFrame(hull, horizflex='elastic', usehullsize=1, hull_width=200, hull_height=400)
|
|
frame = sf.interior()
|
|
sf.pack(padx=5, pady=3, fill=BOTH, expand=1)
|
|
self.radioSelect = Pmw.RadioSelect(frame, selectmode=MULTIPLE, orient=DGG.VERTICAL, pady=0, command=self.toggleVisZone)
|
|
self.buttons = []
|
|
for modelZoneNum in self.modelZones:
|
|
buttonStr = '%d - %s' % (modelZoneNum, self.editor.getEntityName(modelZoneNum))
|
|
button = self.radioSelect.add(buttonStr, width=12)
|
|
button.configure(anchor=W, justify=LEFT)
|
|
self.buttons.append(button)
|
|
|
|
self.radioSelect.pack(expand=1, fill=X)
|
|
sf.reposition()
|
|
buttonFrame = Frame(hull)
|
|
self.showMode = IntVar()
|
|
self.showMode.set(self.editor.level.level.getColorZones())
|
|
self.showActiveButton = Radiobutton(buttonFrame, text='Stash Invisible', value=0, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode)
|
|
self.showActiveButton.pack(side=LEFT, fill=X, expand=1)
|
|
self.showAllButton = Radiobutton(buttonFrame, text='Color Invisible', value=1, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode)
|
|
self.showAllButton.pack(side=LEFT, fill=X, expand=1)
|
|
buttonFrame.pack(fill=X, expand=1)
|
|
self.initialiseoptions(LevelVisZonesEditor)
|
|
self.setVisible(visible)
|
|
return None
|
|
|
|
def toggleVisZone(self, zoneStr, state):
|
|
zoneNum = int(zoneStr.split('-')[0])
|
|
if state == 0:
|
|
if zoneNum in self.visible:
|
|
self.visible.remove(zoneNum)
|
|
elif zoneNum not in self.visible:
|
|
self.visible.append(zoneNum)
|
|
self.visible.sort()
|
|
if self.updateCommand:
|
|
self.updateCommand(self.visible)
|
|
|
|
def setVisible(self, visible):
|
|
self.visible = visible
|
|
self.refreshVisibility()
|
|
|
|
def setVisRenderMode(self):
|
|
self.editor.level.level.setColorZones(self.showMode.get())
|
|
|
|
def refreshVisibility(self):
|
|
self.radioSelect.selection = []
|
|
for button in self.buttons:
|
|
componentName = button['text']
|
|
modelZone = int(componentName.split('-')[0])
|
|
if modelZone in self.visible:
|
|
button.configure(relief='sunken')
|
|
self.radioSelect.selection.append(componentName)
|
|
else:
|
|
button.configure(relief='raised')
|
|
|
|
def destroy(self):
|
|
self.editor.visZonesEditor = None
|
|
Pmw.MegaToplevel.destroy(self)
|
|
return
|
|
|
|
def toggleBalloon(self):
|
|
if self.toggleBalloonVar.get():
|
|
self.balloon.configure(state='balloon')
|
|
else:
|
|
self.balloon.configure(state='none')
|
|
|
|
|
|
DEFAULT_MENU_ITEMS = ['Update Explorer',
|
|
'Separator',
|
|
'Select',
|
|
'Deselect',
|
|
'Separator',
|
|
'Delete',
|
|
'Separator',
|
|
'Fit',
|
|
'Flash',
|
|
'Isolate',
|
|
'Toggle Vis',
|
|
'Show All',
|
|
'Separator',
|
|
'Set Reparent Target',
|
|
'Reparent',
|
|
'WRT Reparent',
|
|
'Separator',
|
|
'Place',
|
|
'Set Name',
|
|
'Set Color',
|
|
'Explore',
|
|
'Separator']
|
|
|
|
class LevelExplorer(Pmw.MegaWidget, DirectObject.DirectObject):
|
|
|
|
def __init__(self, parent = None, editor = None, **kw):
|
|
optiondefs = (('menuItems', [], Pmw.INITOPT),)
|
|
self.defineoptions(kw, optiondefs)
|
|
Pmw.MegaWidget.__init__(self, parent)
|
|
self.editor = editor
|
|
interior = self.interior()
|
|
interior.configure(relief=GROOVE, borderwidth=2)
|
|
self._scrolledCanvas = self.createcomponent('scrolledCanvas', (), None, Pmw.ScrolledCanvas, (interior,), hull_width=300, hull_height=80, usehullsize=1)
|
|
self._canvas = self._scrolledCanvas.component('canvas')
|
|
self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i')
|
|
self._scrolledCanvas.resizescrollregion()
|
|
self._scrolledCanvas.pack(padx=3, pady=3, 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())
|
|
self.interior().bind('<Destroy>', self.onDestroy)
|
|
self._treeItem = LevelExplorerItem(self.editor.level, self.editor)
|
|
self._node = TreeNode(self._canvas, None, self._treeItem, DEFAULT_MENU_ITEMS + self['menuItems'])
|
|
self._node.expand()
|
|
self._parentFrame = Frame(interior)
|
|
self.accept('Level_Update Explorer', lambda f, s = self: s.update())
|
|
self.initialiseoptions(LevelExplorer)
|
|
return
|
|
|
|
def update(self, fUseCachedChildren = 1):
|
|
self._node.update(fUseCachedChildren)
|
|
self._node.expand()
|
|
|
|
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]
|
|
|
|
def onDestroy(self, event):
|
|
self.ignore('Level_Update Explorer')
|
|
|
|
|
|
class LevelExplorerItem(TreeItem):
|
|
|
|
def __init__(self, levelElement, editor):
|
|
self.levelElement = levelElement
|
|
self.editor = editor
|
|
|
|
def GetText(self):
|
|
return self.levelElement.getName()
|
|
|
|
def GetKey(self):
|
|
return self.levelElement.id()
|
|
|
|
def IsEditable(self):
|
|
return 1
|
|
|
|
def SetText(self, text):
|
|
self.levelElement.setNewName(text)
|
|
|
|
def GetIconName(self):
|
|
return 'sphere2'
|
|
|
|
def IsExpandable(self):
|
|
return self.levelElement.getNumChildren() != 0
|
|
|
|
def GetSubList(self):
|
|
sublist = []
|
|
for element in self.levelElement.getChildren():
|
|
item = LevelExplorerItem(element, self.editor)
|
|
sublist.append(item)
|
|
|
|
return sublist
|
|
|
|
def OnSelect(self):
|
|
messenger.send(self.editor.getEventMsgName('Select'), [self.levelElement])
|
|
|
|
def MenuCommand(self, command):
|
|
messenger.send(self.editor.getEventMsgName(command), [self.levelElement])
|