Some cleanups to the way scenes end

This commit is contained in:
Grant Sanderson 2022-05-03 12:41:44 -07:00
parent a87d3b5f59
commit 22c5e79f5f

View file

@ -64,7 +64,6 @@ class Scene(object):
"preview": True, "preview": True,
"presenter_mode": False, "presenter_mode": False,
"show_animation_progress": False, "show_animation_progress": False,
"linger_after_completion": True,
"pan_sensitivity": 3, "pan_sensitivity": 3,
"max_num_saved_states": 50, "max_num_saved_states": 50,
} }
@ -117,12 +116,14 @@ class Scene(object):
self.file_writer.begin() self.file_writer.begin()
self.setup() self.setup()
if self.presenter_mode:
self.wait()
try: try:
self.construct() self.construct()
except (EndSceneEarlyException, KeyboardInterrupt): self.interact()
self.linger_after_completion = False except EndScene:
pass
except KeyboardInterrupt:
# Get rid keyboard interupt symbols
print("", end="\r")
self.tear_down() self.tear_down()
def setup(self) -> None: def setup(self) -> None:
@ -142,35 +143,33 @@ class Scene(object):
self.stop_skipping() self.stop_skipping()
self.file_writer.finish() self.file_writer.finish()
if self.window: if self.window:
if self.linger_after_completion:
self.interact()
else:
self.window.destroy() self.window.destroy()
self.window = None self.window = None
if self.inside_embed:
self.embed_shell.enable_gui(None)
self.embed_shell.exiter()
def interact(self) -> None: def interact(self) -> None:
# If there is a window, enter a loop # If there is a window, enter a loop
# which updates the frame while under # which updates the frame while under
# the hood calling the pyglet event loop # the hood calling the pyglet event loop
if self.window is None:
return
log.info( log.info(
"Tips: You are now in the interactive mode. Now you can use the keyboard" "Tips: You are now in the interactive mode. Now you can use the keyboard"
" and the mouse to interact with the scene. Just press `command + q` or `esc`" " and the mouse to interact with the scene. Just press `command + q` or `esc`"
" if you want to quit." " if you want to quit."
) )
self.skip_animations = False
self.refresh_static_mobjects() self.refresh_static_mobjects()
try: while not self.is_window_closing():
while True:
self.update_frame(1 / self.camera.frame_rate) self.update_frame(1 / self.camera.frame_rate)
except (EndSceneEarlyException, KeyboardInterrupt):
return
def embed(self, close_scene_on_exit: bool = True) -> None: def embed(self, close_scene_on_exit: bool = True) -> None:
if not self.preview: if not self.preview:
# Ignore embed calls when there is no preview return # Embed is only relevant with a preview
return
self.inside_embed = True self.inside_embed = True
self.stop_skipping() self.stop_skipping()
self.linger_after_completion = False
self.update_frame() self.update_frame()
self.save_state() self.save_state()
@ -214,8 +213,10 @@ class Scene(object):
# Enables gui interactions during the embed # Enables gui interactions during the embed
def inputhook(context): def inputhook(context):
while not context.input_is_ready(): while not context.input_is_ready():
if not self.window.is_closing: if not self.is_window_closing():
self.update_frame(dt=0) self.update_frame(dt=0)
if self.is_window_closing():
shell.ask_exit()
pt_inputhooks.register("manim", inputhook) pt_inputhooks.register("manim", inputhook)
shell.enable_gui("manim") shell.enable_gui("manim")
@ -226,7 +227,10 @@ class Scene(object):
# That comes up a fair bit during scene construction, to get around this, # That comes up a fair bit during scene construction, to get around this,
# we (admittedly sketchily) update the global namespace to match the local # we (admittedly sketchily) update the global namespace to match the local
# namespace, since this is just a shell session anyway. # namespace, since this is just a shell session anyway.
shell.events.register("pre_run_cell", lambda: shell.user_global_ns.update(shell.user_ns)) shell.events.register(
"pre_run_cell",
lambda: shell.user_global_ns.update(shell.user_ns)
)
# Operation to run after each ipython command # Operation to run after each ipython command
def post_cell_func(): def post_cell_func():
@ -236,13 +240,11 @@ class Scene(object):
shell.events.register("post_run_cell", post_cell_func) shell.events.register("post_run_cell", post_cell_func)
# Launch shell, with stack_depth=2 indicating we should use caller globals/locals
shell(local_ns=local_ns, stack_depth=2) shell(local_ns=local_ns, stack_depth=2)
self.inside_embed = False
# End scene when exiting an embed # End scene when exiting an embed
if close_scene_on_exit: if close_scene_on_exit:
raise EndSceneEarlyException() raise EndScene()
# Only these methods should touch the camera # Only these methods should touch the camera
@ -259,8 +261,8 @@ class Scene(object):
if self.skip_animations and not ignore_skipping: if self.skip_animations and not ignore_skipping:
return return
if self.window is not None and (self.window.is_closing or self.quit_interaction): if self.is_window_closing():
raise EndSceneEarlyException() raise EndScene()
if self.window: if self.window:
self.window.clear() self.window.clear()
@ -444,7 +446,7 @@ class Scene(object):
self.stop_skipping() self.stop_skipping()
if self.end_at_animation_number is not None: if self.end_at_animation_number is not None:
if self.num_plays >= self.end_at_animation_number: if self.num_plays >= self.end_at_animation_number:
raise EndSceneEarlyException() raise EndScene()
def stop_skipping(self) -> None: def stop_skipping(self) -> None:
self.virtual_animation_start_time = self.time self.virtual_animation_start_time = self.time
@ -707,6 +709,9 @@ class Scene(object):
path = os.path.join(directory, file_name) path = os.path.join(directory, file_name)
return Mobject.load(path) return Mobject.load(path)
def is_window_closing(self):
return self.window and (self.window.is_closing or self.quit_interaction)
# Event handling # Event handling
def on_mouse_motion( def on_mouse_motion(
@ -887,5 +892,5 @@ class SceneState():
] ]
class EndSceneEarlyException(Exception): class EndScene(Exception):
pass pass