Task.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. from pandac.PandaModules import *
  2. from pandac.libpandaexpressModules import *
  3. from direct.directnotify.DirectNotifyGlobal import *
  4. from direct.showbase.PythonUtil import *
  5. from direct.showbase.MessengerGlobal import *
  6. import time
  7. import fnmatch
  8. import string
  9. import signal
  10. from libheapq import heappush, heappop
  11. # MRM: Need to make internal task variables like time, name, index
  12. # more unique (less likely to have name clashes)
  13. exit = -1
  14. done = 0
  15. cont = 1
  16. # Task needs this because it might run before __builtin__.globalClock
  17. # can be set.
  18. globalClock = ClockObject.getGlobalClock()
  19. def print_exc_plus():
  20. """
  21. Print the usual traceback information, followed by a listing of all the
  22. local variables in each frame.
  23. """
  24. import sys
  25. import traceback
  26. tb = sys.exc_info()[2]
  27. while 1:
  28. if not tb.tb_next:
  29. break
  30. tb = tb.tb_next
  31. stack = []
  32. f = tb.tb_frame
  33. while f:
  34. stack.append(f)
  35. f = f.f_back
  36. stack.reverse()
  37. traceback.print_exc()
  38. print "Locals by frame, innermost last"
  39. for frame in stack:
  40. print
  41. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  42. frame.f_code.co_filename,
  43. frame.f_lineno)
  44. for key, value in frame.f_locals.items():
  45. print "\t%20s = " % key,
  46. #We have to be careful not to cause a new error in our error
  47. #printer! Calling str() on an unknown object could cause an
  48. #error we don't want.
  49. try:
  50. print value
  51. except:
  52. print "<ERROR WHILE PRINTING VALUE>"
  53. class Task:
  54. count = 0
  55. def __init__(self, callback, priority = 0):
  56. # Unique ID for each task
  57. self.id = Task.count
  58. Task.count += 1
  59. self.__call__ = callback
  60. self.__priority = priority
  61. self.__removed = 0
  62. if TaskManager.taskTimerVerbose:
  63. self.dt = 0.0
  64. self.avgDt = 0.0
  65. self.maxDt = 0.0
  66. self.runningTotal = 0.0
  67. self.pstats = None
  68. self.extraArgs = None
  69. # Used for doLaters
  70. self.wakeTime = 0.0
  71. # Used for putting into the doLaterList
  72. # the heapq calls __cmp__ via the rich compare function
  73. def __cmp__(self, other):
  74. if isinstance(other, Task):
  75. if self.wakeTime < other.wakeTime:
  76. return -1
  77. elif self.wakeTime > other.wakeTime:
  78. return 1
  79. # If the wakeTimes happen to be the same, just
  80. # sort them based on id
  81. else:
  82. return cmp(id(self), id(other))
  83. # This is important for people doing a (task != None) and such.
  84. else:
  85. return cmp(id(self), id(other))
  86. # According to the Python manual (3.3.1), if you define a cmp operator
  87. # you should also define a hash operator or your objects will not be
  88. # usable in dictionaries. Since no two task objects are unique, we can
  89. # just return the unique id.
  90. def __hash__(self):
  91. return self.id
  92. def remove(self):
  93. if not self.__removed:
  94. self.__removed = 1
  95. # Remove any refs to real objects
  96. # In case we hang around the doLaterList for a while
  97. del self.__call__
  98. del self.extraArgs
  99. def isRemoved(self):
  100. return self.__removed
  101. def getPriority(self):
  102. return self.__priority
  103. def setPriority(self, pri):
  104. self.__priority = pri
  105. def setStartTimeFrame(self, startTime, startFrame):
  106. self.starttime = startTime
  107. self.startframe = startFrame
  108. def setCurrentTimeFrame(self, currentTime, currentFrame):
  109. # Calculate and store this task's time (relative to when it started)
  110. self.time = currentTime - self.starttime
  111. self.frame = currentFrame - self.startframe
  112. def setupPStats(self, name):
  113. if __debug__ and TaskManager.taskTimerVerbose:
  114. from pandac import PStatCollector
  115. self.pstats = PStatCollector.PStatCollector("App:Show code:" + name)
  116. def finishTask(self, verbose):
  117. if hasattr(self, "uponDeath"):
  118. self.uponDeath(self)
  119. if verbose:
  120. # We regret to announce...
  121. messenger.send('TaskManager-removeTask', sentArgs = [self, self.name])
  122. del self.uponDeath
  123. def __repr__(self):
  124. if hasattr(self, 'name'):
  125. return ('Task id: %s, name %s' % (self.id, self.name))
  126. else:
  127. return ('Task id: %s, no name' % (self.id))
  128. def pause(delayTime):
  129. def func(self):
  130. if (self.time < self.delayTime):
  131. return cont
  132. else:
  133. return done
  134. task = Task(func)
  135. task.name = 'pause'
  136. task.delayTime = delayTime
  137. return task
  138. def sequence(*taskList):
  139. return make_sequence(taskList)
  140. def make_sequence(taskList):
  141. def func(self):
  142. frameFinished = 0
  143. taskDoneStatus = -1
  144. while (not frameFinished):
  145. task = self.taskList[self.index]
  146. # If this is a new task, set its start time and frame
  147. if (self.index > self.prevIndex):
  148. task.setStartTimeFrame(self.time, self.frame)
  149. self.prevIndex = self.index
  150. # Calculate this task's time since it started
  151. task.setCurrentTimeFrame(self.time, self.frame)
  152. # Execute the current task
  153. ret = task(task)
  154. # Check the return value from the task
  155. # If this current task wants to continue,
  156. # come back to it next frame
  157. if (ret == cont):
  158. taskDoneStatus = cont
  159. frameFinished = 1
  160. # If this task is done, increment the index so that next frame
  161. # we will start executing the next task on the list
  162. elif (ret == done):
  163. self.index = self.index + 1
  164. taskDoneStatus = cont
  165. frameFinished = 0
  166. # If this task wants to exit, the sequence exits
  167. elif (ret == exit):
  168. taskDoneStatus = exit
  169. frameFinished = 1
  170. # If we got to the end of the list, this sequence is done
  171. if (self.index >= len(self.taskList)):
  172. # TaskManager.notify.debug('sequence done: ' + self.name)
  173. frameFinished = 1
  174. taskDoneStatus = done
  175. return taskDoneStatus
  176. task = Task(func)
  177. task.name = 'sequence'
  178. task.taskList = taskList
  179. task.prevIndex = -1
  180. task.index = 0
  181. return task
  182. def resetSequence(task):
  183. # Should this automatically be done as part of spawnTaskNamed?
  184. # Or should one have to create a new task instance every time
  185. # one wishes to spawn a task (currently sequences and can
  186. # only be fired off once
  187. task.index = 0
  188. task.prevIndex = -1
  189. def loop(*taskList):
  190. return make_loop(taskList)
  191. def make_loop(taskList):
  192. def func(self):
  193. frameFinished = 0
  194. taskDoneStatus = -1
  195. while (not frameFinished):
  196. task = self.taskList[self.index]
  197. # If this is a new task, set its start time and frame
  198. if (self.index > self.prevIndex):
  199. task.setStartTimeFrame(self.time, self.frame)
  200. self.prevIndex = self.index
  201. # Calculate this task's time since it started
  202. task.setCurrentTimeFrame(self.time, self.frame)
  203. # Execute the current task
  204. ret = task(task)
  205. # Check the return value from the task
  206. # If this current task wants to continue,
  207. # come back to it next frame
  208. if (ret == cont):
  209. taskDoneStatus = cont
  210. frameFinished = 1
  211. # If this task is done, increment the index so that next frame
  212. # we will start executing the next task on the list
  213. # TODO: we should go to the next frame now
  214. elif (ret == done):
  215. self.index = self.index + 1
  216. taskDoneStatus = cont
  217. frameFinished = 0
  218. # If this task wants to exit, the sequence exits
  219. elif (ret == exit):
  220. taskDoneStatus = exit
  221. frameFinished = 1
  222. # If we got to the end of the list, wrap back around
  223. if (self.index >= len(self.taskList)):
  224. self.prevIndex = -1
  225. self.index = 0
  226. frameFinished = 1
  227. return taskDoneStatus
  228. task = Task(func)
  229. task.name = 'loop'
  230. task.taskList = taskList
  231. task.prevIndex = -1
  232. task.index = 0
  233. return task
  234. class TaskPriorityList(list):
  235. def __init__(self, priority):
  236. self.__priority = priority
  237. self.__emptyIndex = 0
  238. def getPriority(self):
  239. return self.__priority
  240. def add(self, task):
  241. if (self.__emptyIndex >= len(self)):
  242. self.append(task)
  243. self.__emptyIndex += 1
  244. else:
  245. self[self.__emptyIndex] = task
  246. self.__emptyIndex += 1
  247. def remove(self, i):
  248. assert(i <= len(self))
  249. if (len(self) == 1) and (i == 1):
  250. self[i] = None
  251. self.__emptyIndex = 0
  252. else:
  253. # Swap the last element for this one
  254. lastElement = self[self.__emptyIndex-1]
  255. self[i] = lastElement
  256. self[self.__emptyIndex-1] = None
  257. self.__emptyIndex -= 1
  258. class TaskManager:
  259. # These class vars are generally overwritten by Config variables which
  260. # are read in at the start of a show (ShowBase.py or AIStart.py)
  261. notify = None
  262. # TODO: there is a bit of a bug when you default this to 0. The first
  263. # task we make, the doLaterProcessor, needs to have this set to 1 or
  264. # else we get an error.
  265. taskTimerVerbose = 1
  266. extendedExceptions = 0
  267. pStatsTasks = 0
  268. def __init__(self):
  269. self.running = 0
  270. self.stepping = 0
  271. self.taskList = []
  272. # Dictionary of priority to newTaskLists
  273. self.pendingTaskDict = {}
  274. # List of tasks scheduled to execute in the future
  275. self.doLaterList = []
  276. self.currentTime, self.currentFrame = self.__getTimeFrame()
  277. if (TaskManager.notify == None):
  278. TaskManager.notify = directNotify.newCategory("TaskManager")
  279. self.fKeyboardInterrupt = 0
  280. self.interruptCount = 0
  281. self.resumeFunc = None
  282. self.fVerbose = 0
  283. # Dictionary of task name to list of tasks with that name
  284. self.nameDict = {}
  285. self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
  286. def stepping(self, value):
  287. self.stepping = value
  288. def setVerbose(self, value):
  289. self.fVerbose = value
  290. messenger.send('TaskManager-setVerbose', sentArgs = [value])
  291. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  292. self.fKeyboardInterrupt = 1
  293. self.interruptCount += 1
  294. if self.interruptCount == 2:
  295. # The user must really want to interrupt this process
  296. # Next time around use the default interrupt handler
  297. signal.signal(signal.SIGINT, signal.default_int_handler)
  298. def hasTaskNamed(self, taskName):
  299. # TODO: check pending task list
  300. # Get the tasks with this name
  301. tasks = self.nameDict.get(taskName)
  302. # If we found some, see if any of them are still active (not removed)
  303. if tasks:
  304. for task in tasks:
  305. if not task.isRemoved():
  306. return 1
  307. # Didnt find any, return 0
  308. return 0
  309. def getTasksNamed(self, taskName):
  310. # TODO: check pending tasks
  311. # Get the tasks with this name
  312. tasks = self.nameDict.get(taskName, [])
  313. # Filter out the tasks that have been removed
  314. if tasks:
  315. tasks = filter(lambda task: not task.isRemoved(), tasks)
  316. return tasks
  317. def __doLaterProcessor(self, task):
  318. # Removing the tasks during the for loop is a bad idea
  319. # Instead we just flag them as removed
  320. # Later, somebody else cleans them out
  321. while self.doLaterList:
  322. # Check the first one on the list to see if it is ready
  323. dl = self.doLaterList[0]
  324. if dl.isRemoved():
  325. # Get rid of this task forever
  326. heappop(self.doLaterList)
  327. continue
  328. # If the time now is less than the start of the doLater + delay
  329. # then we are not ready yet, continue to next one
  330. elif task.time < dl.wakeTime:
  331. # Since the list is sorted, the first one we get to, that
  332. # is not ready to go, we can return
  333. break
  334. else:
  335. # Take it off the doLaterList, set its time, and make it pending
  336. # TaskManager.notify.debug('__doLaterProcessor: spawning %s' % (dl))
  337. heappop(self.doLaterList)
  338. dl.setStartTimeFrame(self.currentTime, self.currentFrame)
  339. self.__addPendingTask(dl)
  340. continue
  341. return cont
  342. def doMethodLater(self, delayTime, func, taskName, extraArgs=None, uponDeath=None):
  343. task = Task(func)
  344. task.name = taskName
  345. if extraArgs:
  346. task.extraArgs = extraArgs
  347. if uponDeath:
  348. task.uponDeath = uponDeath
  349. # TaskManager.notify.debug('spawning doLater: %s' % (task))
  350. # Add this task to the nameDict
  351. nameList = self.nameDict.get(taskName)
  352. if nameList:
  353. nameList.append(task)
  354. else:
  355. self.nameDict[taskName] = [task]
  356. # be sure to ask the globalClock for the current frame time
  357. # rather than use a cached value; globalClock's frame time may
  358. # have been synced since the start of this frame
  359. currentTime = globalClock.getFrameTime()
  360. # Cache the time we should wake up for easier sorting
  361. task.wakeTime = currentTime + delayTime
  362. # Push this onto the doLaterList. The heap maintains the sorting.
  363. heappush(self.doLaterList, task)
  364. if self.fVerbose:
  365. # Alert the world, a new task is born!
  366. messenger.send('TaskManager-spawnDoLater',
  367. sentArgs = [task, task.name, task.id])
  368. return task
  369. def add(self, funcOrTask, name, priority=0, extraArgs=None, uponDeath=None):
  370. """
  371. Add a new task to the taskMgr.
  372. You can add a Task object or a method that takes one argument.
  373. """
  374. # TaskManager.notify.debug('add: %s' % (name))
  375. if isinstance(funcOrTask, Task):
  376. task = funcOrTask
  377. elif callable(funcOrTask):
  378. task = Task(funcOrTask, priority)
  379. else:
  380. self.notify.error('add: Tried to add a task that was not a Task or a func')
  381. task.setPriority(priority)
  382. task.name = name
  383. if extraArgs:
  384. task.extraArgs = extraArgs
  385. if uponDeath:
  386. task.uponDeath = uponDeath
  387. # be sure to ask the globalClock for the current frame time
  388. # rather than use a cached value; globalClock's frame time may
  389. # have been synced since the start of this frame
  390. currentTime = globalClock.getFrameTime()
  391. task.setStartTimeFrame(currentTime, self.currentFrame)
  392. nameList = self.nameDict.get(name)
  393. if nameList:
  394. nameList.append(task)
  395. else:
  396. self.nameDict[name] = [task]
  397. # Put it on the list for the end of this frame
  398. self.__addPendingTask(task)
  399. return task
  400. def __addPendingTask(self, task):
  401. # TaskManager.notify.debug('__addPendingTask: %s' % (task.name))
  402. pri = task.getPriority()
  403. taskPriList = self.pendingTaskDict.get(pri)
  404. if not taskPriList:
  405. taskPriList = TaskPriorityList(pri)
  406. self.pendingTaskDict[pri] = taskPriList
  407. taskPriList.add(task)
  408. def __addNewTask(self, task):
  409. # The taskList is really an ordered list of TaskPriorityLists
  410. # search back from the end of the list until we find a
  411. # taskList with a lower priority, or we hit the start of the list
  412. taskPriority = task.getPriority()
  413. index = len(self.taskList) - 1
  414. while (1):
  415. if (index < 0):
  416. newList = TaskPriorityList(taskPriority)
  417. newList.add(task)
  418. # Add the new list to the beginning of the taskList
  419. self.taskList.insert(0, newList)
  420. break
  421. taskListPriority = self.taskList[index].getPriority()
  422. if (taskListPriority == taskPriority):
  423. self.taskList[index].add(task)
  424. break
  425. elif (taskListPriority > taskPriority):
  426. index = index - 1
  427. elif (taskListPriority < taskPriority):
  428. # Time to insert
  429. newList = TaskPriorityList(taskPriority)
  430. newList.add(task)
  431. # Insert this new priority level
  432. # If we are already at the end, just append it
  433. if (index == len(self.taskList)-1):
  434. self.taskList.append(newList)
  435. else:
  436. # Otherwise insert it
  437. self.taskList.insert(index+1, newList)
  438. break
  439. if __debug__:
  440. if self.pStatsTasks and task.name != "igloop":
  441. # Get the PStats name for the task. By convention,
  442. # this is everything until the first hyphen; the part
  443. # of the task name following the hyphen is generally
  444. # used to differentiate particular tasks that do the
  445. # same thing to different objects.
  446. name = task.name
  447. hyphen = name.find('-')
  448. if hyphen >= 0:
  449. name = name[0:hyphen]
  450. task.setupPStats(name)
  451. if self.fVerbose:
  452. # Alert the world, a new task is born!
  453. messenger.send('TaskManager-spawnTask', sentArgs = [task, task.name, index])
  454. return task
  455. def remove(self, taskOrName):
  456. if type(taskOrName) == type(''):
  457. return self.__removeTasksNamed(taskOrName)
  458. elif isinstance(taskOrName, Task):
  459. return self.__removeTasksEqual(taskOrName)
  460. else:
  461. self.notify.error('remove takes a string or a Task')
  462. def removeTasksMatching(self, taskPattern):
  463. """removeTasksMatching(self, string taskPattern)
  464. Removes tasks whose names match the pattern, which can include
  465. standard shell globbing characters like *, ?, and [].
  466. """
  467. # TaskManager.notify.debug('removing tasks matching: ' + taskPattern)
  468. num = 0
  469. keyList = filter(lambda key: fnmatch.fnmatchcase(key, taskPattern), self.nameDict.keys())
  470. for key in keyList:
  471. num += self.__removeTasksNamed(key)
  472. return num
  473. def __removeTasksEqual(self, task):
  474. # Remove this task from the nameDict (should be a short list)
  475. if self.__removeTaskFromNameDict(task):
  476. # TaskManager.notify.debug('__removeTasksEqual: removing task: %s' % (task))
  477. # Flag the task for removal from the real list
  478. task.remove()
  479. task.finishTask(self.fVerbose)
  480. return 1
  481. else:
  482. return 0
  483. def __removeTasksNamed(self, taskName):
  484. tasks = self.nameDict.get(taskName)
  485. if not tasks:
  486. return 0
  487. # TaskManager.notify.debug('__removeTasksNamed: removing tasks named: %s' % (taskName))
  488. for task in tasks:
  489. # Flag for removal
  490. task.remove()
  491. task.finishTask(self.fVerbose)
  492. # Record the number of tasks removed
  493. num = len(tasks)
  494. # Blow away the nameDict entry completely
  495. del self.nameDict[taskName]
  496. return num
  497. def __removeTaskFromNameDict(self, task):
  498. taskName = task.name
  499. # If this is the only task with that name, remove the dict entry
  500. tasksWithName = self.nameDict.get(taskName)
  501. if tasksWithName:
  502. if task in tasksWithName:
  503. # If this is the last element, just remove the entry
  504. # from the dictionary
  505. if len(tasksWithName) == 1:
  506. del self.nameDict[taskName]
  507. else:
  508. tasksWithName.remove(task)
  509. return 1
  510. return 0
  511. def __executeTask(self, task):
  512. task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
  513. if not self.taskTimerVerbose:
  514. # don't record timing info
  515. if task.extraArgs:
  516. ret = apply(task, task.extraArgs)
  517. else:
  518. ret = task(task)
  519. else:
  520. # Run the task and check the return value
  521. if task.pstats:
  522. task.pstats.start()
  523. startTime = globalClock.getRealTime()
  524. if task.extraArgs:
  525. ret = apply(task, task.extraArgs)
  526. else:
  527. ret = task(task)
  528. endTime = globalClock.getRealTime()
  529. if task.pstats:
  530. task.pstats.stop()
  531. # Record the dt
  532. dt = endTime - startTime
  533. task.dt = dt
  534. # See if this is the new max
  535. if dt > task.maxDt:
  536. task.maxDt = dt
  537. # Record the running total of all dts so we can compute an average
  538. task.runningTotal = task.runningTotal + dt
  539. if (task.frame > 0):
  540. task.avgDt = (task.runningTotal / task.frame)
  541. else:
  542. task.avgDt = 0
  543. return ret
  544. def __stepThroughList(self, taskPriList):
  545. # Traverse the taskPriList with an iterator
  546. i = 0
  547. while (i < len(taskPriList)):
  548. task = taskPriList[i]
  549. # See if we are at the end of the real tasks
  550. if task is None:
  551. break
  552. # See if this task has been removed in show code
  553. if task.isRemoved():
  554. # assert(TaskManager.notify.debug('__stepThroughList: task is flagged for removal %s' % (task)))
  555. # If it was removed in show code, it will need finishTask run
  556. # If it was removed by the taskMgr, it will not, but that is ok
  557. # because finishTask is safe to call twice
  558. task.finishTask(self.fVerbose)
  559. taskPriList.remove(i)
  560. # Do not increment the iterator
  561. continue
  562. # Now actually execute the task
  563. ret = self.__executeTask(task)
  564. # See if the task is done
  565. if (ret == cont):
  566. # Leave it for next frame, its not done yet
  567. pass
  568. elif ((ret == done) or (ret == exit) or (ret == None)):
  569. # assert(TaskManager.notify.debug('__stepThroughList: task is finished %s' % (task)))
  570. # Remove the task
  571. if not task.isRemoved():
  572. # assert(TaskManager.notify.debug('__stepThroughList: task not removed %s' % (task)))
  573. task.remove()
  574. # Note: Should not need to remove from doLaterList here because
  575. # this task is not in the doLaterList
  576. task.finishTask(self.fVerbose)
  577. self.__removeTaskFromNameDict(task)
  578. else:
  579. # assert(TaskManager.notify.debug('__stepThroughList: task already removed %s' % (task)))
  580. self.__removeTaskFromNameDict(task)
  581. taskPriList.remove(i)
  582. # Do not increment the iterator
  583. continue
  584. else:
  585. raise StandardError, "Task named %s did not return cont, exit, done, or None" % task.name
  586. # Move to the next element
  587. i += 1
  588. def __addPendingTasksToTaskList(self):
  589. # Now that we are all done, add any left over pendingTasks generated in
  590. # priority levels lower or higher than where we were when we iterated
  591. for taskList in self.pendingTaskDict.values():
  592. for task in taskList:
  593. if (task and not task.isRemoved()):
  594. # assert(TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name)))
  595. self.__addNewTask(task)
  596. self.pendingTaskDict.clear()
  597. def step(self):
  598. # assert(TaskManager.notify.debug('step: begin'))
  599. self.currentTime, self.currentFrame = self.__getTimeFrame()
  600. # Replace keyboard interrupt handler during task list processing
  601. # so we catch the keyboard interrupt but don't handle it until
  602. # after task list processing is complete.
  603. self.fKeyboardInterrupt = 0
  604. self.interruptCount = 0
  605. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  606. # Traverse the task list in order because it is in priority order
  607. priIndex = 0
  608. while priIndex < len(self.taskList):
  609. taskPriList = self.taskList[priIndex]
  610. pri = taskPriList.getPriority()
  611. # assert(TaskManager.notify.debug('step: running through taskList at pri: %s, priIndex: %s' % (pri, priIndex)))
  612. self.__stepThroughList(taskPriList)
  613. # Now see if that generated any pending tasks for this taskPriList
  614. pendingTasks = self.pendingTaskDict.get(pri)
  615. while pendingTasks:
  616. # assert(TaskManager.notify.debug('step: running through pending tasks at pri: %s' % (pri)))
  617. # Remove them from the pendingTaskDict
  618. del self.pendingTaskDict[pri]
  619. # Execute them
  620. self.__stepThroughList(pendingTasks)
  621. # Add these to the real taskList
  622. for task in pendingTasks:
  623. if (task and not task.isRemoved()):
  624. # assert(TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name)))
  625. self.__addNewTask(task)
  626. # See if we generated any more for this pri level
  627. pendingTasks = self.pendingTaskDict.get(pri)
  628. # Any new tasks that were made pending should be converted
  629. # to real tasks now in case they need to run this frame at a
  630. # later priority level
  631. self.__addPendingTasksToTaskList()
  632. # Go to the next priority level
  633. priIndex += 1
  634. # Add new pending tasks
  635. self.__addPendingTasksToTaskList()
  636. # Restore default interrupt handler
  637. signal.signal(signal.SIGINT, signal.default_int_handler)
  638. if self.fKeyboardInterrupt:
  639. raise KeyboardInterrupt
  640. return
  641. def run(self):
  642. # Set the clock to have last frame's time in case we were
  643. # Paused at the prompt for a long time
  644. t = globalClock.getFrameTime()
  645. timeDelta = t - globalClock.getRealTime()
  646. globalClock.setRealTime(t)
  647. messenger.send("resetClock", [timeDelta])
  648. if self.resumeFunc != None:
  649. self.resumeFunc()
  650. if self.stepping:
  651. self.step()
  652. else:
  653. self.running = 1
  654. while self.running:
  655. try:
  656. self.step()
  657. except KeyboardInterrupt:
  658. self.stop()
  659. except:
  660. if self.extendedExceptions:
  661. self.stop()
  662. print_exc_plus()
  663. else:
  664. raise
  665. def stop(self):
  666. # Set a flag so we will stop before beginning next frame
  667. self.running = 0
  668. def replaceMethod(self, oldMethod, newFunction):
  669. import new
  670. for taskPriList in self.taskList:
  671. for task in taskPriList:
  672. if (task is None) or (task.isRemoved()):
  673. break
  674. method = task.__call__
  675. if (type(method) == types.MethodType):
  676. function = method.im_func
  677. else:
  678. function = method
  679. #print ('function: ' + `function` + '\n' +
  680. # 'method: ' + `method` + '\n' +
  681. # 'oldMethod: ' + `oldMethod` + '\n' +
  682. # 'newFunction: ' + `newFunction` + '\n')
  683. if (function == oldMethod):
  684. newMethod = new.instancemethod(newFunction,
  685. method.im_self,
  686. method.im_class)
  687. task.__call__ = newMethod
  688. # Found it return true
  689. return 1
  690. return 0
  691. def __repr__(self):
  692. taskNameWidth = 32
  693. dtWidth = 10
  694. priorityWidth = 10
  695. totalDt = 0
  696. totalAvgDt = 0
  697. str = "The taskMgr is handling:\n"
  698. str += ('taskList'.ljust(taskNameWidth)
  699. + 'dt(ms)'.rjust(dtWidth)
  700. + 'avg'.rjust(dtWidth)
  701. + 'max'.rjust(dtWidth)
  702. + 'priority'.rjust(priorityWidth)
  703. + '\n')
  704. str += '-------------------------------------------------------------------------\n'
  705. for taskPriList in self.taskList:
  706. priority = `taskPriList.getPriority()`
  707. for task in taskPriList:
  708. if task is None:
  709. break
  710. if task.isRemoved():
  711. taskName = '(R)' + task.name
  712. else:
  713. taskName = task.name
  714. if self.taskTimerVerbose:
  715. import fpformat
  716. totalDt = totalDt + task.dt
  717. totalAvgDt = totalAvgDt + task.avgDt
  718. str += (taskName.ljust(taskNameWidth)
  719. + fpformat.fix(task.dt*1000, 2).rjust(dtWidth)
  720. + fpformat.fix(task.avgDt*1000, 2).rjust(dtWidth)
  721. + fpformat.fix(task.maxDt*1000, 2).rjust(dtWidth)
  722. + priority.rjust(priorityWidth)
  723. + '\n')
  724. else:
  725. str += (task.name.ljust(taskNameWidth)
  726. + '----'.rjust(dtWidth)
  727. + '----'.rjust(dtWidth)
  728. + '----'.rjust(dtWidth)
  729. + priority.rjust(priorityWidth)
  730. + '\n')
  731. str += '-------------------------------------------------------------------------\n'
  732. str += 'pendingTasks\n'
  733. str += '-------------------------------------------------------------------------\n'
  734. for pri, taskList in self.pendingTaskDict.items():
  735. for task in taskList:
  736. if task.isRemoved():
  737. taskName = '(PR)' + task.name
  738. else:
  739. taskName = '(P)' + task.name
  740. if (self.taskTimerVerbose):
  741. import fpformat
  742. str += (' ' + taskName.ljust(taskNameWidth-2)
  743. + fpformat.fix(pri, 2).rjust(dtWidth)
  744. + '\n')
  745. else:
  746. str += (' ' + taskName.ljust(taskNameWidth-2)
  747. + '----'.rjust(dtWidth)
  748. + '\n')
  749. str += '-------------------------------------------------------------------------\n'
  750. str += ('doLaterList'.ljust(taskNameWidth)
  751. + 'waitTime(s)'.rjust(dtWidth)
  752. + '\n')
  753. str += '-------------------------------------------------------------------------\n'
  754. # When we print, show the doLaterList in actual sorted order.
  755. # The priority heap is not actually in order - it is a tree
  756. # Make a shallow copy so we can sort it
  757. sortedDoLaterList = self.doLaterList[:]
  758. sortedDoLaterList.sort()
  759. sortedDoLaterList.reverse()
  760. for task in sortedDoLaterList:
  761. remainingTime = ((task.wakeTime) - self.currentTime)
  762. if task.isRemoved():
  763. taskName = '(R)' + task.name
  764. else:
  765. taskName = task.name
  766. if (self.taskTimerVerbose):
  767. import fpformat
  768. str += (' ' + taskName.ljust(taskNameWidth-2)
  769. + fpformat.fix(remainingTime, 2).rjust(dtWidth)
  770. + '\n')
  771. else:
  772. str += (' ' + taskName.ljust(taskNameWidth-2)
  773. + '----'.rjust(dtWidth)
  774. + '\n')
  775. str += '-------------------------------------------------------------------------\n'
  776. if (self.taskTimerVerbose):
  777. import fpformat
  778. str += ('total'.ljust(taskNameWidth)
  779. + fpformat.fix(totalDt*1000, 2).rjust(dtWidth)
  780. + fpformat.fix(totalAvgDt*1000, 2).rjust(dtWidth)
  781. + '\n')
  782. else:
  783. str += ('total'.ljust(taskNameWidth)
  784. + '----'.rjust(dtWidth)
  785. + '----'.rjust(dtWidth)
  786. + '\n')
  787. str += "End of taskMgr info\n"
  788. return str
  789. def resetStats(self):
  790. # WARNING: this screws up your do-later timings
  791. if self.taskTimerVerbose:
  792. for task in self.taskList:
  793. task.dt = 0
  794. task.avgDt = 0
  795. task.maxDt = 0
  796. task.runningTotal = 0
  797. task.setStartTimeFrame(self.currentTime, self.currentFrame)
  798. def popupControls(self):
  799. from direct.tkpanels import TaskManagerPanel
  800. return TaskManagerPanel.TaskManagerPanel(self)
  801. def __getTimeFrame(self):
  802. # WARNING: If you are testing tasks without an igloop,
  803. # you must manually tick the clock
  804. # Ask for the time last frame
  805. return globalClock.getFrameTime(), globalClock.getFrameCount()
  806. """
  807. import Task
  808. def goo(task):
  809. print 'goo'
  810. return Task.done
  811. def bar(task):
  812. print 'bar'
  813. taskMgr.add(goo, 'goo')
  814. return Task.done
  815. def foo(task):
  816. print 'foo'
  817. taskMgr.add(bar, 'bar')
  818. return Task.done
  819. taskMgr.add(foo, 'foo')
  820. """