Using Generators as Tasks

Last week, I discovered the yield statement and I must say it fascinates me.
So, when I tried to use it within Panda tasks, it realised it wasn’t yet supported. I decided to write a bit of code that would make the usage of generators possible.
I managed to write this little piece of code. It’s primitive and doesn’t use all of yield’s power, but I’ll try to improve it!

Right now, it simply takes a generator as argument and then acts like a function.

class GeneratorTask:
    def __init__(self,func):#where func returns a generator
        self.generator=func
        self.__isfirstcall__=True
        self._task=None
    def __call__(self,task,*args,**kwargs):
        #grab the task instance
        self._task=task
        #check is it's the first call
        if self.__isfirstcall__:
            self.generator=self.generator(self._task)
            self.__isfirstcall__ = False
        #do the thingy
        try:
            yielded=self.generator.next()
        except StopIteration:
            return Task.done
        #the generator ought to yield Task attributes
        #if None is yielded the Panda Reference specifies
        #it will end the task
        return yielded

The following code:

def foo(task):
    x=0
    while x<10:
        x+=1
        print "FOO",x
        yield Task.cont
    yield Task.done
taskMgr.add(GeneratorTask(foo),"test")

Outputs:

The following code:

def bar(task):
    x=0
    while x<10:
        x+=1
        print "BAR",task.time,task.frame,task.id,task.name
        yield Task.cont
    yield Task.done
taskMgr.add(GeneratorTask(bar),"test")

Outputs:

Here is an example of how I used it:

class Camera:
    """Creates a camera"""
    def __init__(self,center):
        self.center=center
        self.x=0    #xposition
        self.y=0    #ypos
        self.z=0
        self.h=0    #xrotation
        self.p=0    #yrot
        self.r=0    #zrot        
        self.current_task=None
        self.tasks=[self.camTask1,self.camTask2]
        self.next_yield_task()
        taskMgr.add(self.SetPosNHpr,"SetCamPos",priority=0)
    def setZeros(self):
        self.target=None
        self.x=self.y=self.z=self.h=self.p=self.r=0        
    def next_yield_task(self,task=None):
        self.current_task=GeneratorTask(random.choice(self.tasks))
        taskMgr.add(self.current_task,"task",uponDeath=self.next_yield_task)
    def camTask1(self,task):
        """A simple zoom-out"""
        self.setZeros()#sets all to 0
        z=20
        while z<100:
            z+=1
            self.z=z
            yield Task.cont
        yield Task.done
    def camTask2(self,task):
        """A simple rotation"""
        a=0
        self.setZeros()
        self.z=20
        self.p=340
        while a<360:
            a+=1
            self.h=a
            yield Task.cont
        yield Task.done
    def SetPosNHpr(self,task):
        """simply sets the position and the rotation according to self attributes"""
        base.cam.setPos(self.x,self.y,self.z)
        if type(self.target)==NodePath:base.cam.lookAt(self.target)
        else: base.cam.setHpr(self.h,self.p,self.r)
        return Task.cont    

The only actual problem in this code is that it goes at an incredible speed :smiley:, so don’t try to run it unless you want a psycho vision, it just shows how it can be used.

So basically that’s it. Some things should be added in the future, like use of arguments, sending values, etc…
At the moment I can’t really improve this script, I’m quite busy :confused: , you can take the initiative !

Any suggestion is welcome!

I read a bit of python doc, and finally understood how arguments were passed whatever nature.
So I changed a few things, you can now add extraArgs to your task, appendTask or whatever you wish!


class GeneratorTask:
    def __init__(self,func):#where func returns a generator
        self.generator=func
        self.__isfirstcall__=True
    def __call__(self,*args,**kwargs):
        #check is it's the first call
        if self.__isfirstcall__:
            self.generator=self.generator(*args,**kwargs)
            self.__isfirstcall__ = False
        #do the thingy
        try:
            yielded=self.generator.next()
        except StopIteration:
            return Task.done
        #the generator ought to yield Task attributes
        #if None is yielded the Panda Reference specifies
        #it will end the task
        return yielded