panda3d/doc/SceneEditor/seAnimPanel.py
2004-08-06 22:22:50 +00:00

609 lines
25 KiB
Python

#################################################################
# collisionWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from AppShell import *
from TkGlobal import *
from tkSimpleDialog import askfloat
import string
import math
import types
import Task
FRAMES = 0
SECONDS = 1
class AnimPanel(AppShell):
#################################################################
# This class will generate a animation panel for an actor
# which user assigned. Inside this panel, instead of using actorInterval
# or just simply calling the play function in Actor, we create a task to
# set animation frame by frame using setPose.
#################################################################
# Override class variables
appname = 'Anim Panel'
frameWidth = 575
frameHeight = 250
usecommandarea = 0
usestatusarea = 0
index = 0
dragMode = False
rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0']
def __init__(self, aNode = None, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'AnimPanel '+ aNode.getName()
self.appname = self.id
optiondefs = (
('title', self.appname, None),
('actor', aNode, None),
('animList', [], None),
)
self.defineoptions(kw, optiondefs)
self.frameHeight = 300
self.id = 'AnimPanel '+ aNode.getName()
self.nodeName = aNode.getName()
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(AnimPanel)
self.currTime = 0.0 # Initialize the start time
self.animName = None
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
menuBar = self.menuBar
menuBar.addmenu('Anim', 'Anim Panel Operations')
# Reset all actor controls
menuBar.addmenuitem('File', 'command',
'Load Animation',
label = 'Load Animation',
command = self.loadAnimation)
menuBar.addmenuitem('Anim', 'command',
'Set actor controls to t = 0.0',
label = 'Jump all to zero',
command = self.resetAllToZero)
menuBar.addmenuitem('Anim', 'command',
'Set Actor controls to end time',
label = 'Jump all to end time',
command = self.resetAllToEnd)
menuBar.addmenuitem('Anim', 'separator')
menuBar.addmenuitem('Anim', 'command',
'Play Current Animation',
label = 'Play',
command = self.play)
menuBar.addmenuitem('Anim', 'command',
'Stop Current Animation',
label = 'stop',
command = self.stop)
# Create a frame to hold all the actor controls
actorFrame = Frame(interior)
name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 16),
relief = SUNKEN, borderwidth=3)
name_label.place(x=5,y=5,anchor=NW)
Label(actorFrame, text= "Animation:", font=('MSSansSerif', 12)).place(x=140,y=5,anchor=NW)
Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 12)).place(x=140,y=35,anchor=NW)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntry = self.createcomponent(
'AnimationMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setAnimation,
scrolledlist_items = self['animList'])
self.AnimEntry.place(x=240,y=10,anchor=NW)
self.playRateEntry = self.createcomponent(
'playRateMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate,
scrolledlist_items = self.rateList)
self.playRateEntry.place(x=240,y=40,anchor=NW)
self.playRateEntry.selectitem('1.0')
### Loop checkbox
Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=05,anchor=NW)
self.loopVar = IntVar()
self.loopVar.set(0)
self.loopButton = self.createcomponent(
'loopButton', (), None,
Checkbutton, (actorFrame,),
variable = self.loopVar)
self.loopButton.place(x=470,y=7,anchor=NW)
### Display Frames/Seconds
Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 11)).place(x=5,y=75,anchor=NW)
self.unitsVar = IntVar()
self.unitsVar.set(FRAMES)
self.displayButton = self.createcomponent(
'displayButton', (), None,
Checkbutton, (actorFrame,),
command = self.updateDisplay,
variable = self.unitsVar)
self.displayButton.place(x=120,y=77,anchor=NW)
## scale control
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
self.minLabel = self.createcomponent(
'minLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0)
self.minLabel.pack(side = LEFT)
self.frameControl = self.createcomponent(
'scale', (), None,
Scale, (frameFrame,),
from_ = 0, to = 24, resolution = 1.0,
command = self.goTo, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.frameControl.pack(side = LEFT, expand = 1)
self.frameControl.bind('<Button-1>', self.onPress)
self.frameControl.bind('<ButtonRelease-1>', self.onRelease)
self.maxLabel = self.createcomponent(
'maxLabel', (), 'sLabel',
Label, (frameFrame,),
text = 24)
self.maxLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
## button contorl
ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5)
self.toStartButton = self.createcomponent(
'toStart', (), None,
Button, (ButtomFrame,),
text = '<<',
width = 8,
command = self.resetAllToZero)
self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
self.playButton = self.createcomponent(
'playButton', (), None,
Button, (ButtomFrame,),
text = 'Play', width = 8,
command = self.play)
self.playButton.pack(side = LEFT, expand = 1, fill = X)
self.stopButton = self.createcomponent(
'stopButton', (), None,
Button, (ButtomFrame,),
text = 'Stop', width = 8, state=DISABLED,
command = self.stop)
self.stopButton.pack(side = LEFT, expand = 1, fill = X)
self.toEndButton = self.createcomponent(
'toEnd', (), None,
Button, (ButtomFrame,),
text = '>>',
width = 8,
command = self.resetAllToEnd)
self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
ButtomFrame.place(anchor=NW,x=5,y=165)
self.removeButton = self.createcomponent(
'Remove Animation', (), None,
Button, (actorFrame,),
text = 'Remove This Animation', width = 20,
command = self.removeAnim)
self.removeButton.place(anchor=NW,x=5,y=220)
self.loadButton = self.createcomponent(
'Load Animation', (), None,
Button, (actorFrame,),
text = 'Load New Animation', width = 20,
command = self.loadAnimation)
self.loadButton.place(anchor=NW,x=180,y=220)
# Now pack the actor frame
actorFrame.pack(expand = 1, fill = BOTH)
def updateList(self):
#################################################################
# updateList(self)
# This function will update the list of animations that the Actor
# currently has into the combo box widget.
#################################################################
self.ignore('DataH_loadFinish'+self.nodeName)
del self.loaderWindow
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntry.setlist(animL)
def removeAnim(self):
#################################################################
# removeAnim(self)
# This function will stop the animation and get the name of animation
# which user wish to remove from the panel. Then, it will send out
# a message to dataHolder to remove the target animation.
# And in the same time, it will start waiting a return message to
# make sure that target animation has been removed.
#################################################################
name = self.AnimEntry.get()
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
self.stop()
self.accept('DataH_removeAnimFinish'+self.nodeName,self.afterRemove)
messenger.send('AW_removeAnim',[self['actor'],name])
return
def afterRemove(self):
#################################################################
# afterRemove(self)
# This function will be called once panel has received the return
# message from dataHolder. This function will call setList to
# reset the list of Animations
#################################################################
self.ignore('DataH_removeAnimFinish'+self.nodeName)
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntry.setlist(animL)
print '-----',animL
return
def loadAnimation(self):
#################################################################
# loadAnimation(self)
# This function will open a dialog window to require user to input
# the animation he wants to load in for this actor.
#################################################################
self.loaderWindow = LoadAnimPanel(aNode=self['actor'])
self.accept('DataH_loadFinish'+self.nodeName,self.updateList)
return
def play(self):
#################################################################
# play(self)
# This function will be called when user click on the "play" button.
# First, this function will initialize all parameter that the actual
# play task need to run and add the play task into the taskMgr.
#################################################################
self.animName = self.AnimEntry.get()
if self.animName in self['animList']:
animName = self.AnimEntry.get()
self.playButton.config(state=DISABLED)
self.lastT = globalClock.getFrameTime()
taskMgr.add(self.playTask, self.id + '_UpdateTask')
self.stopButton.config(state=NORMAL)
else:
print '----Illegal Animaion name!!', self.animName
return
def playTask(self, task):
#################################################################
# playTask(self, task)
# This task will record time by each frame
# In fact it is just a clock keeper.
# If the current frame time over the max long of the animation,
# it will reset the timer.
# Anyway, this function will call gotoT by each frame.
#################################################################
fLoop = self.loopVar.get()
currT = globalClock.getFrameTime()
deltaT = currT - self.lastT
self.lastT = currT
if self.dragMode:
return Task.cont
self.currTime = self.currTime + deltaT
if (self.currTime > self.maxSeconds):
if fLoop:
self.currTime = self.currTime%self.duration
self.gotoT(self.currTime)
else:
self.currTime = 0.0
self.gotoT(0.0)
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return Task.done
else:
self.gotoT(self.currTime)
return Task.cont
def stop(self):
#################################################################
# stop(self)
# This function will remove the play task from taskMgr when user
# click on the "Stop" button
#################################################################
taskMgr.remove(self.id + '_UpdateTask')
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return
def setAnimation(self, animation):
#################################################################
# setAnimation(self, animation)
# This function will be called each time when user change
# the current animation. Most important thing this function do is
# to recalculate all variables to fit the selected animation
#################################################################
self.animName = self.AnimEntry.get()
playRate = '%0.1f' % self['actor'].getPlayRate(self.animName)
if playRate not in self.rateList:
def strCmp(a, b):
return cmp(eval(a), eval(b))
self.rateList.append(playRate)
self.rateList.sort(strCmp)
self.playRateEntry.reset(self.rateList)
self.playRateEntry.selectitem(playRate)
self.currTime = 0.0
self.frameControl.set(0)
self.updateDisplay()
return
def setPlayRate(self,rate):
#################################################################
# setPlayRate(self, rate)
# This function will be called each time when user changes the current play rate.
#################################################################
self.animName = self.AnimEntry.get()
if self.animName in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animName)
self.updateDisplay()
return
def updateDisplay(self):
#################################################################
# updateDisplay(self)
# This function will be called whenever something has been changed
# on the panel. In here we will re-new all widgets on the panel to
# correct value.
#################################################################
self.fps = self['actor'].getFrameRate(self.animName)
self.duration = self['actor'].getDuration(self.animName)
self.maxFrame = self['actor'].getNumFrames(self.animName) - 1
self.maxSeconds = self.duration
if self.unitsVar.get() == FRAMES:
fromFrame = 0
toFrame = self.maxFrame
self.minLabel['text'] = fromFrame
self.maxLabel['text'] = toFrame
self.frameControl.configure(from_ = fromFrame,
to = toFrame,
resolution = 1.0)
else:
self.minLabel['text'] = '0.0'
self.maxLabel['text'] = "%.2f" % self.duration
self.frameControl.configure(from_ = 0.0,
to = self.duration,
resolution = 0.01)
def gotoT(self,time):
#################################################################
# gotoT(self, time)
# calculate the right parameter which will be send to set Frame
# Control slider, which is the real place we play the animation.
#################################################################
if self.unitsVar.get() == FRAMES:
self.frameControl.set(time * self.fps)
else:
self.frameControl.set(time)
return
def goTo(self,frame):
#################################################################
# goto(self, frame)
# Call back function for the frame control slider.
# This function will set the animation by the value on the slider.
#
# This function is the real function we "play" the animation.
#
#################################################################
if self.animName in self['animList']:
# Convert scale value to float
frame = string.atof(frame)
# Now convert t to seconds for offset calculations
if self.unitsVar.get() == FRAMES:
frame = frame / self.fps
if self.dragMode:
# If user is clicking on the slider and is draging the bar, reset the global timer.
self.currTime = frame
self['actor'].pose(self.animName,
min(self.maxFrame, int(frame * self.fps)))
return
def onRelease(self,frame):
#################################################################
# onRelease(self, frame)
# disable the dragMode when user releases the bar on the slider.
#################################################################
self.dragMode = False
return
def onPress(self,frame):
#################################################################
# onPress(self, frame)
# enable the dragMode when user press the bar on the slider.
#################################################################
self.dragMode = True
return
def resetAllToZero(self):
#################################################################
# resetAllToZero(self)
# reset the global timer to zero and also move the slider to zero.
# This will also reset the actor to the zero frame of current animation
#################################################################
self.currTime = 0.0
self.gotoT(0)
return
def resetAllToEnd(self):
#################################################################
# resetAllToEnd(self)
# set the global timer to the end of current animation and
# also move the slider to the end.
#################################################################
self.currTime = self.maxSeconds
self.gotoT(self.duration)
return
def onDestroy(self, event):
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
taskMgr.remove(self.id + '_UpdateTask')
self.ignore('DataH_loadFinish')
messenger.send('AW_close',[self.nodeName])
'''
If you have open any thing, please rewrite here!
'''
pass
class LoadAnimPanel(AppShell):
#################################################################
# LoadAnimPanel(AppShell)
# This class will open a dialog to ask user to input names and
# file paths of animations
#################################################################
# Override class variables
appname = 'Load Animation'
frameWidth = 575
frameHeight = 200
usecommandarea = 0
usestatusarea = 0
index = 0
## Anim name : File Path
def __init__(self, aNode = None, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'Load Animation '+ aNode.getName()
self.appname = self.id
self.animDic = {}
self.animList = []
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
self.frameHeight = 300
self.nodeName = aNode.getName()
self.Actor = aNode
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(LoadAnimPanel)
def createInterface(self):
self.menuBar.destroy()
interior = self.interior()
mainFrame = Frame(interior)
self.inputZone = Pmw.Group(mainFrame, tag_text='File Setting')
self.inputZone.pack(fill='both',expand=1)
settingFrame = self.inputZone.interior()
Label(settingFrame,text='Anim Name').place(anchor=NW,x=60,y=5)
Label(settingFrame,text='File Path').place(anchor=NW,x=205,y=5)
self.AnimName_1 = self.createcomponent(
'Anim Name List', (), None,
Pmw.ComboBox, (settingFrame,),label_text='Anim :',
labelpos = W, entry_width = 10, selectioncommand = self.selectAnim,
scrolledlist_items = self.animList)
self.AnimFile_1 = Pmw.EntryField(settingFrame,value='')
self.AnimFile_1.component('entry').config(width=20)
self.AnimName_1.place(anchor=NW,x=10,y=25)
self.AnimFile_1.place(anchor=NW,x=140,y=25)
self.Browse_1 = self.createcomponent(
'File Browser1', (), None,
Button, (mainFrame,),
text = 'Browse...',
command = self.Browse_1)
self.Browse_1.place(anchor=NW,x=270,y=38)
self.addIntoButton = self.createcomponent(
'Load Add', (), None,
Button, (mainFrame,),
text = 'Add to Load',
command = self.addIntoList)
self.addIntoButton.place(anchor=NW,x=345,y=38)
att_label = Label(mainFrame, font=('MSSansSerif', 10),
text= "Attention! Animations won't be loaded in before you press the 'OK' button below!")
att_label.place(anchor=NW,x=10,y=80)
self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10)
self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT)
mainFrame.pack(expand = 1, fill = BOTH)
def onDestroy(self, event):
messenger.send('AWL_close',[self.nodeName])
'''
If you have open any thing, please rewrite here!
'''
pass
def selectAnim(self,name):
#################################################################
# selectAnim(self, name)
# This function will be called if user select an animation on the list.
#################################################################
if name in self.animDic:
self.AnimFile_1.setvalue = self.animDic[name]
return
def Browse_1(self):
#################################################################
# Browse_1(self)
# when the browse button pused, this function will be called.
# Do nothing but open a file dialog for user to set the path to target file
# Then, set the path back to the entry on the panel.
#################################################################
AnimFilename = askopenfilename(
defaultextension = '.egg',
filetypes = (('Egg Files', '*.egg'),
('Bam Files', '*.bam'),
('All files', '*')),
initialdir = '.',
title = 'File Path for Anim 1',
parent = self.parent)
if AnimFilename:
self.AnimFile_1.setvalue(AnimFilename)
return
def addIntoList(self):
#################################################################
# addIntoList(self)
# This function will be called each time when user click on the
# "Add to Load" button. This function will read the current data
# on the panel into a dictionary. then reset the list of the animation
# name list on this panel.(not the one in the animation panel...)
#
# This function won't load any animation....
#
#################################################################
name = self.AnimName_1.get()
self.animDic[name] = Filename.fromOsSpecific(self.AnimFile_1.getvalue()).getFullpath()
if name in self.animList:
pass
else:
self.animList.append(name)
self.AnimName_1.setlist(self.animList)
print self.animDic
return
def ok_press(self):
#################################################################
# ok_press(Self)
# This functiion will be called when user click on the "OK"
# button. This function will send a message along with the animation
# file we wish to load for this actor.
# Then, it will close the panel itself.
#################################################################
messenger.send('AW_AnimationLoad',[self.Actor,self.animDic])
#print self.animDic
self.quit()
return