def run( self, inputcode, max_iterations=None, run_forever=False, frame_limiter=False, verbose=False, ): if os.path.isfile(inputcode): source = open(inputcode).read() filename = inputcode elif isinstance(inputcode, str): filename = "<string>" source = inputcode else: raise ValueError("inputcode must be a str or file like object.") self._load_namespace(self._namespace, filename) # TODO: The shell module (sbio) accesses the executor via its name here, # making this event based would remove the need for this. self._executor = executor = LiveExecution( source, ns=self._namespace, filename=filename ) if run_forever is False: if max_iterations is None: max_iterations = 1 try: # Iterations only increment, whereas FRAME can decrement if the user sets a negative speed. iteration = 0 first_run = True while first_run or iteration != max_iterations: # Main loop: # - Setup bot on first run. # - Run draw function for if present. # - Process events # - Update state start_time = time() iteration += 1 canvas_dirty = False # Reset output graphics state self._canvas.reset_canvas() with executor.run_context() as (known_good, source, ns): if not known_good: # New code has been loaded, but it may have errors. # Setting first_run forces the global context to be re-run # Which has the side effect of loading all functions and state. first_run = True if first_run: # Run code in the global namespace, followed by setup() executor.run() if "setup" in executor.ns: executor.ns["setup"]() if "draw" in executor.ns: if self._speed is None: self._speed = DEFAULT_ANIMATION_SPEED # Store initial state so script can revert to a known state when livecoding. self._initial_namespace = copy.copy(self._namespace) canvas_dirty = True is_animation = "draw" in executor.ns if is_animation and self._speed != 0: # If speed is 0, then don't output anything.. executor.ns["draw"]() canvas_dirty = True if canvas_dirty: self._canvas.flush(self._frame) if frame_limiter: # Frame limiting is only used when running the GUI. if is_animation: # User specifies framerate, via speed(...) or use a default. fps = self._speed timeout = self._calculate_frame_delay( fps if fps is not None else DEFAULT_ANIMATION_SPEED, start_time, ) next_frame_due = time() + timeout else: # Re-run the mainloop at 30fps, so that the GUI remains responsive. next_frame_due = time() + 1.0 / DEFAULT_GUI_UPDATE_SPEED else: # Do not sleep between frames. next_frame_due = time() # Handle events continue_running, first_run = self._handle_events( iteration, is_animation, next_frame_due ) if not continue_running: # Event handler returns False if it receives a message to quit. break # Main loop has finished, return True to indicate it exited normally. return True except Exception as e: # Catch Exception, not BaseException, so that KeyboardInterrupts (ctrl+c) still work. # if something goes wrong, print verbose system output. import sys if verbose: errmsg = traceback.format_exc() else: errmsg = simple_traceback(e, executor.known_good or "") sys.stderr.write(f"{errmsg}\n") return False
def run(self, inputcode, iterations=None, run_forever=False, frame_limiter=False, verbose=False, break_on_error=False): ''' Executes the contents of a Nodebox/Shoebot script in current surface's context. :param inputcode: Path to shoebot source or string containing source :param iterations: None or Maximum amount of frames to run :param run_forever: If True then run until user quits the bot :param frame_limiter: If True then sleep between frames to respect speed() command. ''' source = None filename = None if os.path.isfile(inputcode): source = open(inputcode).read() filename = inputcode elif isinstance(inputcode, str): filename = 'shoebot_code' source = inputcode self._load_namespace(self._namespace, filename) self._executor = executor = LiveExecution(source, ns=self._namespace, filename=filename) try: if not iterations: if run_forever: iterations = None else: iterations = 1 iteration = 0 event = None while iteration != iterations and not event_is(event, QUIT_EVENT): # do the magic # First iteration self._run_frame(executor, limit=frame_limiter, iteration=iteration) if iteration == 0: self._initial_namespace = copy.copy( self._namespace) # Stored so script can be rewound iteration += 1 # Subsequent iterations while self._should_run(iteration, iterations) and event is None: iteration += 1 self._run_frame(executor, limit=frame_limiter, iteration=iteration) event = next_event() if not event: self._canvas.sink.main_iteration( ) # update GUI, may generate events.. while run_forever: # # Running in GUI, bot has finished # Either - # receive quit event and quit # receive any other event and loop (e.g. if var changed or source edited) # while event is None: self._canvas.sink.main_iteration() event = next_event(block=True, timeout=0.05) if not event: self._canvas.sink.main_iteration( ) # update GUI, may generate events.. if event.type == QUIT_EVENT: break elif event.type == SOURCE_CHANGED_EVENT: # Debounce SOURCE_CHANGED events - # gedit generates two events for changing a single character - # delete and then add while event and event.type == SOURCE_CHANGED_EVENT: event = next_event(block=True, timeout=0.001) elif event.type == SET_WINDOW_TITLE: self._canvas.sink.set_title(event.data) event = None # this loop is a bit weird... break except Exception as e: # this makes KeyboardInterrupts still work # if something goes wrong, print verbose system output # maybe this is too verbose, but okay for now import sys if verbose: errmsg = traceback.format_exc() else: errmsg = simple_traceback(e, executor.known_good or '') print(errmsg, file=sys.stderr) if break_on_error: raise
def run(self, inputcode, iterations=None, run_forever=False, frame_limiter=False, verbose=False, break_on_error=False): ''' Executes the contents of a Nodebox/Shoebot script in current surface's context. :param inputcode: Path to shoebot source or string containing source :param iterations: None or Maximum amount of frames to run :param run_forever: If True then run until user quits the bot :param frame_limiter: If True then sleep between frames to respect speed() command. ''' source = None filename = None if os.path.isfile(inputcode): source = open(inputcode).read() filename = inputcode elif isinstance(inputcode, basestring): filename = 'shoebot_code' source = inputcode self._load_namespace(self._namespace, filename) self._executor = executor = LiveExecution(source, ns=self._namespace, filename=filename) try: if not iterations: if run_forever: iterations = None else: iterations = 1 iteration = 0 event = None while iteration != iterations and not event_is(event, QUIT_EVENT): # do the magic # First iteration self._run_frame(executor, limit=frame_limiter, iteration=iteration) if iteration == 0: self._initial_namespace = copy.copy(self._namespace) # Stored so script can be rewound iteration += 1 # Subsequent iterations while self._should_run(iteration, iterations) and event is None: iteration += 1 self._run_frame(executor, limit=frame_limiter, iteration=iteration) event = next_event() if not event: self._canvas.sink.main_iteration() # update GUI, may generate events.. while run_forever: # # Running in GUI, bot has finished # Either - # receive quit event and quit # receive any other event and loop (e.g. if var changed or source edited) # while event is None: self._canvas.sink.main_iteration() event = next_event(block=True, timeout=0.05) if not event: self._canvas.sink.main_iteration() # update GUI, may generate events.. if event.type == QUIT_EVENT: break elif event.type == SOURCE_CHANGED_EVENT: # Debounce SOURCE_CHANGED events - # gedit generates two events for changing a single character - # delete and then add while event and event.type == SOURCE_CHANGED_EVENT: event = next_event(block=True, timeout=0.001) elif event.type == SET_WINDOW_TITLE: self._canvas.sink.set_title(event.data) event = None # this loop is a bit weird... break except Exception as e: # this makes KeyboardInterrupts still work # if something goes wrong, print verbose system output # maybe this is too verbose, but okay for now import sys if verbose: errmsg = traceback.format_exc() else: errmsg = simple_traceback(e, executor.known_good or '') print >> sys.stderr, errmsg if break_on_error: raise