1
2
3
4
5
6
7
8
9
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
21 import VisionEgg.ParameterTypes as ve_types
22 import numpy.oldnumeric as Numeric, math, types
23 import pygame
24
25
26
27
28
29
30
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
89
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,
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,
118 ve_types.Real,
119 "threshold to print observered vs. expected frame rate warning (fraction units)"),
120 'warn_longest_frame_threshold': (2.0,
121 ve_types.Real,
122 "threshold to print frame skipped warning (units: factor of inter-frame-interval)"),
123 'override_t_abs_sec':(None,
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
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
152
153
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
166 if type(class_with_parameters) != types.NoneType and type(parameter_name) != types.NoneType:
167
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:
182
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:
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
222
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:
237
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
252 done_once = []
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
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
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
302 return self.frames_dropped_in_last_go_loop
303
305 return self.last_go_loop_start_time_absolute_sec
306
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
324 self.in_go_loop = 1
325
326 swap_buffers = VisionEgg.Core.swap_buffers
327
328
329 self.frames_dropped_in_last_go_loop = False
330
331
332
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
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
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
361 self.__call_controllers(
362 go_started=1,
363 doing_transition=1)
364
365
366 start_time_absolute = self.time_sec_absolute
367 if p.go_duration[0] == 'forever':
368 current_duration_value = 0
369 elif p.go_duration[1] == 'seconds':
370 current_duration_value = self.time_sec_since_go
371 elif p.go_duration[1] == 'frames':
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
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
385 for screen in screens:
386 screen.clear()
387
388
389 self.__call_controllers(
390 go_started=1,
391 doing_transition=0)
392
393
394 for viewport in p.viewports:
395 viewport.draw()
396
397
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
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
418 if p.go_duration[0] == 'forever':
419 pass
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
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
435 self.__call_controllers(
436 go_started=0,
437 doing_transition=1)
438
439
440 if synclync_connection:
441 synclync_connection.send_control_packet()
442
443
444
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
450 mean_frame_time_sec = 0.0
451 calculated_fps = 0.0
452
453 if self.num_frame_controllers:
454 impossibly_fast_frame_rate = 210.0
455 if calculated_fps > 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
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="."):
511 VisionEgg.time_func = fake_time_func
512
513 logger = logging.getLogger('VisionEgg.FlowControl')
514
515
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
522 self.__call_controllers(
523 go_started=1,
524 doing_transition=1)
525
526
527 image_no = 1
528 if p.go_duration[0] == 'forever':
529 current_duration_value = 0
530 elif p.go_duration[1] == 'seconds':
531 current_duration_value = self.time_sec_since_go
532 elif p.go_duration[1] == 'frames':
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
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
545 for screen in screens:
546 screen.clear()
547
548
549 self.__call_controllers(
550 go_started=1,
551 doing_transition=0)
552
553
554 for viewport in p.viewports:
555 viewport.draw()
556
557
558 VisionEgg.Core.swap_buffers()
559
560
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
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
575 if p.go_duration[0] == 'forever':
576 pass
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
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
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
612 VisionEgg.time_func = true_time_func
613
615 """Main control loop between go loops."""
616 p = self.parameters
617
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
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
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
654 screens = []
655 for viewport in viewports:
656 s = viewport.parameters.screen
657 if s not in screens:
658 screens.append(s)
659
660
661 for screen in screens:
662 screen.clear()
663
664 for viewport in viewports:
665 viewport.draw()
666 VisionEgg.Core.swap_buffers()
667 self.frames_absolute += 1
668
669
670
671
672
673
674
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
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
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
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:
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
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
818 self.eval_frequency = eval_frequency
819
821 """Called by Presentation. Get the return type of this controller."""
822 return self.return_type
823
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
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
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
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
884 ve_types.assert_type(ve_types.get_type(during_go_value),self.return_type)
885 self.during_go_value = during_go_value
886
887
888
889
890
892 return self.during_go_value
893
895 ve_types.assert_type(ve_types.get_type(between_go_value),self.return_type)
896 self.between_go_value = between_go_value
897
898
899
900
901
903 return self.between_go_value
904
906 """Called by Presentation. Overrides method in Controller base class."""
907 return self.during_go_value
908
910 """Called by Presentation. Overrides method in Controller base class."""
911 return self.between_go_value
912
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
941
942
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
949 self.eval_globals['Numeric'] = Numeric
950 self.eval_globals['math'] = math
951
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
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
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
986 self.during_go_eval_code = compile(during_go_eval_string,'<string>','eval')
987 self.during_go_eval_string = during_go_eval_string
988
990 return self.during_go_eval_string
991
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
998 if hasattr(self,"between_go_eval_string"):
999 return self.between_go_eval_string
1000 else:
1001 return None
1002
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
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
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
1057
1058
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
1068 self.eval_globals['Numeric'] = Numeric
1069 self.eval_globals['math'] = math
1070
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
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
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
1105 self.during_go_exec_code = compile(during_go_exec_string,'<string>','exec')
1106 self.during_go_exec_string = during_go_exec_string
1107
1109 return self.during_go_exec_string
1110
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
1117 if hasattr(self,"between_go_exec_string"):
1118 return self.between_go_exec_string
1119 else:
1120 return None
1121
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
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
1161
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
1201
1202 if during_go_func is None:
1203 raise ValueError("Must specify during_go_func")
1204
1205
1206 kw.setdefault('temporal_variables',TIME_SEC_SINCE_GO)
1207
1208
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
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
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
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
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
1265 Controller.__init__(self,**{'return_type':types.NoneType})
1266 self.contained_controller = initial_controller
1267 self.__sync_mimic()
1268
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
1275 """Call this to encapsulate a (new) controller."""
1276 self.contained_controller = new_controller
1277 self.__sync_mimic()
1278
1280 """Called by Presentation. Overrides method in Controller base class."""
1281 import VisionEgg.Core
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
1293 """Called by Presentation. Overrides method in Controller base class."""
1294 import VisionEgg.Core
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
1303
1304
1305
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
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