mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Refactor Scene.embed and checkpoint_paste
This commit is contained in:
parent
c330dfddae
commit
99fa3ee620
1 changed files with 64 additions and 45 deletions
|
@ -8,6 +8,10 @@ import pyperclip
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from IPython.terminal import pt_inputhooks
|
||||||
|
from IPython.terminal.embed import InteractiveShellEmbed
|
||||||
|
from IPython.core.getipython import get_ipython
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm as ProgressDisplay
|
from tqdm import tqdm as ProgressDisplay
|
||||||
|
|
||||||
|
@ -110,7 +114,6 @@ class Scene(object):
|
||||||
self.mouse_point = Point()
|
self.mouse_point = Point()
|
||||||
self.mouse_drag_point = Point()
|
self.mouse_drag_point = Point()
|
||||||
self.hold_on_wait = self.presenter_mode
|
self.hold_on_wait = self.presenter_mode
|
||||||
self.inside_embed = False
|
|
||||||
self.quit_interaction = False
|
self.quit_interaction = False
|
||||||
|
|
||||||
# Much nicer to work with deterministic scenes
|
# Much nicer to work with deterministic scenes
|
||||||
|
@ -176,59 +179,39 @@ class Scene(object):
|
||||||
while not self.is_window_closing():
|
while not self.is_window_closing():
|
||||||
self.update_frame(1 / self.camera.fps)
|
self.update_frame(1 / self.camera.fps)
|
||||||
|
|
||||||
def embed(self, close_scene_on_exit: bool = True) -> None:
|
def embed(
|
||||||
|
self,
|
||||||
|
close_scene_on_exit: bool = True,
|
||||||
|
show_animation_progress: bool = True,
|
||||||
|
) -> None:
|
||||||
if not self.preview:
|
if not self.preview:
|
||||||
return # Embed is only relevant with a preview
|
return # Embed is only relevant with a preview
|
||||||
self.inside_embed = True
|
|
||||||
self.stop_skipping()
|
self.stop_skipping()
|
||||||
self.update_frame()
|
self.update_frame()
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
self.show_animation_progress = show_animation_progress
|
||||||
|
|
||||||
# Configure and launch embedded IPython terminal
|
# Create embedded IPython terminal to be configured
|
||||||
from IPython.terminal import embed, pt_inputhooks
|
shell = InteractiveShellEmbed.instance()
|
||||||
shell = embed.InteractiveShellEmbed.instance()
|
|
||||||
|
|
||||||
# Use the locals namespace of the caller
|
# Use the locals namespace of the caller
|
||||||
caller_frame = inspect.currentframe().f_back
|
caller_frame = inspect.currentframe().f_back
|
||||||
local_ns = dict(caller_frame.f_locals)
|
local_ns = dict(caller_frame.f_locals)
|
||||||
|
|
||||||
# Add a few custom shortcuts
|
# Add a few custom shortcuts
|
||||||
local_ns.update({
|
local_ns.update(
|
||||||
name: getattr(self, name)
|
play=self.play,
|
||||||
for name in [
|
wait=self.wait,
|
||||||
"play", "wait", "add", "remove", "clear",
|
add=self.add,
|
||||||
"save_state", "undo", "redo", "i2g", "i2m"
|
remove=self.remove,
|
||||||
]
|
clear=self.clear,
|
||||||
})
|
save_state=self.save_state,
|
||||||
|
undo=self.undo,
|
||||||
module = get_module(caller_frame.f_globals["__file__"])
|
redo=self.redo,
|
||||||
|
i2g=self.i2g,
|
||||||
# This is useful if one wants to re-run a block of scene
|
i2m=self.i2m,
|
||||||
# code, while developing, tweaking it each time.
|
checkpoint_paste=self.checkpoint_paste,
|
||||||
# As long as the copied selection starts with a comment,
|
)
|
||||||
# this will revert to the state of the scene at the first
|
|
||||||
# point of running.
|
|
||||||
def checkpoint_paste(show_progress=True, skip=False):
|
|
||||||
pasted = pyperclip.paste()
|
|
||||||
line0 = pasted.lstrip().split("\n")[0]
|
|
||||||
if line0.startswith("#"):
|
|
||||||
if line0 not in self.checkpoint_states:
|
|
||||||
self.checkpoint(line0)
|
|
||||||
else:
|
|
||||||
self.revert_to_checkpoint(line0)
|
|
||||||
self.update_frame(dt=0)
|
|
||||||
|
|
||||||
prev_show_progress = self.show_animation_progress
|
|
||||||
prev_skipping = self.skip_animations
|
|
||||||
self.show_animation_progress = show_progress
|
|
||||||
self.skip_animations = skip
|
|
||||||
|
|
||||||
shell.run_cell(pyperclip.paste())
|
|
||||||
|
|
||||||
self.show_animation_progress = prev_show_progress
|
|
||||||
self.skip_animations = prev_skipping
|
|
||||||
|
|
||||||
local_ns['checkpoint_paste'] = checkpoint_paste
|
|
||||||
|
|
||||||
# Enables gui interactions during the embed
|
# Enables gui interactions during the embed
|
||||||
def inputhook(context):
|
def inputhook(context):
|
||||||
|
@ -261,7 +244,14 @@ class Scene(object):
|
||||||
|
|
||||||
shell.events.register("post_run_cell", post_cell_func)
|
shell.events.register("post_run_cell", post_cell_func)
|
||||||
|
|
||||||
shell(local_ns=local_ns, stack_depth=2, module=module)
|
# Launch shell
|
||||||
|
shell(
|
||||||
|
local_ns=local_ns,
|
||||||
|
# Pretend like we're embeding in the caller function, not here
|
||||||
|
stack_depth=2,
|
||||||
|
# Specify that the present module is the caller's, not here
|
||||||
|
module=get_module(caller_frame.f_globals["__file__"])
|
||||||
|
)
|
||||||
|
|
||||||
# End scene when exiting an embed
|
# End scene when exiting an embed
|
||||||
if close_scene_on_exit:
|
if close_scene_on_exit:
|
||||||
|
@ -527,8 +517,6 @@ class Scene(object):
|
||||||
return self.get_time_progression(duration, **kw)
|
return self.get_time_progression(duration, **kw)
|
||||||
|
|
||||||
def pre_play(self):
|
def pre_play(self):
|
||||||
if self.inside_embed:
|
|
||||||
self.save_state()
|
|
||||||
if self.presenter_mode and self.num_plays == 0:
|
if self.presenter_mode and self.num_plays == 0:
|
||||||
self.hold_loop()
|
self.hold_loop()
|
||||||
|
|
||||||
|
@ -699,6 +687,37 @@ class Scene(object):
|
||||||
self.restore_state(self.redo_stack.pop())
|
self.restore_state(self.redo_stack.pop())
|
||||||
self.refresh_static_mobjects()
|
self.refresh_static_mobjects()
|
||||||
|
|
||||||
|
def checkpoint_paste(self, skip: bool = False):
|
||||||
|
"""
|
||||||
|
Used during interactive development to run (or re-run)
|
||||||
|
a block of scene code.
|
||||||
|
|
||||||
|
If the copied selection starts with a comment, this will
|
||||||
|
revert to the state of the scene the first time this function
|
||||||
|
was called on a block of code starting with that comment.
|
||||||
|
"""
|
||||||
|
pasted = pyperclip.paste()
|
||||||
|
line0 = pasted.lstrip().split("\n")[0]
|
||||||
|
if line0.startswith("#"):
|
||||||
|
if line0 not in self.checkpoint_states:
|
||||||
|
self.checkpoint(line0)
|
||||||
|
else:
|
||||||
|
self.revert_to_checkpoint(line0)
|
||||||
|
|
||||||
|
shell = get_ipython()
|
||||||
|
if shell is None:
|
||||||
|
raise Exception(
|
||||||
|
"Scene.checkpoint_paste cannot be called outside of " +
|
||||||
|
"an ipython shell"
|
||||||
|
)
|
||||||
|
|
||||||
|
prev_skipping = self.skip_animations
|
||||||
|
self.skip_animations = skip
|
||||||
|
|
||||||
|
shell.run_cell(pasted)
|
||||||
|
|
||||||
|
self.skip_animations = prev_skipping
|
||||||
|
|
||||||
def checkpoint(self, key: str):
|
def checkpoint(self, key: str):
|
||||||
self.checkpoint_states[key] = self.get_state()
|
self.checkpoint_states[key] = self.get_state()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue