Skip to content

Commit 585577b

Browse files
authored
Merge pull request #98 from ManimCommunity/configparser-setup
Configparser setup: figuring out config.py, constants.py, dirs.py, CLI arguments, and more [WIP]
2 parents 4b6492b + b69f6e8 commit 585577b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1030
-988
lines changed

.travis.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
- os: linux
1616
language: python
17-
python: 3.7
17+
python: 3.7
1818
install:
1919
- pip3 install --upgrade pip
2020
- pip3 install -r ./.travis/travis-requirements.txt
@@ -39,7 +39,7 @@ jobs:
3939
language: sh
4040
python: 3.6
4141
env: PYVER="3.6.10"
42-
before_install:
42+
before_install:
4343
- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
4444
- brew install ffmpeg
4545
- brew cask install mactex
@@ -56,7 +56,7 @@ jobs:
5656
language: sh
5757
python: 3.7
5858
env: PYVER="3.7.7"
59-
before_install:
59+
before_install:
6060
- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
6161
- brew install ffmpeg
6262
- brew cask install mactex
@@ -73,7 +73,7 @@ jobs:
7373
language: sh
7474
python: 3.8
7575
env: PYVER="3.8.0" # Using Python 3.8.0 due to error with rich
76-
before_install:
76+
before_install:
7777
- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
7878
- brew install ffmpeg
7979
- brew cask install mactex
@@ -101,7 +101,7 @@ jobs:
101101
- python ./scripts/pycairoinstall.py
102102
- cmd.exe //c "RefreshEnv.cmd"
103103
- python -m pip install --user .
104-
script:
104+
script:
105105
- python -m pytest --skip_end_to_end -rs
106106

107107
- os: windows
@@ -117,7 +117,7 @@ jobs:
117117
- python ./scripts/pycairoinstall.py
118118
- cmd.exe //c "RefreshEnv.cmd"
119119
- python -m pip install --user .
120-
script:
120+
script:
121121
- python -m pytest --skip_end_to_end -rs
122122

123123
- os: windows
@@ -133,12 +133,13 @@ jobs:
133133
- python ./scripts/pycairoinstall.py
134134
- cmd.exe //c "RefreshEnv.cmd"
135135
- python -m pip install --user .
136-
script:
136+
script:
137137
- python -m pytest --skip_end_to_end -rs
138138

139139
before_install:
140140
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then chmod +x ./.travis/osx.sh; sh ./.travis/osx.sh; fi
141141
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then chmod +x ./.travis/linux.sh; sh ./.travis/linux.sh; fi
142+
142143
branches:
143144
only:
144145
- master

.travis/linux.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
sudo apt update
44
sudo apt install -y ffmpeg
5-
sudo apt-get -y install texlive texlive-latex-extra texlive-fonts-extra texlive-latex-recommended texlive-science texlive-fonts-extra tipa
5+
sudo apt-get -y install texlive texlive-latex-extra texlive-fonts-extra texlive-latex-recommended texlive-science texlive-fonts-extra tipa

.travis/osx.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
brew update
44
brew install openssl readline
55
brew outdated pyenv || brew upgrade pyenv
6+
brew install ffmpeg
67
brew install pyenv-virtualenv
78
pyenv install $PYVER
89
export PYENV_VERSION=$PYVER
910
export PATH="/Users/travis/.pyenv/shims:${PATH}"
1011
pyenv virtualenv venv
1112
source ~/.pyenv/versions/venv/bin/activate
12-
python --version
13+
python --version

.travis/travis-requirements.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ Pillow
55
progressbar
66
scipy
77
tqdm
8-
# opencv-python
9-
# pycairo
108
pydub
119
pygments
1210
pyreadline; sys_platform == 'win32'
1311
rich
14-
pytest
12+
pytest

example_scenes/basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def construct(self):
106106
)
107107
group = VGroup(example_text, example_tex)
108108
group.arrange(DOWN)
109-
group.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)
109+
group.set_width(config['frame_width'] - 2 * LARGE_BUFF)
110110

111111
self.play(Write(example_text))
112112
self.play(Write(example_tex))

example_scenes/customtex.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@
1212

1313
class ExampleFileScene(Scene):
1414
def construct(self):
15-
text=TexMobject(r"\vv{vb}")
15+
text = TexMobject(r"\vv{vb}")
1616
#text=TextMobject(r"$\vv{vb}$")
1717
self.play(Write(text))
1818

19+
1920
class ExampleClassScene(Scene):
2021
def construct(self):
21-
tpl=TexTemplate()
22+
tpl = TexTemplate()
2223
tpl.append_package(["esvect",["f"]])
2324
config.register_tex_template(tpl)
2425

2526
#text=TextMobject(r"$\vv{vb}$")
26-
text=TexMobject(r"\vv{vb}")
27+
text = TexMobject(r"\vv{vb}")
2728
self.play(Write(text))

manim/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#!/usr/bin/env python
2+
3+
# Importing config should be the first thing since other modules use it
4+
from .config import config
25
from .constants import *
36

47
from .animation.animation import *

manim/__main__.py

Lines changed: 160 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,167 @@
1-
from . import extract_scene
2-
from . import config
1+
import inspect
2+
import os
3+
import platform
4+
import subprocess as sp
5+
import sys
6+
import re
7+
import traceback
8+
import importlib.util
9+
10+
from .config import file_writer_config
11+
from .scene.scene import Scene
12+
from .utils.sounds import play_error_sound
13+
from .utils.sounds import play_finish_sound
314
from . import constants
15+
from .logger import logger
16+
17+
18+
def open_file_if_needed(file_writer):
19+
if file_writer_config["quiet"]:
20+
curr_stdout = sys.stdout
21+
sys.stdout = open(os.devnull, "w")
22+
23+
open_file = any([
24+
file_writer_config["preview"],
25+
file_writer_config["show_file_in_finder"]
26+
])
27+
if open_file:
28+
current_os = platform.system()
29+
file_paths = []
30+
31+
if file_writer_config["save_last_frame"]:
32+
file_paths.append(file_writer.get_image_file_path())
33+
if file_writer_config["write_to_movie"]:
34+
file_paths.append(file_writer.get_movie_file_path())
35+
36+
for file_path in file_paths:
37+
if current_os == "Windows":
38+
os.startfile(file_path)
39+
else:
40+
commands = []
41+
if current_os == "Linux":
42+
commands.append("xdg-open")
43+
elif current_os.startswith("CYGWIN"):
44+
commands.append("cygstart")
45+
else: # Assume macOS
46+
commands.append("open")
47+
48+
if file_writer_config["show_file_in_finder"]:
49+
commands.append("-R")
50+
51+
commands.append(file_path)
52+
53+
# commands.append("-g")
54+
FNULL = open(os.devnull, 'w')
55+
sp.call(commands, stdout=FNULL, stderr=sp.STDOUT)
56+
FNULL.close()
57+
58+
if file_writer_config["quiet"]:
59+
sys.stdout.close()
60+
sys.stdout = curr_stdout
61+
62+
63+
def is_child_scene(obj, module):
64+
return (inspect.isclass(obj)
65+
and issubclass(obj, Scene)
66+
and obj != Scene
67+
and obj.__module__.startswith(module.__name__))
68+
69+
70+
def prompt_user_for_choice(scene_classes):
71+
num_to_class = {}
72+
for count, scene_class in enumerate(scene_classes):
73+
count += 1 # start with 1 instead of 0
74+
name = scene_class.__name__
75+
print("%d: %s" % (count, name))
76+
num_to_class[count] = scene_class
77+
try:
78+
user_input = input(constants.CHOOSE_NUMBER_MESSAGE)
79+
return [num_to_class[int(num_str)]
80+
for num_str in re.split(r"\s*,\s*", user_input.strip())]
81+
except KeyError:
82+
logger.error(constants.INVALID_NUMBER_MESSAGE)
83+
sys.exit(2)
84+
except EOFError:
85+
sys.exit(1)
86+
87+
88+
def get_scenes_to_render(scene_classes):
89+
if not scene_classes:
90+
logger.error(constants.NO_SCENE_MESSAGE)
91+
return []
92+
if file_writer_config["write_all"]:
93+
return scene_classes
94+
result = []
95+
for scene_name in file_writer_config["scene_names"]:
96+
found = False
97+
for scene_class in scene_classes:
98+
if scene_class.__name__ == scene_name:
99+
result.append(scene_class)
100+
found = True
101+
break
102+
if not found and (scene_name != ""):
103+
logger.error(
104+
constants.SCENE_NOT_FOUND_MESSAGE.format(
105+
scene_name
106+
)
107+
)
108+
if result:
109+
return result
110+
return [scene_classes[0]] if len(scene_classes) == 1 else prompt_user_for_choice(scene_classes)
111+
112+
113+
def get_scene_classes_from_module(module):
114+
if hasattr(module, "SCENES_IN_ORDER"):
115+
return module.SCENES_IN_ORDER
116+
else:
117+
return [
118+
member[1]
119+
for member in inspect.getmembers(
120+
module,
121+
lambda x: is_child_scene(x, module)
122+
)
123+
]
124+
125+
126+
def get_module(file_name):
127+
if file_name == "-":
128+
module = types.ModuleType("input_scenes")
129+
code = sys.stdin.read()
130+
try:
131+
exec(code, module.__dict__)
132+
return module
133+
except Exception as e:
134+
logger.error(f"Failed to render scene: {str(e)}")
135+
sys.exit(2)
136+
else:
137+
if os.path.exists(file_name):
138+
module_name = re.sub(r"\..+$", "", file_name.replace(os.sep, "."))
139+
spec = importlib.util.spec_from_file_location(module_name, file_name)
140+
module = importlib.util.module_from_spec(spec)
141+
spec.loader.exec_module(module)
142+
return module
143+
else:
144+
raise FileNotFoundError(f'{file_name} not found')
4145

5146

6147
def main():
7-
args = config.parse_cli()
8-
cfg = config.get_configuration(args)
9-
config.initialize_directories(cfg)
10-
config.initialize_tex(cfg)
11-
extract_scene.main(cfg)
148+
module = get_module(file_writer_config["input_file"])
149+
all_scene_classes = get_scene_classes_from_module(module)
150+
scene_classes_to_render = get_scenes_to_render(all_scene_classes)
151+
sound_on = file_writer_config["sound"]
152+
for SceneClass in scene_classes_to_render:
153+
try:
154+
# By invoking, this renders the full scene
155+
scene = SceneClass()
156+
open_file_if_needed(scene.file_writer)
157+
if sound_on:
158+
play_finish_sound()
159+
except Exception:
160+
print("\n\n")
161+
traceback.print_exc()
162+
print("\n\n")
163+
if sound_on:
164+
play_error_sound()
12165

13166

14167
if __name__ == "__main__":

manim/animation/transform.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,4 @@ def __init__(self, start_anim, end_anim, **kwargs):
321321
def interpolate(self, alpha):
322322
self.start_anim.interpolate(alpha)
323323
self.end_anim.interpolate(alpha)
324-
Transform.interpolate(self, alpha)
324+
Transform.interpolate(self, alpha)

manim/camera/camera.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import numpy as np
1111

1212
from ..constants import *
13+
from ..config import config
1314
from ..logger import logger
1415
from ..mobject.types.image_mobject import AbstractImageMobject
1516
from ..mobject.mobject import Mobject
@@ -36,24 +37,24 @@ class Camera(object):
3637
self.background_image : str, optional
3738
The path to an image that should be the background image.
3839
If not set, the background is filled with `self.background_color`
39-
40+
4041
self.pixel_height
4142
"""
4243
CONFIG = {
4344
"background_image": None,
44-
"pixel_height": DEFAULT_PIXEL_HEIGHT,
45-
"pixel_width": DEFAULT_PIXEL_WIDTH,
46-
"frame_rate": DEFAULT_FRAME_RATE,
45+
"pixel_height": config['pixel_height'],
46+
"pixel_width": config['pixel_width'],
47+
"frame_rate": config['frame_rate'],
4748
# Note: frame height and width will be resized to match
4849
# the pixel aspect ratio
49-
"frame_height": FRAME_HEIGHT,
50-
"frame_width": FRAME_WIDTH,
50+
"frame_height": config['frame_height'],
51+
"frame_width": config['frame_width'],
5152
"frame_center": ORIGIN,
5253
"background_color": BLACK,
5354
"background_opacity": 1,
5455
# Points in vectorized mobjects with norm greater
5556
# than this value will be rescaled.
56-
"max_allowable_norm": FRAME_WIDTH,
57+
"max_allowable_norm": config['frame_width'],
5758
"image_mode": "RGBA",
5859
"n_channels": 4,
5960
"pixel_array_dtype": 'uint8',
@@ -767,7 +768,7 @@ def apply_stroke(self, ctx, vmobject, background=False):
767768
width * self.cairo_line_width_multiple *
768769
# This ensures lines have constant width
769770
# as you zoom in on them.
770-
(self.get_frame_width() / FRAME_WIDTH)
771+
(self.get_frame_width() / self.frame_width)
771772
)
772773
ctx.stroke_preserve()
773774
return self
@@ -1331,4 +1332,4 @@ def display(self, *cvmobjects):
13311332
else:
13321333
curr_array = np.maximum(curr_array, new_array)
13331334
self.reset_pixel_array()
1334-
return curr_array
1335+
return curr_array

0 commit comments

Comments
 (0)