| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- from libpandaexpressModules import *
- from DirectNotify import *
- from PythonUtil import *
- import time
- import fnmatch
- import string
- # MRM: Need to make internal task variables like time, name, index
- # more unique (less likely to have name clashes)
- exit = -1
- done = 0
- cont = 1
- # Store the global clock
- globalClock = ClockObject.getGlobalClock()
- def getTimeFrame():
- # WARNING: If you are testing tasks without an igloop,
- # you must manually tick the clock
- # Ask for the time last frame
- t = globalClock.getTime()
- # Get the new frame count
- f = globalClock.getFrameCount()
- return t, f
- class Task:
- def __init__(self, callback, priority = 0):
- self.__call__ = callback
- self._priority = priority
- self.uponDeath = None
- def getPriority(self):
- return self._priority
- def setStartTimeFrame(self, startTime, startFrame):
- self.starttime = startTime
- self.startframe = startFrame
- def setCurrentTimeFrame(self, currentTime, currentFrame):
- # Calculate and store this task's time (relative to when it started)
- self.time = currentTime - self.starttime
- self.frame = currentFrame - self.startframe
- def doLater(delayTime, task, taskName):
- task.name = taskName
- # make a sequence out of the delay and the task
- seq = sequence(pause(delayTime), task)
- return seq
- def spawnMethodNamed(self, func, name):
- task = Task(func)
- self.spawnTaskNamed(task, name)
- def pause(delayTime):
- def func(self):
- if (self.time < self.delayTime):
- return cont
- else:
- # Time is up, return done
- TaskManager.notify.debug('pause done: ' + self.name)
- return done
- task = Task(func)
- task.name = 'pause'
- task.delayTime = delayTime
- return task
- def release():
- def func(self):
- # A release is immediately done
- TaskManager.notify.debug('release done: ' + self.name)
- return done
- task = Task(func)
- task.name = 'release'
- return task
- def sequence(*taskList):
- return make_sequence(taskList)
- def make_sequence(taskList):
- def func(self):
- # If we got to the end of the list, this sequence is done
- if (self.index >= len(self.taskList)):
- TaskManager.notify.debug('sequence done: ' + self.name)
- return done
- else:
- task = self.taskList[self.index]
- # If this is a new task, set it's start time and frame
- if (self.index > self.prevIndex):
- task.setStartTimeFrame(self.time, self.frame)
- self.prevIndex = self.index
- # Calculate this task's time since it started
- task.setCurrentTimeFrame(self.time, self.frame)
- # Execute the current task
- ret = task(task)
- # Check the return value from the task
- # If this current task wants to continue,
- # come back to it next frame
- if (ret == cont):
- return cont
- # If this task is done, increment the index so that next frame
- # we will start executing the next task on the list
- elif (ret == done):
- self.index = self.index + 1
- return cont
- # If this task wants to exit, the sequence exits
- elif (ret == exit):
- return exit
- task = Task(func)
- task.name = 'sequence'
- task.taskList = taskList
- task.prevIndex = -1
- task.index = 0
- return task
- def resetSequence(task):
- # Should this automatically be done as part of spawnTaskNamed?
- # Or should one have to create a new task instance every time
- # one wishes to spawn a task (currently sequences and can
- # only be fired off once
- task.index = 0
- task.prevIndex = -1
- def loop(*taskList):
- return make_loop(taskList)
- def make_loop(taskList):
- def func(self):
- # If we got to the end of the list, this sequence is done
- if (self.index >= len(self.taskList)):
- TaskManager.notify.debug('sequence done, looping: ' + self.name)
- self.prevIndex = -1
- self.index = 0
- return cont
- else:
- task = self.taskList[self.index]
- # If this is a new task, set it's start time and frame
- if (self.index > self.prevIndex):
- task.setStartTimeFrame(self.time, self.frame)
- self.prevIndex = self.index
- # Calculate this task's time since it started
- task.setCurrentTimeFrame(self.time, self.frame)
- # Execute the current task
- ret = task(task)
- # Check the return value from the task
- # If this current task wants to continue,
- # come back to it next frame
- if (ret == cont):
- return cont
- # If this task is done, increment the index so that next frame
- # we will start executing the next task on the list
- elif (ret == done):
- self.index = self.index + 1
- return cont
- # If this task wants to exit, the sequence exits
- elif (ret == exit):
- return exit
- task = Task(func)
- task.name = 'loop'
- task.taskList = taskList
- task.prevIndex = -1
- task.index = 0
- return task
- def makeSpawner(task, taskName, taskMgr):
- def func(self):
- self.taskMgr.spawnTaskNamed(self.task, self.taskName)
- return done
- newTask = Task(func)
- newTask.name = taskName + "-spawner"
- newTask.task = task
- newTask.taskName = taskName
- newTask.taskMgr = taskMgr
- return newTask
- def makeSequenceFromTimeline(timelineList, taskMgr):
- timeline = []
- lastPause = 0
- sortedList = list(timelineList)
- sortedList.sort()
- for triple in sortedList:
- t = triple[0] - lastPause
- lastPause = triple[0]
- task = triple[1]
- taskName = triple[2]
- timeline.append(pause(t))
- timeline.append(makeSpawner(task, taskName, taskMgr))
- return make_sequence(timeline)
- def timeline(*timelineList):
- def func(self):
- # Step our sub task manager (returns the number of tasks remaining)
- lenTaskList = self.taskMgr.step()
- # The sequence start time is the same as our start time
- self.sequence.time = self.time
- self.sequence.frame = self.frame
- if (not self.sequenceDone):
- # Execute the sequence for this frame
- seqRet = self.sequence(self.sequence)
- # See if sequence is done
- if (seqRet == done):
- self.sequenceDone = 1
- # See if the timeline is done
- if (lenTaskList == 0):
- TaskManager.notify.debug('timeline done: ' + self.name)
- return done
- else:
- return cont
- else:
- return cont
- else:
- return cont
- task = Task(func)
- task.name = 'timeline'
- task.taskMgr = TaskManager()
- task.sequence = makeSequenceFromTimeline(timelineList, task.taskMgr)
- task.sequenceDone = 0
- return task
- class TaskManager:
- notify = None
- def __init__(self):
- self.running = 0
- self.stepping = 0
- self.taskList = []
- self.currentTime, self.currentFrame = getTimeFrame()
- if (TaskManager.notify == None):
- TaskManager.notify = directNotify.newCategory("TaskManager")
- # TaskManager.notify.setDebug(1)
- def stepping(value):
- self.stepping = value
- def spawnMethodNamed(self, func, name):
- task = Task(func)
- return self.spawnTaskNamed(task, name)
- def spawnTaskNamed(self, task, name):
- TaskManager.notify.debug('spawning task named: ' + name)
- task.name = name
- task.setStartTimeFrame(self.currentTime, self.currentFrame)
- # search back from the end of the list until we find a
- # task with a lower priority, or we hit the start of the list
- index = len(self.taskList) - 1
- while (1):
- if (index < 0):
- break
- if (self.taskList[index].getPriority() <= task.getPriority()):
- break
- index = index - 1
- index = index + 1
- self.taskList.insert(index, task)
- return task
- def doMethodLater(self, delayTime, func, taskName):
- task = Task(func)
- seq = doLater(delayTime, task, taskName)
- return self.spawnTaskNamed(seq, 'doLater-' + taskName)
- def removeAllTasks(self):
- # Make a shallow copy so we do not modify the list in place
- taskListCopy = self.taskList[:]
- for task in taskListCopy:
- self.removeTask(task)
- def removeTask(self, task):
- TaskManager.notify.debug('removing task: ' + `task`)
- try:
- self.taskList.remove(task)
- except:
- pass
- if task.uponDeath:
- task.uponDeath(task)
- def removeTasksNamed(self, taskName):
- TaskManager.notify.debug('removing tasks named: ' + taskName)
- removedTasks = []
- # Find the tasks that match by name and make a list of them
- for task in self.taskList:
- if (task.name == taskName):
- removedTasks.append(task)
- # Now iterate through the tasks we need to remove and remove them
- for task in removedTasks:
- self.removeTask(task)
- # Return the number of tasks removed
- return len(removedTasks)
- def removeTasksMatching(self, taskPattern):
- """removeTasksMatching(self, string taskPattern)
- Removes tasks whose names match the pattern, which can include
- standard shell globbing characters like *, ?, and [].
- """
-
- TaskManager.notify.debug('removing tasks matching: ' + taskPattern)
- removedTasks = []
- # Find the tasks that match by name and make a list of them
- for task in self.taskList:
- if (fnmatch.fnmatchcase(task.name, taskPattern)):
- removedTasks.append(task)
- # Now iterate through the tasks we need to remove and remove them
- for task in removedTasks:
- self.removeTask(task)
- # Return the number of tasks removed
- return len(removedTasks)
- def step(self):
- TaskManager.notify.debug('step')
- self.currentTime, self.currentFrame = getTimeFrame()
- for task in self.taskList:
- task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
- # Run the task and check the return value
- # Record the dt
- startTime = globalClock.getTime()
- ret = task(task)
- endTime = globalClock.getTime()
- dt = endTime - startTime
- task.dt = dt
- if (ret == cont):
- continue
- elif (ret == done):
- self.removeTask(task)
- elif (ret == exit):
- self.removeTask(task)
- else:
- raise 'invalid task state'
- return len(self.taskList)
- def run(self):
- # Set the clock to have last frame's time in case we were
- # Paused at the prompt for a long time
- t = globalClock.getTime()
- globalClock.setTime(t)
- if self.stepping:
- self.step()
- else:
- self.running = 1
- while self.running:
- try:
- self.step()
- except KeyboardInterrupt:
- self.stop()
- except:
- raise
- def stop(self):
- # Set a flag so we will stop before beginning next frame
- self.running = 0
- def replaceMethod(self, oldMethod, newFunction):
- import new
- for task in self.taskList:
- method = task.__call__
- if (type(method) == types.MethodType):
- function = method.im_func
- else:
- function = method
- #print ('function: ' + `function` + '\n' +
- # 'method: ' + `method` + '\n' +
- # 'oldMethod: ' + `oldMethod` + '\n' +
- # 'newFunction: ' + `newFunction` + '\n')
- if (function == oldMethod):
- newMethod = new.instancemethod(newFunction,
- method.im_self,
- method.im_class)
- task.__call__ = newMethod
- # Found it retrun true
- return 1
- return 0
- def __repr__(self):
- taskNameWidth = 32
- dtWidth = 6
- priorityWidth = 10
- str = ('taskList'.ljust(taskNameWidth)
- + 'dt(ms)'.rjust(dtWidth)
- + 'priority'.rjust(priorityWidth)
- + '\n')
- str = str + '------------------------------------------------\n'
- for task in self.taskList:
- str = str + (task.name.ljust(taskNameWidth)
- + `int(round(task.dt * 1000))`.rjust(dtWidth)
- + `task.getPriority()`.rjust(priorityWidth)
- + '\n')
- return str
- """
- import Task
- from ShowBaseGlobal import * # to get taskMgr, and run()
- # sequence
- def seq1(state):
- print 'seq1'
- return Task.done
- def seq2(state):
- print 'seq2'
- return Task.done
- t = Task.sequence(Task.pause(1.0), Task.Task(seq1), Task.release(),
- Task.pause(3.0), Task.Task(seq2))
- taskMgr.spawnTaskNamed(t, 'sequence')
- run()
- # If you want it to loop, make a loop
- t = Task.loop(Task.pause(1.0), Task.Task(seq1), Task.release(),
- Task.pause(3.0), Task.Task(seq2))
- taskMgr.spawnTaskNamed(t, 'sequence')
- run()
- # timeline
- def keyframe1(state):
- print 'keyframe1'
- return Task.done
- def keyframe2(state):
- print 'keyframe2'
- return Task.done
- def keyframe3(state):
- print 'keyframe3'
- return Task.done
- testtl = Task.timeline(
- (0.5, Task.Task(keyframe1), 'key1'),
- (0.6, Task.Task(keyframe2), 'key2'),
- (0.7, Task.Task(keyframe3), 'key3')
- )
- t = taskMgr.spawnTaskNamed(testtl, 'timeline')
- run()
- # do later - returns a sequence
- def foo(state):
- print 'later...'
- return Task.done
- seq = Task.doLater(3.0, Task.Task(foo), 'fooLater')
- t = taskMgr.spawnTaskNamed(seq, 'doLater-fooLater')
- run()
- # tasks with state
- # Combined with uponDeath
- someValue = 1
- def func(state):
- if (state.someValue > 10):
- print 'true!'
- return Task.done
- else:
- state.someValue = state.someValue + 1
- return Task.cont
- def deathFunc(state):
- print 'Value at death: ', state.someValue
- task = Task.Task(func)
- # set task state here
- task.someValue = someValue
- # Use instance variable uponDeath to specify function
- # to perform on task removal
- # Default value of uponDeath is None
- task.uponDeath = deathFunc
- t = taskMgr.spawnTaskNamed(task, 'funcTask')
- run()
- """
|