3b1b-manim/manimlib/extract_scene.py

169 lines
4.8 KiB
Python
Raw Normal View History

import inspect
import itertools as it
2017-06-29 14:33:01 +03:00
import os
import platform
import subprocess as sp
import sys
import traceback
from manimlib.scene.scene import Scene
from manimlib.utils.sounds import play_error_sound
from manimlib.utils.sounds import play_finish_sound
import manimlib.constants
2015-10-10 18:48:54 -07:00
2019-01-24 21:47:40 -08:00
def open_file_if_needed(file_writer, **config):
if config["quiet"]:
curr_stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
open_file = any([
config["open_video_upon_completion"],
config["show_file_in_finder"]
])
if open_file:
current_os = platform.system()
2019-01-24 21:47:40 -08:00
file_paths = []
2018-07-11 12:26:24 -07:00
2019-01-24 21:47:40 -08:00
if config["file_writer_config"]["save_last_frame"]:
file_paths.append(file_writer.get_image_file_path())
if config["file_writer_config"]["write_to_movie"]:
file_paths.append(file_writer.get_movie_file_path())
2019-01-24 21:47:40 -08:00
for file_path in file_paths:
if current_os == "Windows":
os.startfile(file_path)
else:
commands = []
2019-04-03 21:10:20 -07:00
if current_os == "Linux":
2019-01-24 21:47:40 -08:00
commands.append("xdg-open")
2019-04-03 21:10:20 -07:00
elif current_os.startswith("CYGWIN"):
commands.append("cygstart")
2019-01-24 21:47:40 -08:00
else: # Assume macOS
commands.append("open")
2019-01-24 21:47:40 -08:00
if config["show_file_in_finder"]:
commands.append("-R")
2019-01-24 21:47:40 -08:00
commands.append(file_path)
2019-01-24 21:47:40 -08:00
# commands.append("-g")
FNULL = open(os.devnull, 'w')
sp.call(commands, stdout=FNULL, stderr=sp.STDOUT)
FNULL.close()
if config["quiet"]:
sys.stdout.close()
sys.stdout = curr_stdout
2015-05-07 21:28:02 -07:00
2018-12-25 19:51:03 -08:00
def is_child_scene(obj, module):
if not inspect.isclass(obj):
return False
if not issubclass(obj, Scene):
return False
if obj == Scene:
return False
2019-07-24 18:01:12 -05:00
if not obj.__module__.startswith(module.__name__):
return False
return True
2015-05-07 21:28:02 -07:00
def prompt_user_for_choice(scene_classes):
num_to_class = {}
for count, scene_class in zip(it.count(1), scene_classes):
name = scene_class.__name__
print("%d: %s" % (count, name))
num_to_class[count] = scene_class
try:
user_input = input(manimlib.constants.CHOOSE_NUMBER_MESSAGE)
return [
num_to_class[int(num_str)]
for num_str in user_input.split(",")
]
except KeyError:
print(manimlib.constants.INVALID_NUMBER_MESSAGE)
2018-12-25 19:51:03 -08:00
sys.exit(2)
user_input = input(manimlib.constants.CHOOSE_NUMBER_MESSAGE)
return [
num_to_class[int(num_str)]
for num_str in user_input.split(",")
]
2018-12-25 19:51:03 -08:00
except EOFError:
sys.exit(1)
2015-10-20 21:55:46 -07:00
def get_scenes_to_render(scene_classes, config):
if len(scene_classes) == 0:
print(manimlib.constants.NO_SCENE_MESSAGE)
return []
if config["write_all"]:
return scene_classes
result = []
for scene_name in config["scene_names"]:
found = False
for scene_class in scene_classes:
if scene_class.__name__ == scene_name:
result.append(scene_class)
found = True
break
if not found and (scene_name != ""):
print(
manimlib.constants.SCENE_NOT_FOUND_MESSAGE.format(
scene_name
),
file=sys.stderr
)
if result:
return result
2019-07-26 14:34:21 +08:00
return [scene_classes[0]] if len(scene_classes) == 1 else prompt_user_for_choice(scene_classes)
def get_scene_classes_from_module(module):
2019-07-24 18:01:12 -05:00
if hasattr(module, "SCENES_IN_ORDER"):
return module.SCENES_IN_ORDER
else:
return [
member[1]
for member in inspect.getmembers(
module,
lambda x: is_child_scene(x, module)
)
]
def main(config):
2018-12-24 12:49:10 -08:00
module = config["module"]
all_scene_classes = get_scene_classes_from_module(module)
scene_classes_to_render = get_scenes_to_render(all_scene_classes, config)
scene_kwargs = dict([
(key, config[key])
for key in [
"camera_config",
2019-01-24 21:47:40 -08:00
"file_writer_config",
"skip_animations",
"start_at_animation_number",
"end_at_animation_number",
"leave_progress_bars",
]
])
for SceneClass in scene_classes_to_render:
try:
2019-01-24 21:47:40 -08:00
# By invoking, this renders the full scene
scene = SceneClass(**scene_kwargs)
open_file_if_needed(scene.file_writer, **config)
if config["sound"]:
play_finish_sound()
except Exception:
print("\n\n")
traceback.print_exc()
print("\n\n")
if config["sound"]:
play_error_sound()
if __name__ == "__main__":
main()