Package VisionEgg :: Module FlowControl |
[frames] | no frames]

# Source Code for Module VisionEgg.FlowControl

       1  # The Vision Egg: FlowControl
       2  #
       3  # Copyright (C) 2001-2004 Andrew Straw.
       4  # Copyright (C) 2008 California Institute of Technology
       5  #
       6  # URL: <http://www.visionegg.org/>
       7  #
       8  # Distributed under the terms of the GNU Lesser General Public License
       9  # (LGPL). See LICENSE.TXT that came with this file.
      10
      11  """
      12  Flow control for the Vision Egg.
      13
      14  """
      15
      16  import logging
      17  import logging.handlers
      18
      19  import VisionEgg
      20  import VisionEgg.GL as gl # get all OpenGL stuff in one namespace
      21  import VisionEgg.ParameterTypes as ve_types
      22  import numpy.oldnumeric as Numeric, math, types
      23  import pygame
      24
      25  ####################################################################
      26  #
      27  #        Presentation
      28  #
      29  ####################################################################
      30
      31 -class Presentation(VisionEgg.ClassWithParameters):
      32      """Handles the timing and coordination of stimulus presentation.
      33
      34      This class is the key to the real-time operation of the Vision
      35      Egg. It contains the main 'go' loop, and maintains the association
      36      between 'controllers', instances of the Controller class, and the
      37      parameters they control.
      38
      39      During the main 'go' loop and at other specific times, the
      40      parameters are updated via function calls to the controllers.
      41
      42      Between entries into the 'go' loop, a Vision Egg application
      43      should call the method between_presentations as often as possible
      44      to ensure parameter values are kept up to date and any
      45      housekeeping done by controllers is done.
      46
      47      No OpenGL environment I know of can guarantee that a new frame is
      48      drawn and the double buffers swapped before the monitor's next
      49      vertical retrace sync pulse.  Still, although one can worry
      50      endlessly about this problem, it works.  In other words, on a fast
      51      computer with a fast graphics card running even a pre-emptive
      52      multi-tasking operating system (see below for specific
      53      information), a new frame is drawn before every monitor update. If
      54      this did become a problem, the go() method could be re-implemented
      55      in C, along with the functions it calls.  This would probably
      56      result in speed gains, but without skipping frames at 200 Hz, why
      57      bother?
      58
      59      Parameters
      60      ==========
      61      check_events                 -- allow input event checking during 'go' loop? (Boolean)
      62                                      Default: True
      63      collect_timing_info          -- log timing statistics during go loop? (Boolean)
      64                                      Default: True
      65      enter_go_loop                -- test used by run_forever() to enter go loop (Boolean)
      66                                      Default: False
      67      go_duration                  -- Tuple to specify 'go' loop duration. Either (value,units) or ('forever',) (Sequence of AnyOf(Real or String))
      68                                      Default: (5.0, 'seconds')
      69      handle_event_callbacks       -- List of tuples to handle events. (event_type,event_callback_func) (Sequence of Sequence2 of AnyOf(Integer or Callable))
      70                                      Default: (determined at runtime)
      71      override_t_abs_sec           -- Override t_abs. Set only when reconstructing experiments. (units: seconds) (Real)
      72                                      Default: (determined at runtime)
      73      quit                         -- quit the run_forever loop? (Boolean)
      74                                      Default: False
      75      trigger_armed                -- test trigger on go loop? (Boolean)
      76                                      Default: True
      77      trigger_go_if_armed          -- trigger go loop? (Boolean)
      78                                      Default: True
      79      viewports                    -- list of Viewport instances to draw. Order is important. (Sequence of Instance of <class 'VisionEgg.ClassWithParameters'>)
      80                                      Default: (determined at runtime)
      81      warn_longest_frame_threshold -- threshold to print frame skipped warning (units: factor of inter-frame-interval) (Real)
      82                                      Default: 2.0
      83      warn_mean_fps_threshold      -- threshold to print observered vs. expected frame rate warning (fraction units) (Real)
      84                                      Default: 0.01
      85      """
      86      parameters_and_defaults = {
      87          'viewports' : (None,
      88                         # XXX should really require VisionEgg.Core.Viewport
      89                         # but that would lead to circular import problem
      90                         ve_types.Sequence(ve_types.Instance(VisionEgg.ClassWithParameters)),
      91                         'list of Viewport instances to draw. Order is important.'),
      92          'collect_timing_info' : (True,
      93                                   ve_types.Boolean,
      94                                   'log timing statistics during go loop?'),
      95          'go_duration' : ((5.0,'seconds'),
      96                           ve_types.Sequence(ve_types.AnyOf(ve_types.Real,
      97                                                            ve_types.String)),
      98                           "Tuple to specify 'go' loop duration. Either (value,units) or ('forever',)"),
      99          'check_events' : (True, # May cause slight performance hit, but probably negligible
     100                            ve_types.Boolean,
     101                            "allow input event checking during 'go' loop?"),
     102          'handle_event_callbacks' : (None,
     103                                      ve_types.Sequence(ve_types.Sequence2(ve_types.AnyOf(ve_types.Integer,ve_types.Callable))),
     104                                      "List of tuples to handle events. (event_type,event_callback_func)"),
     105          'trigger_armed':(True,
     106                           ve_types.Boolean,
     107                           "test trigger on go loop?"),
     108          'trigger_go_if_armed':(True,
     109                                 ve_types.Boolean,
     110                                 "trigger go loop?"),
     111          'enter_go_loop':(False,
     112                           ve_types.Boolean,
     113                           "test used by run_forever() to enter go loop"),
     114          'quit':(False,
     115                  ve_types.Boolean,
     116                  "quit the run_forever loop?"),
     117          'warn_mean_fps_threshold':(0.01, # fraction (0.1 = 10%)
     118                                     ve_types.Real,
     119                                     "threshold to print observered vs. expected frame rate warning (fraction units)"),
     120          'warn_longest_frame_threshold': (2.0, # fraction (set to 2.0 for no false positives)
     121                                           ve_types.Real,
     122                                           "threshold to print frame skipped warning (units: factor of inter-frame-interval)"),
     123          'override_t_abs_sec':(None, # override t_abs (in seconds) -- set only when reconstructing experiments
     124                                ve_types.Real,
     125                                "Override t_abs. Set only when reconstructing experiments. (units: seconds)"),
     126          }
     127
     128      __slots__ = (
     129          'controllers',
     130          'num_frame_controllers',
     131          'frame_draw_times',
     132          'time_sec_absolute',
     133          'frames_absolute',
     134          'in_go_loop',
     135          'frames_dropped_in_last_go_loop',
     136          'last_go_loop_start_time_absolute_sec',
     137          'time_sec_since_go',
     138          'frames_since_go',
     139          )
     140
     141 -    def __init__(self,**kw):
     142          VisionEgg.ClassWithParameters.__init__(self,**kw)
     143
     144          if self.parameters.viewports is None:
     145              self.parameters.viewports = []
     146
     147          if self.parameters.handle_event_callbacks is None:
     148              self.parameters.handle_event_callbacks = []
     149
     150          self.controllers = []
     151          self.num_frame_controllers = 0 # reference counter for controllers that are called on frame by frame basis
     152
     153          # A list that optionally records when frames were drawn by go() method.
     154          self.frame_draw_times = []
     155
     156          self.time_sec_absolute=VisionEgg.time_func()
     157          self.frames_absolute=0
     158
     159          self.in_go_loop = False
     160          self.frames_dropped_in_last_go_loop = False
     161          self.last_go_loop_start_time_absolute_sec = None
     162
     163 -    def add_controller( self, class_with_parameters, parameter_name, controller ):
     164          """Add a controller"""
     165          # Check if type checking needed
     166          if type(class_with_parameters) != types.NoneType and type(parameter_name) != types.NoneType:
     167              # Check if return type of controller eval is same as parameter type
     168              if class_with_parameters.is_constant_parameter(parameter_name):
     169                  raise TypeError("Attempt to control constant parameter '%s' of class %s."%(parameter_name,class_with_parameters))
     170              require_type = class_with_parameters.get_specified_type(parameter_name)
     171              try:
     172                  ve_types.assert_type(controller.returns_type(),require_type)
     173              except TypeError:
     174                  raise TypeError("Attempting to control parameter '%s' of type %s with controller that returns type %s"%(
     175                      parameter_name,
     176                      require_type,
     177                      controller.returns_type()))
     178              if not hasattr(class_with_parameters.parameters,parameter_name):
     179                  raise AttributeError("%s has no instance '%s'"%parameter_name)
     180              self.controllers.append( (class_with_parameters.parameters,parameter_name, controller) )
     181          else: # At least one of class_with_parameters or parameter_name is None.
     182              # Make sure they both are None.
     183              if not (type(class_with_parameters) == types.NoneType and type(parameter_name) == types.NoneType):
     184                  raise ValueError("Neither or both of class_with_parameters and parameter_name must be None.")
     185              self.controllers.append( (None,None,controller) )
     186          if controller.temporal_variables & (FRAMES_SINCE_GO|FRAMES_ABSOLUTE):
     187              self.num_frame_controllers = self.num_frame_controllers + 1
     188
     189 -    def remove_controller( self, class_with_parameters, parameter_name, controller=None ):
     190          """Remove one (or more--see below) controller(s).
     191
     192          If controller is None, all controllers affecting the
     193          specified parameter are removed.
     194
     195          If class_with_parameters and paramter_name are None, the
     196          controller is removed completely
     197
     198          If class_with_parameters, paramter_name, and controller are
     199          all None, all controllers are removed.
     200
     201          """
     202
     203          if class_with_parameters is None and parameter_name is None:
     204              if not isinstance(controller,Controller) and controller != None:
     205
     206                  raise TypeError( "When deleting a controller, specify an "
     207                                   "instance of VisionEgg.FlowControl.Controller class!")
     208
     209              if controller == None: #Added by Tony, May30/2005
     210                  self.controllers = []
     211
     212              i = 0
     213              while i < len(self.controllers):
     214                  orig_parameters,orig_parameter_name,orig_controller = self.controllers[i]
     215                  if controller == orig_controller:
     216                      del self.controllers[i]
     217                  else:
     218                      i = i + 1
     219              return
     220          if controller is None:
     221              # The controller function is not specified:
     222              # Delete all controllers that control the parameter specified.
     223              if class_with_parameters is None or parameter_name is None:
     224                  raise ValueError("Must specify parameter from which controller should be removed.")
     225              i = 0
     226              while i < len(self.controllers):
     227                  orig_parameters,orig_parameter_name,orig_controller = self.controllers[i]
     228                  if (orig_parameters == class_with_parameters.parameters and
     229                      orig_parameter_name == parameter_name):
     230                      controller = self.controllers[i][2]
     231                      if controller.temporal_variables & (FRAMES_SINCE_GO|FRAMES_ABSOLUTE):
     232                          self.num_frame_controllers = self.num_frame_controllers - 1
     233                      del self.controllers[i]
     234                  else:
     235                      i = i + 1
     236          else: # controller is specified
     237              # Delete only that specific controller
     238              i = 0
     239              while i < len(self.controllers):
     240                  orig_parameters,orig_parameter_name,orig_controller = self.controllers[i]
     241                  if (orig_parameters == class_with_parameters.parameters and
     242                      orig_parameter_name == parameter_name and
     243                      orig_controller == controller):
     244                      if controller.temporal_variables & (FRAMES_SINCE_GO|FRAMES_ABSOLUTE):
     245                          self.num_frame_controllers = self.num_frame_controllers - 1
     246                  else:
     247                      i = i + 1
     248
     249 -    def __call_controllers(self,
     250                           go_started=None,
     251                           doing_transition=None):
     252          done_once = [] # list of ONCE contollers to switch status of
     253          for (parameters_instance, parameter_name, controller) in self.controllers:
     254              evaluate = 0
     255              if controller.eval_frequency & ONCE:
     256                  evaluate = 1
     257                  done_once.append(controller)
     258              elif doing_transition and (controller.eval_frequency & TRANSITIONS):
     259                  evaluate = 1
     260              elif controller.eval_frequency & EVERY_FRAME:
     261                  evaluate = 1
     262
     263              if evaluate:
     264                  if controller.temporal_variables & TIME_SEC_ABSOLUTE:
     265                      controller.time_sec_absolute = self.time_sec_absolute
     266                  if controller.temporal_variables & FRAMES_ABSOLUTE:
     267                      controller.frames_absolute = self.frames_absolute
     268
     269                  if go_started:
     270                      if not (controller.eval_frequency & NOT_DURING_GO):
     271                          if controller.temporal_variables & TIME_SEC_SINCE_GO:
     272                              controller.time_sec_since_go = self.time_sec_since_go
     273                          if controller.temporal_variables & FRAMES_SINCE_GO:
     274                              controller.frames_since_go = self.frames_since_go
     275                          result = controller.during_go_eval()
     276                          if parameter_name is not None:
     277                              setattr(parameters_instance, parameter_name, result)
     278                  else:
     279                      if not (controller.eval_frequency & NOT_BETWEEN_GO):
     280                          if controller.temporal_variables & TIME_SEC_SINCE_GO:
     281                              controller.time_sec_since_go = None
     282                          if controller.temporal_variables & FRAMES_SINCE_GO:
     283                              controller.frames_since_go = None
     284                          result = controller.between_go_eval()
     285                          if parameter_name is not None:
     286                              setattr(parameters_instance, parameter_name, result)
     287
     288          for controller in done_once:
     289              #Unset ONCE flag
     290              controller.eval_frequency = controller.eval_frequency & ~ONCE
     291              if isinstance(controller,EncapsulatedController):
     292                  controller.contained_controller.eval_frequency = controller.contained_controller.eval_frequency & ~ONCE
     293
     294 -    def is_in_go_loop(self):
     295          """Queries if the presentation is in a go loop.
     296
     297          This is useful to check the state of the Vision Egg
     298          application from a remote client over Pyro."""
     299          return self.in_go_loop
     300
     301 -    def were_frames_dropped_in_last_go_loop(self):
     302          return self.frames_dropped_in_last_go_loop
     303
     304 -    def get_last_go_loop_start_time_absolute_sec(self):
     305          return self.last_go_loop_start_time_absolute_sec
     306
     307 -    def go(self):
     308          """Main control loop during stimulus presentation.
     309
     310          This is the heart of realtime control in the Vision Egg, and
     311          contains the main loop during a stimulus presentation. This
     312          coordinates the timing of calling the controllers.
     313
     314          In the main loop, the current time (in absolute seconds,
     315          go-loop-start-relative seconds, and go-loop-start-relative
     316          frames) is computed, the appropriate controllers are called
     317          with this information, the screen is cleared, each viewport is
     318          drawn to the back buffer (while the video card continues
     319          painting the front buffer on the display), and the buffers are
     320          swapped.
     321
     322          """
     323          import VisionEgg.Core # here to prevent circular import
     324          self.in_go_loop = 1
     325
     326          swap_buffers = VisionEgg.Core.swap_buffers # shorthand
     327
     328          # Clear boolean indicator
     329          self.frames_dropped_in_last_go_loop = False
     330
     331          # Create shorthand notation, which speeds the main loop
     332          # slightly by not performing name lookup each time.
     333          p = self.parameters
     334
     335          if p.collect_timing_info:
     336              frame_timer = VisionEgg.Core.FrameTimer()
     337
     338          while (not p.trigger_armed) or (not p.trigger_go_if_armed):
     339              self.between_presentations()
     340
     341          # Go!
     342
     343          self.time_sec_absolute=VisionEgg.time_func()
     344
     345          if p.override_t_abs_sec is not None:
     346              raise NotImplementedError("Cannot override absolute time yet")
     347
     348          self.last_go_loop_start_time_absolute_sec = self.time_sec_absolute
     349          self.time_sec_since_go = 0.0
     350          self.frames_since_go = 0
     351
     352          synclync_connection = VisionEgg.config._SYNCLYNC_CONNECTION # create shorthand
     353          if synclync_connection:
     354              import synclync
     355              synclync_connection.next_control_packet.action_flags += (synclync.SL_CLEAR_VSYNC_COUNT +
     356                                                                       synclync.SL_CLEAR_NOTIFY_SWAPPED_COUNT +
     357                                                                       synclync.SL_CLEAR_FRAMESKIP_COUNT)
     358              synclync_hack_done_once = 0
     359
     360          # Tell transitional controllers a presentation is starting
     361          self.__call_controllers(
     362              go_started=1,
     363              doing_transition=1)
     364
     365          # Do the main loop
     366          start_time_absolute = self.time_sec_absolute
     367          if p.go_duration[0] == 'forever': # forever
     368              current_duration_value = 0
     369          elif p.go_duration[1] == 'seconds': # duration units
     370              current_duration_value = self.time_sec_since_go
     371          elif p.go_duration[1] == 'frames': # duration units
     372              current_duration_value = self.frames_since_go
     373          else:
     374              raise RuntimeError("Unknown duration unit '%s'"%p.go_duration[1])
     375
     376          while (current_duration_value < p.go_duration[0]):
     377              # Get list of screens
     378              screens = []
     379              for viewport in p.viewports:
     380                  s = viewport.parameters.screen
     381                  if s not in screens:
     382                      screens.append(s)
     383
     384              # Clear the screen(s)
     385              for screen in screens:
     386                  screen.clear()
     387
     388              # Update all the realtime parameters
     389              self.__call_controllers(
     390                  go_started=1,
     391                  doing_transition=0)
     392
     393              # Draw each viewport
     394              for viewport in p.viewports:
     395                  viewport.draw()
     396
     397              # Swap the buffers
     398              if synclync_connection:
     399                  if not synclync_hack_done_once:
     400                      synclync_connection.next_control_packet.action_flags += (synclync.SL_NOTIFY_SWAPPED_BUFFERS +
     401                                                                               synclync.SL_NOTIFY_IN_GO_LOOP)
     402                      synclync_connection.send_control_packet()
     403                      synclync_hack_done_once = 1
     404                  data_packet = synclync_connection.get_latest_data_packet()
     405              swap_buffers()
     406
     407              # Set the time variables for the next frame
     408              self.time_sec_absolute=VisionEgg.time_func()
     409              last_time_sec_since_go = self.time_sec_since_go
     410              self.time_sec_since_go = self.time_sec_absolute - start_time_absolute
     411              self.frames_absolute += 1
     412              self.frames_since_go += 1
     413
     414              if p.collect_timing_info:
     415                  frame_timer.tick()
     416
     417              # Make sure we use the right value to check if we're done
     418              if p.go_duration[0] == 'forever': # forever
     419                  pass # current_duration_value already set to 0
     420              elif p.go_duration[1] == 'seconds':
     421                  current_duration_value = self.time_sec_since_go
     422              elif p.go_duration[1] == 'frames':
     423                  current_duration_value = self.frames_since_go
     424              else:
     425                  raise RuntimeError("Unknown duration unit '%s'"%p.go_duration[1])
     426
     427              # Check events if requested
     428              if p.check_events:
     429                  for event in pygame.event.get():
     430                      for event_type, event_callback in p.handle_event_callbacks:
     431                          if event.type is event_type:
     432                              event_callback(event)
     433
     434          # Tell transitional controllers a presentation has ended
     435          self.__call_controllers(
     436              go_started=0,
     437              doing_transition=1)
     438
     439          # Tell SyncLync we're not in go loop anymore
     440          if synclync_connection:
     441              synclync_connection.send_control_packet() # nothing in action_flags -- finishes go loop
     442
     443          # Check to see if frame by frame control was desired
     444          # but OpenGL not syncing to vertical retrace
     445          try:
     446              mean_frame_time_sec = frame_timer.get_average_ifi_sec()
     447              calculated_fps = 1.0/mean_frame_time_sec
     448          except:
     449              # the above fails when no frames were drawn
     450              mean_frame_time_sec = 0.0
     451              calculated_fps = 0.0
     452
     453          if self.num_frame_controllers: # Frame by frame control desired
     454              impossibly_fast_frame_rate = 210.0
     455              if calculated_fps > impossibly_fast_frame_rate: # Let's assume no monitor can exceed impossibly_fast_frame_rate
     456                  logger = logging.getLogger('VisionEgg.FlowControl')
     457                  logger.error("Frame by frame control desired, but "
     458                               "average frame rate was %.2f frames per "
     459                               "second-- faster than any display device "
     460                               "(that I know of).  Set your drivers to "
     461                               "sync buffer swapping to vertical "
     462                               "retrace. (platform/driver "
     463                               "dependent)"%(calculated_fps))
     464          # Warn if > warn_mean_fps_threshold error in frame rate
     465          if abs(calculated_fps-VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ) / float(VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ) > self.parameters.warn_mean_fps_threshold:
     466              logger = logging.getLogger('VisionEgg.FlowControl')
     467              logger.warning("Calculated frames per second was %.3f, "
     468                             "while the VISIONEGG_MONITOR_REFRESH_HZ "
     469                             "variable is %s."%(calculated_fps,
     470                             VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ))
     471          frame_skip_fraction = self.parameters.warn_longest_frame_threshold
     472          inter_frame_inteval = 1.0/VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ
     473
     474          if p.collect_timing_info:
     475              longest_frame_draw_time_sec = frame_timer.get_longest_frame_duration_sec()
     476              if longest_frame_draw_time_sec is not None:
     477                  logger = logging.getLogger('VisionEgg.FlowControl')
     478                  if longest_frame_draw_time_sec >= (frame_skip_fraction*inter_frame_inteval):
     479                      self.frames_dropped_in_last_go_loop = True
     480                      logger.warning("One or more frames took %.1f msec, "
     481                                     "which is signficantly longer than the "
     482                                     "expected inter frame interval of %.1f "
     483                                     "msec for your frame rate (%.1f Hz)."%(
     484                                     longest_frame_draw_time_sec*1000.0,
     485                                     inter_frame_inteval*1000.0,
     486                                     VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ))
     487                  else:
     488                      logger.debug("Longest frame update was %.1f msec. "
     489                                   "Your expected inter frame interval is "
     490                                   "%f msec."%(longest_frame_draw_time_sec*1000.0,
     491                                   inter_frame_inteval*1000.0))
     492              frame_timer.log_histogram()
     493
     494          self.in_go_loop = 0
     495
     496 -    def export_movie_go(self, frames_per_sec=12.0, filename_suffix=".tif", filename_base="visionegg_movie", path="."):
     497          """Emulates method 'go' but saves a movie."""
     498          import VisionEgg.Core # here to prevent circular import
     499          import Image # Could import this at the beginning of the file, but it breaks sometimes!
     500          import os # Could also import this, but this is the only place its needed
     501
     502          # Create shorthand notation, which speeds the main loop
     503          # slightly by not performing name lookup each time.
     504          p = self.parameters
     505
     506          # Switch function VisionEgg.time_func
     507          self.time_sec_absolute=VisionEgg.time_func() # Set for real once
     508          true_time_func = VisionEgg.time_func
     509          def fake_time_func():
     510              return self.time_sec_absolute
     511          VisionEgg.time_func = fake_time_func
     512
     513          logger = logging.getLogger('VisionEgg.FlowControl')
     514
     515          # Go!
     516
     517          self.time_sec_absolute=VisionEgg.time_func()
     518          self.time_sec_since_go = 0.0
     519          self.frames_since_go = 0
     520
     521          # Tell transitional controllers a presentation is starting
     522          self.__call_controllers(
     523              go_started=1,
     524              doing_transition=1)
     525
     526          # Do the main loop
     527          image_no = 1
     528          if p.go_duration[0] == 'forever': # forever
     529              current_duration_value = 0
     530          elif p.go_duration[1] == 'seconds': # duration units
     531              current_duration_value = self.time_sec_since_go
     532          elif p.go_duration[1] == 'frames': # duration units
     533              current_duration_value = self.frames_since_go
     534          else:
     535              raise RuntimeError("Unknown duration unit '%s'"%p.go_duration[1])
     536          while (current_duration_value < p.go_duration[0]):
     537              # Get list of screens
     538              screens = []
     539              for viewport in p.viewports:
     540                  s = viewport.parameters.screen
     541                  if s not in screens:
     542                      screens.append(s)
     543
     544              # Clear the screen(s)
     545              for screen in screens:
     546                  screen.clear()
     547
     548              # Update all the realtime parameters
     549              self.__call_controllers(
     550                  go_started=1,
     551                  doing_transition=0)
     552
     553              # Draw each viewport
     554              for viewport in p.viewports:
     555                  viewport.draw()
     556
     557              # Swap the buffers
     558              VisionEgg.Core.swap_buffers()
     559
     560              # Now save the contents of the framebuffer
     561              fb_image = screen.get_framebuffer_as_image(buffer='front',format=gl.GL_RGB)
     562              filename = "%s%04d%s"%(filename_base,image_no,filename_suffix)
     563              savepath = os.path.join( path, filename )
     564              logger.info("Saving '%s'"%filename)
     565              fb_image.save( savepath )
     566              image_no = image_no + 1
     567
     568              # Set the time variables for the next frame
     569              self.time_sec_absolute += 1.0/frames_per_sec
     570              self.time_sec_since_go += 1.0/frames_per_sec
     571              self.frames_absolute += 1
     572              self.frames_since_go += 1
     573
     574              # Make sure we use the right value to check if we're done
     575              if p.go_duration[0] == 'forever':
     576                  pass # current_duration_value already set to 0
     577              elif p.go_duration[1] == 'seconds':
     578                  current_duration_value = self.time_sec_since_go
     579              elif p.go_duration[1] == 'frames':
     580                  current_duration_value = self.frames_since_go
     581              else:
     582                  raise RuntimeError("Unknown duration unit '%s'"%p.go_duration[1])
     583
     584              # Check events if requested
     585              if p.check_events:
     586                  for event in pygame.event.get():
     587                      for event_type, event_callback in p.handle_event_callbacks:
     588                          if event.type is event_type:
     589                              event_callback(event)
     590
     591          # Tell transitional controllers a presentation has ended
     592          self.__call_controllers(
     593              go_started=0,
     594              doing_transition=1)
     595
     596          if len(screens) > 1:
     597              logger.warning("Only saved movie from last screen.")
     598
     599          scp = screen.constant_parameters
     600          if scp.red_bits is not None:
     601              warn_about_movie_depth = 0
     602              if scp.red_bits > 8:
     603                  warn_about_movie_depth = 1
     604              elif scp.green_bits > 8:
     605                  warn_about_movie_depth = 1
     606              elif scp.blue_bits > 8:
     607                  warn_about_movie_depth = 1
     608              if warn_about_movie_depth:
     609                  logger.warning("Only saved 8 bit per pixel movie, even "
     610                                 "though your framebuffer supports more!")
     611          # Restore VisionEgg.time_func
     612          VisionEgg.time_func = true_time_func
     613
     614 -    def run_forever(self):
     615          """Main control loop between go loops."""
     616          p = self.parameters
     617          # enter with transitional contoller call
     618          self.__call_controllers(
     619              go_started=0,
     620              doing_transition=1)
     621          while not p.quit:
     622              self.between_presentations()
     623              if self.parameters.enter_go_loop:
     624                  self.parameters.enter_go_loop = False
     625                  self.go()
     626              if p.check_events:
     627                  for event in pygame.event.get():
     628                      for event_type, event_callback in p.handle_event_callbacks:
     629                          if event.type is event_type:
     630                              event_callback(event)
     631
     632 -    def between_presentations(self):
     633          """Maintain display while between stimulus presentations.
     634
     635          This function gets called as often as possible when in the
     636          'run_forever' loop except when execution has shifted to the
     637          'go' loop.
     638
     639          Other than the difference in the time variable passed to the
     640          controllers, this routine is very similar to the inside of the
     641          main loop in the go method.
     642          """
     643          import VisionEgg.Core # here to prevent circular import
     644
     645          self.time_sec_absolute=VisionEgg.time_func()
     646
     647          self.__call_controllers(
     648              go_started=0,
     649              doing_transition=0)
     650
     651          viewports = self.parameters.viewports
     652
     653          # Get list of screens
     654          screens = []
     655          for viewport in viewports:
     656              s = viewport.parameters.screen
     657              if s not in screens:
     658                  screens.append(s)
     659
     660          # Clear the screen(s)
     661          for screen in screens:
     662              screen.clear()
     663          # Draw each viewport, including each stimulus
     664          for viewport in viewports:
     665              viewport.draw()
     666          VisionEgg.Core.swap_buffers()
     667          self.frames_absolute += 1
     668
     669  ####################################################################
     670  #
     671  #        Controller
     672  #
     673  ####################################################################
     674
     675 -class Controller(object):
     676      """Control parameters.
     677
     678      This abstract base class defines the interface to any controller.
     679
     680      Methods:
     681
     682      returns_type() -- Get the type of the value returned by the eval functions
     683      during_go_eval() -- Evaluate controller during the main 'go' loop.
     684      between_go_eval() -- Evaluate controller between runs of the main 'go' loop.
     685
     686      The during_go_eval() and between_go_eval() methods are called to
     687      update a particular parameter such as the position of a stimulus
     688      on the screen.  These methods must return a value specified by the
     689      returns_type() method.  These methods are called at particular
     690      intervals as specified by eval_frequency and with temporal
     691      parameters specified by temporal_variables (see below for more
     692      details).  Also, see the documentation for the Presentation class.
     693
     694      Attributes:
     695
     696      return_type -- type of the value returned by the eval functions
     697      eval_frequency -- when eval functions called (see above)
     698      temporal_variables -- what time variables used (see above)
     699
     700      A Controller instance's attribute "eval_frequency" controls when a
     701      controller is evaluated. This variable is a bitwise "or" (the |
     702      operator) of the following flags:
     703
     704      EVERY_FRAME    -- every frame
     705      TRANSITIONS    -- on enter and exit from go loop
     706      ONCE           -- at the next chance possible (see below)
     707      NOT_DURING_GO  -- as above, but never during go loop (see below)
     708      NOT_BETWEEN_GO -- as above, but never between go loops (see below)
     709
     710      The ONCE flag is automatically unset after evaluation,
     711      hence its name. As an example, if eval_frequency is set to
     712      ONCE | TRANSITIONS, it will be evaluated
     713      before drawing the next frame and then only before and after the
     714      go loop.
     715
     716      NOT_DURING_GO and NOT_BETWEEN_GO modify other behavior. For
     717      example, to evaluate a controller on every frame during go loops
     718      but not between go loops:
     719
     720      eval_frequency = EVERY_FRAME | NOT_BETWEEN_GO
     721
     722      If none of the above flags is set, the value is:
     723
     724      NEVER          -- this controller is never called
     725
     726      A Controller instance's attribute "temporal_variables" controls
     727      what time variables are set for use. This variable is a bitwise
     728      "or" of the following flags:
     729
     730      TIME_SEC_ABSOLUTE -- seconds, continuously increasing
     731      TIME_SEC_SINCE_GO -- seconds, reset to 0.0 each go loop
     732      FRAMES_ABSOLUTE   -- frames, continuously increasing
     733      FRAMES_SINCE_GO   -- frames, reset to 0 each go loop
     734
     735      If none of these flags is set, the value is:
     736
     737      TIME_INDEPENDENT -- No temporal variables.
     738
     739      When the eval methods (during_go_eval and between_go_eval) are
     740      called, attributes are set depending on the temporal variables
     741      used:
     742
     743      temporal_variable   attribute set
     744      -----------------   -------------
     745      TIME_SEC_ABSOLUTE   self.time_sec_absolute
     746      TIME_SEC_SINCE_GO   self.time_sec_since_go
     747      FRAMES_ABSOLUTE     self.frames_absolute
     748      FRAMES_SINCE_GO     self.frames_since_go
     749
     750      Other information:
     751
     752      Instances of Controller are called by instances of the
     753      Presentation class.  during_go_eval() is called during a go()
     754      loop, and between_go_eval() is called by between_presentations()
     755      (during run_forever(), for example).  Before calling these
     756      methods, attributes of the controller are set accoring to
     757      \attribute{temporal_variables}.
     758
     759      """
     760      # temporal_variables flags:
     761      TIME_INDEPENDENT  = 0x00
     762      TIME_SEC_ABSOLUTE = 0x01
     763      TIME_SEC_SINCE_GO = 0x02
     764      FRAMES_ABSOLUTE   = 0x04
     765      FRAMES_SINCE_GO   = 0x08
     766
     767      # eval_frequency flags:
     768      NEVER          = 0x00
     769      EVERY_FRAME    = 0x01
     770      TRANSITIONS    = 0x02
     771      ONCE           = 0x04
     772      NOT_DURING_GO  = 0x08
     773      NOT_BETWEEN_GO = 0x10
     774
     775      flag_dictionary = {
     776          'TIME_INDEPENDENT'  : TIME_INDEPENDENT,
     777          'TIME_SEC_ABSOLUTE' : TIME_SEC_ABSOLUTE,
     778          'TIME_SEC_SINCE_GO' : TIME_SEC_SINCE_GO,
     779          'FRAMES_ABSOLUTE'   : FRAMES_ABSOLUTE,
     780          'FRAMES_SINCE_GO'   : FRAMES_SINCE_GO,
     781
     782          'NEVER'             : NEVER,
     783          'EVERY_FRAME'       : EVERY_FRAME,
     784          'TRANSITIONS'       : TRANSITIONS,
     785          'ONCE'              : ONCE,
     786          'NOT_DURING_GO'     : NOT_DURING_GO,
     787          'NOT_BETWEEN_GO'    : NOT_BETWEEN_GO}
     788
     789 -    def __init__(self,
     790                   eval_frequency = EVERY_FRAME,
     791                   temporal_variables = TIME_SEC_SINCE_GO,
     792                   return_type = None):
     793          """Create instance of Controller.
     794
     795          Arguments:
     796
     797          eval_frequency -- Int, bitwise "or" of flags
     798          temporal_variables -- Int, bitwise "or" of flags
     799          return_type -- Set to type() of the parameter under control
     800
     801          """
     802          if return_type is None: # Can be types.NoneType, but not None!
     803              raise ValueError("Must set argument 'return_type' in Controller.")
     804          if not ve_types.is_parameter_type_def(return_type):
     805              if type(return_type) == types.TypeType:
     806                  raise TypeError("Argument 'return_type' in Controller must be a VisionEgg parameter type definition.  Hint: use VisionEgg.ParameterTypes.get_type() to get the type of your value")
     807              raise TypeError("Argument 'return_type' in Controller must be a VisionEgg parameter type definition")
     808          self.return_type = return_type
     809
     810          self.temporal_variables = temporal_variables
     811          self.eval_frequency = eval_frequency
     812
     813 -    def evaluate_now(self):
     814          """Call this after updating the values of a controller if it's not evaluated EVERY_FRAME."""
     815          self.eval_frequency = self.eval_frequency | ONCE
     816
     817 -    def set_eval_frequency(self,eval_frequency):
     818          self.eval_frequency = eval_frequency
     819
     820 -    def returns_type(self):
     821          """Called by Presentation. Get the return type of this controller."""
     822          return self.return_type
     823
     824 -    def during_go_eval(self):
     825          """Called by Presentation. Evaluate during the main 'go' loop.
     826
     827          Override this method in subclasses."""
     828          raise RuntimeError("%s: Definition of during_go_eval() in abstract base class Contoller must be overriden."%(str(self),))
     829
     830 -    def between_go_eval(self):
     831          """Called by Presentation. Evaluate between runs of the main 'go' loop.
     832
     833          Override this method in subclasses."""
     834          raise RuntimeError("%s: Definition of between_go_eval() in abstract base class Controller must be overriden."%(str(self),))
     835
     836 -    def _test_self(self,go_started):
     837          """Test whether a controller works.
     838
     839          This method performs everything the Presentation go() or
     840          run_forever() methods do when calling controllers, except that
     841          the temporal variables are set to -1 and that the return value
     842          is not used to set parameters."""
     843
     844          if self.temporal_variables & TIME_SEC_ABSOLUTE:
     845              self.time_sec_absolute = -1.0
     846          if self.temporal_variables & FRAMES_ABSOLUTE:
     847              self.frames_absolute = -1
     848
     849          if go_started:
     850              if not (self.eval_frequency & NOT_DURING_GO):
     851                  if self.temporal_variables & TIME_SEC_SINCE_GO:
     852                      self.time_sec_since_go = -1.0
     853                  if self.temporal_variables & FRAMES_SINCE_GO:
     854                      self.frames_since_go = -1
     855                  return self.during_go_eval()
     856          else:
     857              if not (self.eval_frequency & NOT_BETWEEN_GO):
     858                  if self.temporal_variables & TIME_SEC_SINCE_GO:
     859                      self.time_sec_since_go = None
     860                  if self.temporal_variables & FRAMES_SINCE_GO:
     861                      self.frames_since_go = None
     862                  return self.between_go_eval()
     863
     864 -class ConstantController(Controller):
     865      """Set parameters to a constant value."""
     866 -    def __init__(self,
     867                   during_go_value = None,
     868                   between_go_value = None,
     869                   **kw
     870                   ):
     871          kw.setdefault('return_type',ve_types.get_type(during_go_value))
     872          kw.setdefault('eval_frequency',ONCE | TRANSITIONS)
     873          Controller.__init__(self,**kw)
     874          if self.return_type is not types.NoneType and during_go_value is None:
     875              raise ValueError("Must specify during_go_value")
     876          if between_go_value is None:
     877              between_go_value = during_go_value
     878          ve_types.assert_type(ve_types.get_type(during_go_value),self.return_type)
     879          ve_types.assert_type(ve_types.get_type(between_go_value),self.return_type)
     880          self.during_go_value = during_go_value
     881          self.between_go_value = between_go_value
     882
     883 -    def set_during_go_value(self,during_go_value):
     884          ve_types.assert_type(ve_types.get_type(during_go_value),self.return_type)
     885          self.during_go_value = during_go_value
     886  ##        if ve_types.get_type(during_go_value) is not self.return_type:
     887  ##            raise TypeError("during_go_value must be %s"%str(self.return_type))
     888  ##        else:
     889  ##            self.during_go_value = during_go_value
     890
     891 -    def get_during_go_value(self):
     892          return self.during_go_value
     893
     894 -    def set_between_go_value(self,between_go_value):
     895          ve_types.assert_type(ve_types.get_type(between_go_value),self.return_type)
     896          self.between_go_value = between_go_value
     897  ##        if ve_types.get_type(between_go_value) is not self.return_type:
     898  ##            raise TypeError("between_go_value must be %s"%str(self.return_type))
     899  ##        else:
     900  ##            self.between_go_value = between_go_value
     901
     902 -    def get_between_go_value(self):
     903          return self.between_go_value
     904
     905 -    def during_go_eval(self):
     906          """Called by Presentation. Overrides method in Controller base class."""
     907          return self.during_go_value
     908
     909 -    def between_go_eval(self):
     910          """Called by Presentation. Overrides method in Controller base class."""
     911          return self.between_go_value
     912
     913 -class EvalStringController(Controller):
     914      """Set parameters using dynamically interpreted Python string.
     915
     916      The string, when evaluated as Python code, becomes the value used.
     917      For example, the string "1.0" would set parameter values to 1.0.
     918
     919      To increase speed, the string is compiled to Python's bytecode
     920      format.
     921
     922      The string can make use of temporal variables, which are made
     923      available depending on the controller's temporal_variables
     924      attribute. Note that only the absolute temporal variables are
     925      available when the go loop is not running.
     926
     927      flag(s) present    variable  description
     928
     929      TIME_SEC_ABSOLUTE  t_abs     seconds, continuously increasing
     930      TIME_SEC_SINCE_GO  t         seconds, reset to 0.0 each go loop
     931      FRAMES_ABSOLUTE    f_abs     frames, continuously increasing
     932      FRAMES_SINCE_GO    f         frames, reset to 0 each go loop
     933
     934      """
     935 -    def __init__(self,
     936                   during_go_eval_string = None,
     937                   between_go_eval_string = None,
     938                   **kw
     939                   ):
     940          import VisionEgg.Core # here to prevent circular import
     941
     942          # Create a namespace for eval_strings to use
     943          self.eval_globals = {}
     944
     945          if during_go_eval_string is None:
     946              raise ValueError("'during_go_eval_string' is a required argument")
     947
     948          # Make Numeric and math modules available
     949          self.eval_globals['Numeric'] = Numeric
     950          self.eval_globals['math'] = math
     951          # Make Numeric and math modules available without module name
     952          for key in dir(Numeric):
     953              self.eval_globals[key] = getattr(Numeric,key)
     954          for key in dir(math):
     955              self.eval_globals[key] = getattr(math,key)
     956
     957          self.during_go_eval_code = compile(during_go_eval_string,'<string>','eval')
     958          self.during_go_eval_string = during_go_eval_string
     959          not_between_go = 0
     960          if between_go_eval_string is None:
     961              not_between_go = 1
     962          else:
     963              self.between_go_eval_code = compile(between_go_eval_string,'<string>','eval')
     964              self.between_go_eval_string = between_go_eval_string
     965
     966          # Check to make sure return_type is set
     967          set_return_type = 0
     968          if not kw.has_key('return_type'):
     969              set_return_type = 1
     970              kw['return_type'] = types.NoneType
     971
     972          # Call base class __init__
     973          Controller.__init__(self,**kw)
     974          if not_between_go:
     975              self.eval_frequency = self.eval_frequency|NOT_BETWEEN_GO
     976          if set_return_type:
     977              logger = logging.getLogger('VisionEgg.FlowControl')
     978              if not (self.eval_frequency & NOT_DURING_GO):
     979                  logger.debug( 'Executing "%s" to test for return type.'%(during_go_eval_string,))
     980                  self.return_type = ve_types.get_type(self._test_self(go_started=1))
     981              elif not (self.eval_frequency & NOT_BETWEEN_GO):
     982                  logger.debug('Executing "%s" to test for return type.'%(between_go_eval_string,))
     983                  self.return_type = ve_types.get_type(self._test_self(go_started=0))
     984
     985 -    def set_during_go_eval_string(self,during_go_eval_string):
     986          self.during_go_eval_code = compile(during_go_eval_string,'<string>','eval')
     987          self.during_go_eval_string = during_go_eval_string
     988
     989 -    def get_during_go_eval_string(self):
     990          return self.during_go_eval_string
     991
     992 -    def set_between_go_eval_string(self,between_go_eval_string):
     993          self.between_go_eval_code = compile(between_go_eval_string,'<string>','eval')
     994          self.between_go_eval_string = between_go_eval_string
     995          self.eval_frequency = self.eval_frequency & ~NOT_BETWEEN_GO
     996
     997 -    def get_between_go_eval_string(self):
     998          if hasattr(self,"between_go_eval_string"):
     999              return self.between_go_eval_string
    1000          else:
    1001              return None
    1002
    1003 -    def during_go_eval(self):
    1004          """Called by Presentation. Overrides method in Controller base class."""
    1005          eval_locals = {}
    1006          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1007              eval_locals['t_abs'] = self.time_sec_absolute
    1008          if self.temporal_variables & TIME_SEC_SINCE_GO:
    1009              eval_locals['t'] = self.time_sec_since_go
    1010          if self.temporal_variables & FRAMES_ABSOLUTE:
    1011              eval_locals['f_abs'] = self.frames_absolute
    1012          if self.temporal_variables & FRAMES_SINCE_GO:
    1013              eval_locals['f'] = self.frames_since_go
    1014          return eval(self.during_go_eval_code,self.eval_globals,eval_locals)
    1015
    1016 -    def between_go_eval(self):
    1017          """Called by Presentation. Overrides method in Controller base class."""
    1018          eval_locals = {}
    1019          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1020              eval_locals['t_abs'] = self.time_sec_absolute
    1021          if self.temporal_variables & FRAMES_ABSOLUTE:
    1022              eval_locals['f_abs'] = self.frames_absolute
    1023          return eval(self.between_go_eval_code,self.eval_globals,eval_locals)
    1024
    1025 -class ExecStringController(Controller):
    1026      """Set parameters using potentially complex Python string.
    1027
    1028      You can execute arbitrarily complex Python code with this
    1029      controller.  The return value must be contained within the
    1030      variable "x".  In other words, this string must assign the
    1031      variable x, so setting the string to "x=1.0" would set the
    1032      parameter under control to 1.0.
    1033
    1034      To increase speed, the string is compiled to Python's
    1035      bytecode format.
    1036
    1037      The string can make use of temporal variables, which are made
    1038      available depending on the controller's temporal_variables
    1039      attribute. Note that only the absolute temporal variables are
    1040      available when the go loop is not running.
    1041
    1042      flag(s) present    variable  description
    1043      -----------------  --------  ----------------------------------
    1044      TIME_SEC_ABSOLUTE  t_abs     seconds, continuously increasing
    1045      TIME_SEC_SINCE_GO  t         seconds, reset to 0.0 each go loop
    1046      FRAMES_ABSOLUTE    f_abs     frames, continuously increasing
    1047      FRAMES_SINCE_GO    f         frames, reset to 0 each go loop
    1048
    1049      """
    1050 -    def __init__(self,
    1051                   during_go_exec_string = None,
    1052                   between_go_exec_string = None,
    1053                   restricted_namespace = 1,
    1054                   **kw
    1055                   ):
    1056          import VisionEgg.Core # here to prevent circular import
    1057
    1058          # Create a namespace for eval_strings to use
    1059          self.eval_globals = {}
    1060
    1061          if during_go_exec_string is None:
    1062              raise ValueError, "'during_go_exec_string' is a required argument"
    1063
    1064          self.restricted_namespace = restricted_namespace
    1065
    1066          if self.restricted_namespace:
    1067              # Make Numeric and math modules available
    1068              self.eval_globals['Numeric'] = Numeric
    1069              self.eval_globals['math'] = math
    1070              # Make Numeric and math modules available without module name
    1071              for key in dir(Numeric):
    1072                  self.eval_globals[key] = getattr(Numeric,key)
    1073              for key in dir(math):
    1074                  self.eval_globals[key] = getattr(math,key)
    1075
    1076          self.during_go_exec_code = compile(during_go_exec_string,'<string>','exec')
    1077          self.during_go_exec_string = during_go_exec_string
    1078          not_between_go = 0
    1079          if between_go_exec_string is None:
    1080              not_between_go = 1
    1081          else:
    1082              self.between_go_exec_code = compile(between_go_exec_string,'<string>','exec')
    1083              self.between_go_exec_string = between_go_exec_string
    1084
    1085          # Check to make sure return_type is set
    1086          set_return_type = 0
    1087          if not kw.has_key('return_type'):
    1088              set_return_type = 1
    1089              kw['return_type'] = types.NoneType
    1090
    1091          # Call base class __init__
    1092          Controller.__init__(self,**kw)
    1093          if not_between_go:
    1094              self.eval_frequency = self.eval_frequency|NOT_BETWEEN_GO
    1095          if set_return_type:
    1096              logger = logging.getLogger('VisionEgg.FlowControl')
    1097              if not (self.eval_frequency & NOT_DURING_GO):
    1098                  logger.debug('Executing "%s" to test for return type.'%(during_go_exec_string,))
    1099                  self.return_type = ve_types.get_type(self._test_self(go_started=1))
    1100              elif not (self.eval_frequency & NOT_BETWEEN_GO):
    1101                  logger.debug('Executing "%s" to test for return type.'%(between_go_exec_string,))
    1102                  self.return_type = ve_types.get_type(self._test_self(go_started=0))
    1103
    1104 -    def set_during_go_exec_string(self,during_go_exec_string):
    1105          self.during_go_exec_code = compile(during_go_exec_string,'<string>','exec')
    1106          self.during_go_exec_string = during_go_exec_string
    1107
    1108 -    def get_during_go_exec_string(self):
    1109          return self.during_go_exec_string
    1110
    1111 -    def set_between_go_exec_string(self,between_go_exec_string):
    1112          self.between_go_exec_code = compile(between_go_exec_string,'<string>','exec')
    1113          self.between_go_exec_string = between_go_exec_string
    1114          self.eval_frequency = self.eval_frequency & ~NOT_BETWEEN_GO
    1115
    1116 -    def get_between_go_exec_string(self):
    1117          if hasattr(self,"between_go_exec_string"):
    1118              return self.between_go_exec_string
    1119          else:
    1120              return None
    1121
    1122 -    def during_go_eval(self):
    1123          """Called by Presentation. Overrides method in Controller base class."""
    1124          eval_locals = {}
    1125          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1126              eval_locals['t_abs'] = self.time_sec_absolute
    1127          if self.temporal_variables & TIME_SEC_SINCE_GO:
    1128              eval_locals['t'] = self.time_sec_since_go
    1129          if self.temporal_variables & FRAMES_ABSOLUTE:
    1130              eval_locals['f_abs'] = self.frames_absolute
    1131          if self.temporal_variables & FRAMES_SINCE_GO:
    1132              eval_locals['f'] = self.frames_since_go
    1133          if self.restricted_namespace:
    1134              exec self.during_go_exec_code in self.eval_globals,eval_locals
    1135              return eval_locals['x']
    1136          else:
    1137              setup_locals_str = "\n"
    1138              for local_variable_name in eval_locals.keys():
    1139                  setup_locals_str = setup_locals_str + local_variable_name + "=" + repr(eval_locals[local_variable_name]) + "\n"
    1140                  exec setup_locals_str
    1141              exec self.during_go_exec_code
    1142              return x
    1143
    1144 -    def between_go_eval(self):
    1145          """Called by Presentation. Overrides method in Controller base class."""
    1146          eval_locals = {}
    1147          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1148              eval_locals['t_abs'] = self.time_sec_absolute
    1149          if self.temporal_variables & FRAMES_ABSOLUTE:
    1150              eval_locals['f_abs'] = self.frames_absolute
    1151          if self.restricted_namespace:
    1152              exec self.between_go_exec_code in self.eval_globals,eval_locals
    1153              return eval_locals['x']
    1154          else:
    1155              setup_locals_str = "\n"
    1156              for local_variable_name in eval_locals.keys():
    1157                  setup_locals_str = setup_locals_str + local_variable_name + "=" + repr(eval_locals[local_variable_name]) + "\n"
    1158                  exec setup_locals_str
    1159              exec self.between_go_exec_code
    1160              return x # x should be assigned by the exec string
    1161
    1162 -class FunctionController(Controller):
    1163      """Set parameters using a Python function.
    1164
    1165      This is a very commonly used subclass of Controller, because it is
    1166      very intuitive and requires a minimum of code to set up.  Many of
    1167      the Vision Egg demo programs create instances of
    1168      FunctionController.
    1169
    1170      A number of parameters are passed to the function depending on the
    1171      value of temporal_variables:
    1172
    1173      The function can make use of temporal variables, which are made
    1174      available by passingkeyword argument(s) depending on the
    1175      controller's temporal_variables attribute. Note that only the
    1176      absolute temporal variables are available when the go loop is not
    1177      running.
    1178
    1179      flag(s) present    argument  description
    1180      -----------------  --------  ----------------------------------
    1181      TIME_SEC_ABSOLUTE  t_abs     seconds, continuously increasing
    1182      TIME_SEC_SINCE_GO  t         seconds, reset to 0.0 each go loop
    1183      FRAMES_ABSOLUTE    f_abs     frames, continuously increasing
    1184      FRAMES_SINCE_GO    f         frames, reset to 0 each go loop
    1185
    1186      """
    1187 -    def __init__(self,
    1188                   during_go_func = None,
    1189                   between_go_func = None,
    1190                   **kw
    1191                   ):
    1192          """Create an instance of FunctionController.
    1193
    1194          Arguments:
    1195
    1196          during_go_func -- function evaluted during go loop
    1197          between_go_func -- function evaluted not during go loop
    1198
    1199          """
    1200          import VisionEgg.Core # here to prevent circular import
    1201
    1202          if during_go_func is None:
    1203              raise ValueError("Must specify during_go_func")
    1204
    1205          # Set default value if not set
    1206          kw.setdefault('temporal_variables',TIME_SEC_SINCE_GO) # default value
    1207
    1208          # Check to make sure return_type is set
    1209          if not kw.has_key('return_type'):
    1210              logger = logging.getLogger('VisionEgg.FlowControl')
    1211              logger.debug('Evaluating %s to test for return type.'%(str(during_go_func),))
    1212              call_args = {}
    1213              if kw['temporal_variables'] & TIME_SEC_ABSOLUTE:
    1214                  call_args['t_abs'] = VisionEgg.time_func()
    1215              if kw['temporal_variables'] & TIME_SEC_SINCE_GO:
    1216                  call_args['t'] = 0.0
    1217              if kw['temporal_variables'] & FRAMES_ABSOLUTE:
    1218                  call_args['f_abs'] = 0
    1219              if kw['temporal_variables'] & FRAMES_SINCE_GO:
    1220                  call_args['f'] = 0
    1221              # Call the function with time variables
    1222              kw['return_type'] = ve_types.get_type(during_go_func(**call_args))
    1223          Controller.__init__(self,**kw)
    1224          self.during_go_func = during_go_func
    1225          self.between_go_func = between_go_func
    1226          if between_go_func is None:
    1227              self.eval_frequency = self.eval_frequency|NOT_BETWEEN_GO
    1228
    1229 -    def during_go_eval(self):
    1230          """Called by Presentation. Overrides method in Controller base class."""
    1231          call_args = {}
    1232          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1233              call_args['t_abs'] = self.time_sec_absolute
    1234          if self.temporal_variables & TIME_SEC_SINCE_GO:
    1235              call_args['t'] = self.time_sec_since_go
    1236          if self.temporal_variables & FRAMES_ABSOLUTE:
    1237              call_args['f_abs'] = self.frames_absolute
    1238          if self.temporal_variables & FRAMES_SINCE_GO:
    1239              call_args['f'] = self.frames_since_go
    1240          return self.during_go_func(**call_args)
    1241
    1242 -    def between_go_eval(self):
    1243          """Called by Presentation. Overrides method in Controller base class."""
    1244          call_args = {}
    1245          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1246              call_args['t_abs'] = self.time_sec_absolute
    1247          if self.temporal_variables & FRAMES_ABSOLUTE:
    1248              call_args['f_abs'] = self.frames_absolute
    1249          return self.between_go_func(**call_args)
    1250
    1251 -class EncapsulatedController(Controller):
    1252      """Set parameters by encapsulating another Controller.
    1253
    1254      Allows a new instance of Controller to control the same parameter
    1255      as an old instance.
    1256
    1257      You probably won't ever have to use this class directly.  Both the
    1258      VisionEgg.TCPController.TCPController and
    1259      VisionEgg.PyroHelpers.PyroEncapsulatedController classes subclass
    1260      this class.
    1261
    1262      """
    1263 -    def __init__(self,initial_controller):
    1264          # Initialize base class without raising error for no return_type
    1265          Controller.__init__(self,**{'return_type':types.NoneType})
    1266          self.contained_controller = initial_controller
    1267          self.__sync_mimic()
    1268
    1269 -    def __sync_mimic(self):
    1270          self.return_type = self.contained_controller.return_type
    1271          self.temporal_variables = self.contained_controller.temporal_variables
    1272          self.eval_frequency = self.contained_controller.eval_frequency
    1273
    1274 -    def set_new_controller(self,new_controller):
    1275          """Call this to encapsulate a (new) controller."""
    1276          self.contained_controller = new_controller
    1277          self.__sync_mimic()
    1278
    1279 -    def during_go_eval(self):
    1280          """Called by Presentation. Overrides method in Controller base class."""
    1281          import VisionEgg.Core # here to prevent circular import
    1282          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1283              self.contained_controller.time_sec_absolute = self.time_sec_absolute
    1284          if self.temporal_variables & TIME_SEC_SINCE_GO:
    1285              self.contained_controller.time_sec_since_go = self.time_sec_since_go
    1286          if self.temporal_variables & FRAMES_ABSOLUTE:
    1287              self.contained_controller.frames_absolute = self.frames_absolute
    1288          if self.temporal_variables & FRAMES_SINCE_GO:
    1289              self.contained_controller.frames_since_go = self.frames_since_go
    1290          return self.contained_controller.during_go_eval()
    1291
    1292 -    def between_go_eval(self):
    1293          """Called by Presentation. Overrides method in Controller base class."""
    1294          import VisionEgg.Core # here to prevent circular import
    1295          import VisionEgg.FlowControl
    1296          if self.temporal_variables & TIME_SEC_ABSOLUTE:
    1297              self.contained_controller.time_sec_absolute = self.time_sec_absolute
    1298          if self.temporal_variables & FRAMES_ABSOLUTE:
    1299              self.contained_controller.frames_absolute = self.frames_absolute
    1300          return self.contained_controller.between_go_eval()
    1301
    1302  # Constants (These are a copy of the static class variables from the
    1303  # Controller class into the module-level namespace. This is done for
    1304  # convenience.)
    1305  #     temporal_variables flags:
    1306  TIME_INDEPENDENT  = Controller.TIME_INDEPENDENT
    1307  TIME_SEC_ABSOLUTE = Controller.TIME_SEC_ABSOLUTE
    1308  TIME_SEC_SINCE_GO = Controller.TIME_SEC_SINCE_GO
    1309  FRAMES_ABSOLUTE   = Controller.FRAMES_ABSOLUTE
    1310  FRAMES_SINCE_GO   = Controller.FRAMES_SINCE_GO
    1311  #     eval_frequency flags:
    1312  NEVER          = Controller.NEVER
    1313  EVERY_FRAME    = Controller.EVERY_FRAME
    1314  TRANSITIONS    = Controller.TRANSITIONS
    1315  ONCE           = Controller.ONCE
    1316  NOT_DURING_GO  = Controller.NOT_DURING_GO
    1317  NOT_BETWEEN_GO = Controller.NOT_BETWEEN_GO
    1318