Generator Functions in ‘base.accept’
The ‘next’ and ‘send’ methods of generator objects, or the ‘next’ function with a generator object as an extraArg in Python 2.6, can be a target for the ‘accept’ method of a DirectObject. Generators provide a nice middle ground between a class instance and a function; they maintain a state but do not require a class definition; good in such cases as a lambda is just barely too limited. Generators have a ‘send’ method which allows arguments to passed be to the generator, which are returned from the ‘yield’ statement, for additional functionality such as ‘restart’ or ‘continue’.
''' This program demonstrates the 'next' or 'send' methods of generators as parameters to the 'accept' method. 'P' and 'Y' start a card at the top of the screen which spirals down. The 'Y' spiral continues if the key is let up; the 'P' starts over; due to the presence of parameters through the 'send' method on return from the 'yield', combined with a flag in the parameter. ''' import direct.directbase.DirectStart from panda3d.core import * from math import * import random as ran def downward_spiral( continueOnNone ): cm= CardMaker( 'cm' ) while 1: print 'new' c= cm.generate( ) obj= render.attachNewNode( c ) # add object to scene z, theta= 0, ran.uniform( 0, 2* pi ) z_vel= ran.uniform( 0.01, 0.02 ) theta_vel= ran.uniform( 0.1, 0.2 ) for _ in xrange( 200 ): # just a downward spiral x, y= cos( theta ), sin( theta ) obj.setPos( x, y, z ) instruction= yield # function exits and re-enters here if not continueOnNone and instruction is None: break z= z- z_vel theta+= theta_vel obj.removeNode( ) # then start over spiral1= downward_spiral( False ) spiral2= downward_spiral( True ) # parameter controls 'send' behavior base.accept( 'p', spiral1.next ) # form 1 (restart spiral) base.accept( 'p-repeat', spiral1.send, extraArgs= [ 'continue' ] ) # form 1 base.accept( 'y-repeat', next, extraArgs= [ spiral2 ] ) # form 2 base.cam.setPos( 0, -5, 0 ) run( )
Remember that if the generator exits, it will throw an exception. We might want to get the ‘accept’ method called when the generator starts, to enable it to remove it when it ends; or enter an empty yield loop. Since a generator is created before its creator has a reference to it, we can’t pass itself in as a parameter. However, we could establish an initialization protocol to pass it in via ‘send’, or as a post-facto attribute of an auxiliary parameter. Or we could wrap the function in a second generator that cancels the accept once the first throws an exception.