Open topic with navigation
Task basics
With viztask, you can pause the execution of a function while waiting
for some condition, like a few seconds to pass, without blocking the Vizard
main loop or spawning an operating system thread.
viztask versus viz.director
The viztask module and viz.director functions have similar functionality;
however, there are important differences. Calling
viz.director
actually creates a new thread, while scheduling a task does not. This
means that calling a long blocking operation in a task will block the
Vizard update loop as well and the frame rate will go to zero. However,
in most cases, tasks should be used because they are much more CPU/memory
efficient and are non-preemptive so synchronization locks are not necessary.
Yield statements
Any function that is passed to the scheduler must have a yield statement
within it. This
line tells the program to stop until a condition has been met. There are
a number of predefined conditions that tasks can wait for. Check out the
yield conditions in the Tasks command
table for a list. In the following example, <viztask>.addAction adds the fading
action to the ball and waits for that action to be completed before moving
to the next line in the scheduled function.
import viztask
ball = viz.add('soccerball.ive')
def fadeAndAppear():
yield
viztask.addAction( ball, vizact.fadeTo(0,time=2) )
print('done fading' )
yield
viztask.addAction( ball, vizact.fadeTo(1,time=2) )
print('done appearing')
viztask.schedule( fadeAndAppear() )
Running a task repeatedly
To run a task repeatedly, just use standard loop statement (while or
for). If you want to stop a looping task, kill it with <viztask:Task>.kill.
import viztask
ball = viz.add('soccerball.ive')
def bigAndSmall():
while
True:
yield viztask.waitTime( 1
)
ball.setScale( 2,2,2 )
yield viztask.waitTime( 1
)
ball.setScale( 1,1,1 )
myTask = viztask.schedule( bigAndSmall() )
vizact.onkeydown( ' ', myTask.kill )
Using event data in a task
Many viztask yield
statements return a viz.Data object with
data about the event or condition that occurred (e.g. <viztask>.waitMouseDown
returns information about the mouse button that was pressed and the time
it was pressed.) The <viztask>.waitEvent
command will wait for any standard or custom event and return data about
that event. Check out the Event reference
for standard events or the Custom events
section for more on making your own kind of event.
Here's an example with <viztask>.waitKeyDown that waits for
a keypress and then uses the returned key to proceed.
import viztask
text = viz.addText(' ', viz.SCREEN)
text.setPosition(.5,.5)
def showLetter():
while True:
dataObject = yield viztask.waitKeyDown(keys=None)
#Change
the text with the returned data.
text.message( dataObject.key )
yield
viztask.addAction( text, vizact.fadeTo(0,begin=1,time=1) )
viztask.schedule( showLetter() )
Custom conditions
If you want to create a custom condition for your yield statement, write
a viztask.Condition class. The update function
within this class will generally be called once per frame. When it returns
True, the scheduler will continue. In the
following example, the up-arrow key moves the ball upwards until it reaches
one meter, and then repositions it at zero. Here, waitHigh
is the custom condition. It returns True
when the object reaches the specified height. The scheduled function then
proceeds.
import viztask
ball = viz.add( 'white_ball.wrl' )
vizact.onkeydown( viz.KEY_UP, ball.setPosition, [0,.2,0], viz.REL_PARENT
)
def limitHeight():
myCondition = waitHigh(1, ball)
while
True:
yield myCondition
print('too high')
ball.setPosition([0,0,0])
class waitHigh( viztask.Condition ):
def __init__( self, height_limit,
node ):
self.__limit = height_limit
self.__node = node
def reset( self ):
#Reset
from the current height of the ball.
self.__currentLimit = self.__node.getPosition()[1] + self.__limit
def update( self ):
#This
function will run about every frame, returning True or False
#to the
yield statement.
return self.__currentLimit <= self.__node.getPosition()[1]
viztask.schedule( limitHeight() )
Using sub tasks
You can embed sub tasks within a scheduled task. For example, doTrial is scheduled within executeExperiment.
The parent task waits for the sub task to finish before it continues.
def executeExperiment():
for trialNumber in
range(3):
yield
viztask.waitKeyDown(' ') #hold here
until spacebar pressed
yield
doTrial() #wait for doTrial to finish
print('Trial Done: ', trialNumber)
print('done
with experiment')
def doTrial():
viz.clearcolor(0, 1, 0)
yield viztask.waitFrame(30) #wait for 30 frames
viz.clearcolor(1, 0, 0)
yield viztask.waitTime(2) #wait for 2 seconds
viz.clearcolor(0, 0, 0)
viztask.schedule(executeExperiment())
Returning values
You can use the return statement within a task to return a value to the parent task:
def waitForIt():
d = yield viztask.waitKeyDown(None)
return f'Key: {d.key}'
def main():
while True:
it = yield waitForIt()
print(it)
viztask.schedule(main())
Signalling within a task
Signal objects are useful for communicating between tasks. Tasks
can either wait for, or send, signals. In the following example, one ball
waits for the signal to change color while the other ball sends out the
signal between actions.
import viztask
ball1 = viz.add('white_ball.wrl')
ball2 = viz.add('white_ball.wrl')
#Create a signal.
changeColorSignal = viztask.Signal()
def colorBall():
#This task will wait for the signalVi.
while True:
yield
changeColorSignal.wait()
ball2.color( viz.RED )
yield
changeColorSignal.wait()
ball2.color( viz.BLUE )
def moveBall():
#This task will repeated send the
signal.
while True:
yield
viztask.addAction( ball1, vizact.moveTo([0,0,0],speed=1) )
changeColorSignal.send()
yield
viztask.addAction( ball1, vizact.moveTo([-1,0,0],speed=1) )
changeColorSignal.send()
viztask.schedule( colorBall() )
viztask.schedule( moveBall() )
See also
In this section:
Tasks command table
Other sections:
Tutorial:
Creating a task
Tutorial:Action
Objects
Action
basics
Director
basics
Event Basics