seAnimPanel.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. #################################################################
  2. # collisionWindow.py
  3. # Written by Yi-Hong Lin, [email protected], 2004
  4. #################################################################
  5. # Import Tkinter, Pmw, and the floater code from this directory tree.
  6. from direct.tkwidgets.AppShell import *
  7. from direct.showbase.TkGlobal import *
  8. from tkSimpleDialog import askfloat
  9. import string
  10. import math
  11. import types
  12. from direct.task import Task
  13. FRAMES = 0
  14. SECONDS = 1
  15. class AnimPanel(AppShell):
  16. #################################################################
  17. # This class will generate a animation panel for an actor
  18. # which user assigned. Inside this panel, instead of using actorInterval
  19. # or just simply calling the play function in Actor, we create a task to
  20. # set animation frame by frame using setPose.
  21. #################################################################
  22. # Override class variables
  23. appname = 'Anim Panel'
  24. frameWidth = 575
  25. frameHeight = 250
  26. usecommandarea = 0
  27. usestatusarea = 0
  28. index = 0
  29. dragMode = False
  30. rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0']
  31. def __init__(self, aNode = None, parent = None, **kw):
  32. INITOPT = Pmw.INITOPT
  33. self.id = 'AnimPanel '+ aNode.getName()
  34. self.appname = self.id
  35. optiondefs = (
  36. ('title', self.appname, None),
  37. ('actor', aNode, None),
  38. ('animList', [], None),
  39. )
  40. self.defineoptions(kw, optiondefs)
  41. self.frameHeight = 300
  42. self.id = 'AnimPanel '+ aNode.getName()
  43. self.nodeName = aNode.getName()
  44. # Initialize the superclass
  45. AppShell.__init__(self)
  46. # Execute option callbacks
  47. self.initialiseoptions(AnimPanel)
  48. self.currTime = 0.0 # Initialize the start time
  49. self.animName = None
  50. self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
  51. def createInterface(self):
  52. # Handle to the toplevels interior
  53. interior = self.interior()
  54. menuBar = self.menuBar
  55. menuBar.addmenu('Anim', 'Anim Panel Operations')
  56. # Reset all actor controls
  57. menuBar.addmenuitem('File', 'command',
  58. 'Load Animation',
  59. label = 'Load Animation',
  60. command = self.loadAnimation)
  61. menuBar.addmenuitem('Anim', 'command',
  62. 'Set actor controls to t = 0.0',
  63. label = 'Jump all to zero',
  64. command = self.resetAllToZero)
  65. menuBar.addmenuitem('Anim', 'command',
  66. 'Set Actor controls to end time',
  67. label = 'Jump all to end time',
  68. command = self.resetAllToEnd)
  69. menuBar.addmenuitem('Anim', 'separator')
  70. menuBar.addmenuitem('Anim', 'command',
  71. 'Play Current Animation',
  72. label = 'Play',
  73. command = self.play)
  74. menuBar.addmenuitem('Anim', 'command',
  75. 'Stop Current Animation',
  76. label = 'stop',
  77. command = self.stop)
  78. # Create a frame to hold all the actor controls
  79. actorFrame = Frame(interior)
  80. name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 16),
  81. relief = SUNKEN, borderwidth=3)
  82. name_label.place(x=5,y=5,anchor=NW)
  83. Label(actorFrame, text= "Animation:", font=('MSSansSerif', 12)).place(x=140,y=5,anchor=NW)
  84. Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 12)).place(x=140,y=35,anchor=NW)
  85. self['animList'] = self['actor'].getAnimNames()
  86. self.AnimEntry = self.createcomponent(
  87. 'AnimationMenu', (), None,
  88. Pmw.ComboBox, (actorFrame,),
  89. labelpos = W, entry_width = 20, selectioncommand = self.setAnimation,
  90. scrolledlist_items = self['animList'])
  91. self.AnimEntry.place(x=240,y=10,anchor=NW)
  92. self.playRateEntry = self.createcomponent(
  93. 'playRateMenu', (), None,
  94. Pmw.ComboBox, (actorFrame,),
  95. labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate,
  96. scrolledlist_items = self.rateList)
  97. self.playRateEntry.place(x=240,y=40,anchor=NW)
  98. self.playRateEntry.selectitem('1.0')
  99. ### Loop checkbox
  100. Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=05,anchor=NW)
  101. self.loopVar = IntVar()
  102. self.loopVar.set(0)
  103. self.loopButton = self.createcomponent(
  104. 'loopButton', (), None,
  105. Checkbutton, (actorFrame,),
  106. variable = self.loopVar)
  107. self.loopButton.place(x=470,y=7,anchor=NW)
  108. ### Display Frames/Seconds
  109. Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 11)).place(x=5,y=75,anchor=NW)
  110. self.unitsVar = IntVar()
  111. self.unitsVar.set(FRAMES)
  112. self.displayButton = self.createcomponent(
  113. 'displayButton', (), None,
  114. Checkbutton, (actorFrame,),
  115. command = self.updateDisplay,
  116. variable = self.unitsVar)
  117. self.displayButton.place(x=120,y=77,anchor=NW)
  118. ## scale control
  119. frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
  120. self.minLabel = self.createcomponent(
  121. 'minLabel', (), 'sLabel',
  122. Label, (frameFrame,),
  123. text = 0)
  124. self.minLabel.pack(side = LEFT)
  125. self.frameControl = self.createcomponent(
  126. 'scale', (), None,
  127. Scale, (frameFrame,),
  128. from_ = 0, to = 24, resolution = 1.0,
  129. command = self.goTo, length = 500,
  130. orient = HORIZONTAL, showvalue = 1)
  131. self.frameControl.pack(side = LEFT, expand = 1)
  132. self.frameControl.bind('<Button-1>', self.onPress)
  133. self.frameControl.bind('<ButtonRelease-1>', self.onRelease)
  134. self.maxLabel = self.createcomponent(
  135. 'maxLabel', (), 'sLabel',
  136. Label, (frameFrame,),
  137. text = 24)
  138. self.maxLabel.pack(side = LEFT)
  139. frameFrame.pack(side = LEFT, expand = 1, fill = X)
  140. ## button contorl
  141. ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5)
  142. self.toStartButton = self.createcomponent(
  143. 'toStart', (), None,
  144. Button, (ButtomFrame,),
  145. text = '<<',
  146. width = 8,
  147. command = self.resetAllToZero)
  148. self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
  149. self.playButton = self.createcomponent(
  150. 'playButton', (), None,
  151. Button, (ButtomFrame,),
  152. text = 'Play', width = 8,
  153. command = self.play)
  154. self.playButton.pack(side = LEFT, expand = 1, fill = X)
  155. self.stopButton = self.createcomponent(
  156. 'stopButton', (), None,
  157. Button, (ButtomFrame,),
  158. text = 'Stop', width = 8, state=DISABLED,
  159. command = self.stop)
  160. self.stopButton.pack(side = LEFT, expand = 1, fill = X)
  161. self.toEndButton = self.createcomponent(
  162. 'toEnd', (), None,
  163. Button, (ButtomFrame,),
  164. text = '>>',
  165. width = 8,
  166. command = self.resetAllToEnd)
  167. self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
  168. ButtomFrame.place(anchor=NW,x=5,y=165)
  169. self.removeButton = self.createcomponent(
  170. 'Remove Animation', (), None,
  171. Button, (actorFrame,),
  172. text = 'Remove This Animation', width = 20,
  173. command = self.removeAnim)
  174. self.removeButton.place(anchor=NW,x=5,y=220)
  175. self.loadButton = self.createcomponent(
  176. 'Load Animation', (), None,
  177. Button, (actorFrame,),
  178. text = 'Load New Animation', width = 20,
  179. command = self.loadAnimation)
  180. self.loadButton.place(anchor=NW,x=180,y=220)
  181. # Now pack the actor frame
  182. actorFrame.pack(expand = 1, fill = BOTH)
  183. def updateList(self):
  184. #################################################################
  185. # updateList(self)
  186. # This function will update the list of animations that the Actor
  187. # currently has into the combo box widget.
  188. #################################################################
  189. self.ignore('DataH_loadFinish'+self.nodeName)
  190. del self.loaderWindow
  191. self['animList'] = self['actor'].getAnimNames()
  192. animL = self['actor'].getAnimNames()
  193. self.AnimEntry.setlist(animL)
  194. def removeAnim(self):
  195. #################################################################
  196. # removeAnim(self)
  197. # This function will stop the animation and get the name of animation
  198. # which user wish to remove from the panel. Then, it will send out
  199. # a message to dataHolder to remove the target animation.
  200. # And in the same time, it will start waiting a return message to
  201. # make sure that target animation has been removed.
  202. #################################################################
  203. name = self.AnimEntry.get()
  204. if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
  205. self.stop()
  206. self.accept('DataH_removeAnimFinish'+self.nodeName,self.afterRemove)
  207. messenger.send('AW_removeAnim',[self['actor'],name])
  208. return
  209. def afterRemove(self):
  210. #################################################################
  211. # afterRemove(self)
  212. # This function will be called once panel has received the return
  213. # message from dataHolder. This function will call setList to
  214. # reset the list of Animations
  215. #################################################################
  216. self.ignore('DataH_removeAnimFinish'+self.nodeName)
  217. self['animList'] = self['actor'].getAnimNames()
  218. animL = self['actor'].getAnimNames()
  219. self.AnimEntry.setlist(animL)
  220. print '-----',animL
  221. return
  222. def loadAnimation(self):
  223. #################################################################
  224. # loadAnimation(self)
  225. # This function will open a dialog window to require user to input
  226. # the animation he wants to load in for this actor.
  227. #################################################################
  228. self.loaderWindow = LoadAnimPanel(aNode=self['actor'])
  229. self.accept('DataH_loadFinish'+self.nodeName,self.updateList)
  230. return
  231. def play(self):
  232. #################################################################
  233. # play(self)
  234. # This function will be called when user click on the "play" button.
  235. # First, this function will initialize all parameter that the actual
  236. # play task need to run and add the play task into the taskMgr.
  237. #################################################################
  238. self.animName = self.AnimEntry.get()
  239. if self.animName in self['animList']:
  240. animName = self.AnimEntry.get()
  241. self.playButton.config(state=DISABLED)
  242. self.lastT = globalClock.getFrameTime()
  243. taskMgr.add(self.playTask, self.id + '_UpdateTask')
  244. self.stopButton.config(state=NORMAL)
  245. else:
  246. print '----Illegal Animaion name!!', self.animName
  247. return
  248. def playTask(self, task):
  249. #################################################################
  250. # playTask(self, task)
  251. # This task will record time by each frame
  252. # In fact it is just a clock keeper.
  253. # If the current frame time over the max long of the animation,
  254. # it will reset the timer.
  255. # Anyway, this function will call gotoT by each frame.
  256. #################################################################
  257. fLoop = self.loopVar.get()
  258. currT = globalClock.getFrameTime()
  259. deltaT = currT - self.lastT
  260. self.lastT = currT
  261. if self.dragMode:
  262. return Task.cont
  263. self.currTime = self.currTime + deltaT
  264. if (self.currTime > self.maxSeconds):
  265. if fLoop:
  266. self.currTime = self.currTime%self.duration
  267. self.gotoT(self.currTime)
  268. else:
  269. self.currTime = 0.0
  270. self.gotoT(0.0)
  271. self.playButton.config(state=NORMAL)
  272. self.stopButton.config(state=DISABLED)
  273. return Task.done
  274. else:
  275. self.gotoT(self.currTime)
  276. return Task.cont
  277. def stop(self):
  278. #################################################################
  279. # stop(self)
  280. # This function will remove the play task from taskMgr when user
  281. # click on the "Stop" button
  282. #################################################################
  283. taskMgr.remove(self.id + '_UpdateTask')
  284. self.playButton.config(state=NORMAL)
  285. self.stopButton.config(state=DISABLED)
  286. return
  287. def setAnimation(self, animation):
  288. #################################################################
  289. # setAnimation(self, animation)
  290. # This function will be called each time when user change
  291. # the current animation. Most important thing this function do is
  292. # to recalculate all variables to fit the selected animation
  293. #################################################################
  294. self.animName = self.AnimEntry.get()
  295. playRate = '%0.1f' % self['actor'].getPlayRate(self.animName)
  296. if playRate not in self.rateList:
  297. def strCmp(a, b):
  298. return cmp(eval(a), eval(b))
  299. self.rateList.append(playRate)
  300. self.rateList.sort(strCmp)
  301. self.playRateEntry.reset(self.rateList)
  302. self.playRateEntry.selectitem(playRate)
  303. self.currTime = 0.0
  304. self.frameControl.set(0)
  305. self.updateDisplay()
  306. return
  307. def setPlayRate(self,rate):
  308. #################################################################
  309. # setPlayRate(self, rate)
  310. # This function will be called each time when user changes the current play rate.
  311. #################################################################
  312. self.animName = self.AnimEntry.get()
  313. if self.animName in self['animList']:
  314. self['actor'].setPlayRate(eval(rate), self.animName)
  315. self.updateDisplay()
  316. return
  317. def updateDisplay(self):
  318. #################################################################
  319. # updateDisplay(self)
  320. # This function will be called whenever something has been changed
  321. # on the panel. In here we will re-new all widgets on the panel to
  322. # correct value.
  323. #################################################################
  324. self.fps = self['actor'].getFrameRate(self.animName)
  325. self.duration = self['actor'].getDuration(self.animName)
  326. self.maxFrame = self['actor'].getNumFrames(self.animName) - 1
  327. self.maxSeconds = self.duration
  328. if self.unitsVar.get() == FRAMES:
  329. fromFrame = 0
  330. toFrame = self.maxFrame
  331. self.minLabel['text'] = fromFrame
  332. self.maxLabel['text'] = toFrame
  333. self.frameControl.configure(from_ = fromFrame,
  334. to = toFrame,
  335. resolution = 1.0)
  336. else:
  337. self.minLabel['text'] = '0.0'
  338. self.maxLabel['text'] = "%.2f" % self.duration
  339. self.frameControl.configure(from_ = 0.0,
  340. to = self.duration,
  341. resolution = 0.01)
  342. def gotoT(self,time):
  343. #################################################################
  344. # gotoT(self, time)
  345. # calculate the right parameter which will be send to set Frame
  346. # Control slider, which is the real place we play the animation.
  347. #################################################################
  348. if self.unitsVar.get() == FRAMES:
  349. self.frameControl.set(time * self.fps)
  350. else:
  351. self.frameControl.set(time)
  352. return
  353. def goTo(self,frame):
  354. #################################################################
  355. # goto(self, frame)
  356. # Call back function for the frame control slider.
  357. # This function will set the animation by the value on the slider.
  358. #
  359. # This function is the real function we "play" the animation.
  360. #
  361. #################################################################
  362. if self.animName in self['animList']:
  363. # Convert scale value to float
  364. frame = string.atof(frame)
  365. # Now convert t to seconds for offset calculations
  366. if self.unitsVar.get() == FRAMES:
  367. frame = frame / self.fps
  368. if self.dragMode:
  369. # If user is clicking on the slider and is draging the bar, reset the global timer.
  370. self.currTime = frame
  371. self['actor'].pose(self.animName,
  372. min(self.maxFrame, int(frame * self.fps)))
  373. return
  374. def onRelease(self,frame):
  375. #################################################################
  376. # onRelease(self, frame)
  377. # disable the dragMode when user releases the bar on the slider.
  378. #################################################################
  379. self.dragMode = False
  380. return
  381. def onPress(self,frame):
  382. #################################################################
  383. # onPress(self, frame)
  384. # enable the dragMode when user press the bar on the slider.
  385. #################################################################
  386. self.dragMode = True
  387. return
  388. def resetAllToZero(self):
  389. #################################################################
  390. # resetAllToZero(self)
  391. # reset the global timer to zero and also move the slider to zero.
  392. # This will also reset the actor to the zero frame of current animation
  393. #################################################################
  394. self.currTime = 0.0
  395. self.gotoT(0)
  396. return
  397. def resetAllToEnd(self):
  398. #################################################################
  399. # resetAllToEnd(self)
  400. # set the global timer to the end of current animation and
  401. # also move the slider to the end.
  402. #################################################################
  403. self.currTime = self.maxSeconds
  404. self.gotoT(self.duration)
  405. return
  406. def onDestroy(self, event):
  407. if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
  408. taskMgr.remove(self.id + '_UpdateTask')
  409. self.ignore('DataH_loadFinish')
  410. messenger.send('AW_close',[self.nodeName])
  411. '''
  412. If you have open any thing, please rewrite here!
  413. '''
  414. pass
  415. class LoadAnimPanel(AppShell):
  416. #################################################################
  417. # LoadAnimPanel(AppShell)
  418. # This class will open a dialog to ask user to input names and
  419. # file paths of animations
  420. #################################################################
  421. # Override class variables
  422. appname = 'Load Animation'
  423. frameWidth = 575
  424. frameHeight = 200
  425. usecommandarea = 0
  426. usestatusarea = 0
  427. index = 0
  428. ## Anim name : File Path
  429. def __init__(self, aNode = None, parent = None, **kw):
  430. INITOPT = Pmw.INITOPT
  431. self.id = 'Load Animation '+ aNode.getName()
  432. self.appname = self.id
  433. self.animDic = {}
  434. self.animList = []
  435. optiondefs = (
  436. ('title', self.appname, None),
  437. )
  438. self.defineoptions(kw, optiondefs)
  439. self.frameHeight = 300
  440. self.nodeName = aNode.getName()
  441. self.Actor = aNode
  442. # Initialize the superclass
  443. AppShell.__init__(self)
  444. # Execute option callbacks
  445. self.initialiseoptions(LoadAnimPanel)
  446. def createInterface(self):
  447. self.menuBar.destroy()
  448. interior = self.interior()
  449. mainFrame = Frame(interior)
  450. self.inputZone = Pmw.Group(mainFrame, tag_text='File Setting')
  451. self.inputZone.pack(fill='both',expand=1)
  452. settingFrame = self.inputZone.interior()
  453. Label(settingFrame,text='Anim Name').place(anchor=NW,x=60,y=5)
  454. Label(settingFrame,text='File Path').place(anchor=NW,x=205,y=5)
  455. self.AnimName_1 = self.createcomponent(
  456. 'Anim Name List', (), None,
  457. Pmw.ComboBox, (settingFrame,),label_text='Anim :',
  458. labelpos = W, entry_width = 10, selectioncommand = self.selectAnim,
  459. scrolledlist_items = self.animList)
  460. self.AnimFile_1 = Pmw.EntryField(settingFrame,value='')
  461. self.AnimFile_1.component('entry').config(width=20)
  462. self.AnimName_1.place(anchor=NW,x=10,y=25)
  463. self.AnimFile_1.place(anchor=NW,x=140,y=25)
  464. self.Browse_1 = self.createcomponent(
  465. 'File Browser1', (), None,
  466. Button, (mainFrame,),
  467. text = 'Browse...',
  468. command = self.Browse_1)
  469. self.Browse_1.place(anchor=NW,x=270,y=38)
  470. self.addIntoButton = self.createcomponent(
  471. 'Load Add', (), None,
  472. Button, (mainFrame,),
  473. text = 'Add to Load',
  474. command = self.addIntoList)
  475. self.addIntoButton.place(anchor=NW,x=345,y=38)
  476. att_label = Label(mainFrame, font=('MSSansSerif', 10),
  477. text= "Attention! Animations won't be loaded in before you press the 'OK' button below!")
  478. att_label.place(anchor=NW,x=10,y=80)
  479. self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10)
  480. self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT)
  481. mainFrame.pack(expand = 1, fill = BOTH)
  482. def onDestroy(self, event):
  483. messenger.send('AWL_close',[self.nodeName])
  484. '''
  485. If you have open any thing, please rewrite here!
  486. '''
  487. pass
  488. def selectAnim(self,name):
  489. #################################################################
  490. # selectAnim(self, name)
  491. # This function will be called if user select an animation on the list.
  492. #################################################################
  493. if name in self.animDic:
  494. self.AnimFile_1.setvalue = self.animDic[name]
  495. return
  496. def Browse_1(self):
  497. #################################################################
  498. # Browse_1(self)
  499. # when the browse button pused, this function will be called.
  500. # Do nothing but open a file dialog for user to set the path to target file
  501. # Then, set the path back to the entry on the panel.
  502. #################################################################
  503. AnimFilename = askopenfilename(
  504. defaultextension = '.egg',
  505. filetypes = (('Egg Files', '*.egg'),
  506. ('Bam Files', '*.bam'),
  507. ('All files', '*')),
  508. initialdir = '.',
  509. title = 'File Path for Anim 1',
  510. parent = self.parent)
  511. if AnimFilename:
  512. self.AnimFile_1.setvalue(AnimFilename)
  513. return
  514. def addIntoList(self):
  515. #################################################################
  516. # addIntoList(self)
  517. # This function will be called each time when user click on the
  518. # "Add to Load" button. This function will read the current data
  519. # on the panel into a dictionary. then reset the list of the animation
  520. # name list on this panel.(not the one in the animation panel...)
  521. #
  522. # This function won't load any animation....
  523. #
  524. #################################################################
  525. name = self.AnimName_1.get()
  526. self.animDic[name] = Filename.fromOsSpecific(self.AnimFile_1.getvalue()).getFullpath()
  527. if name in self.animList:
  528. pass
  529. else:
  530. self.animList.append(name)
  531. self.AnimName_1.setlist(self.animList)
  532. print self.animDic
  533. return
  534. def ok_press(self):
  535. #################################################################
  536. # ok_press(Self)
  537. # This functiion will be called when user click on the "OK"
  538. # button. This function will send a message along with the animation
  539. # file we wish to load for this actor.
  540. # Then, it will close the panel itself.
  541. #################################################################
  542. messenger.send('AW_AnimationLoad',[self.Actor,self.animDic])
  543. #print self.animDic
  544. self.quit()
  545. return