feat(lib): Sphinx directive can now read files (#261)

* feat(lib): Sphinx directive can now read files

You can optionally read a file instead of the Sphinx directive's content

* fix(lib): rst syntax and docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jérome Eertmans
2023-08-29 16:23:54 +02:00
committed by GitHub
parent 9a3a343231
commit bb5b294f40
5 changed files with 57 additions and 32 deletions

View File

@ -56,18 +56,8 @@ jobs:
id: cache-media-restore id: cache-media-restore
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
with: with:
path: media path: docs/media
key: ${{ runner.os }}-media key: ${{ runner.os }}-docs-media
- name: Build animations
run: |
poetry run manim example.py ConvertExample BasicExample ThreeDExample
- name: Convert animations to HTML slides
run: |
poetry run manim-slides convert -v DEBUG ConvertExample docs/source/_static/slides.html -ccontrols=true
poetry run manim-slides convert -v DEBUG BasicExample docs/source/_static/basic_example.html -ccontrols=true
poetry run manim-slides convert -v DEBUG ThreeDExample docs/source/_static/three_d_example.html -ccontrols=true
- name: Show docs/source/_static/ dir content (video only)
run: tree -L 3 docs/source/_static/ -P '*.mp4'
- name: Clear cache - name: Clear cache
run: | run: |
gh extension install actions/gh-actions-cache gh extension install actions/gh-actions-cache
@ -78,7 +68,7 @@ jobs:
id: cache-media-save id: cache-media-save
uses: actions/cache/save@v3 uses: actions/cache/save@v3
with: with:
path: media path: docs/media
key: ${{ steps.cache-media-restore.outputs.cache-primary-key }} key: ${{ steps.cache-media-restore.outputs.cache-primary-key }}
- name: Build docs - name: Build docs
run: cd docs && poetry run make html run: cd docs && poetry run make html

View File

@ -25,6 +25,9 @@ In an effort to better document changes, this CHANGELOG document is now created.
- Added a full screen key binding (defaults to <kbd>F</kbd>) in the - Added a full screen key binding (defaults to <kbd>F</kbd>) in the
presenter. presenter.
[#243](https://github.com/jeertmans/manim-slides/pull/243) [#243](https://github.com/jeertmans/manim-slides/pull/243)
- Added support for including code from a file in Manim Slides
Sphinx directive.
[#261](https://github.com/jeertmans/manim-slides/pull/261)
### Changed ### Changed

View File

@ -28,11 +28,11 @@ In a [very few steps](./quickstart), you can create slides and present them eith
Slide through the demo below to get a quick glimpse on what you can do with Manim Slides. Slide through the demo below to get a quick glimpse on what you can do with Manim Slides.
```{eval-rst}
<!-- From: https://faq.dailymotion.com/hc/en-us/articles/360022841393-How-to-preserve-the-player-aspect-ratio-on-a-responsive-page --> .. manim-slides:: ../../example.py:ConvertExample
:hide_source:
<div style="position:relative;padding-bottom:56.25%;"> <iframe style="width:100%;height:100%;position:absolute;left:0px;top:0px;" frameborder="0" width="100%" height="100%" allowfullscreen allow="autoplay" src="_static/slides.html"></iframe></div> :quality: high
```
```{toctree} ```{toctree}
:hidden: :hidden:

View File

@ -29,9 +29,11 @@ where `-ccontrols=true` indicates that we want to display the blue navigation ar
Basic example from quickstart. Basic example from quickstart.
<div style="position:relative;padding-bottom:56.25%;"> <iframe style="width:100%;height:100%;position:absolute;left:0px;top:0px;" frameborder="0" width="100%" height="100%" allowfullscreen allow="autoplay" src="../_static/basic_example.html"></iframe></div>
```{eval-rst} ```{eval-rst}
.. manim-slides: ../../../example.py:BasicExample
:hide_source:
:quality: high
.. literalinclude:: ../../../example.py .. literalinclude:: ../../../example.py
:language: python :language: python
:linenos: :linenos:
@ -42,11 +44,13 @@ Basic example from quickstart.
Example using 3D camera. As Manim and ManimGL handle 3D differently, definitions are slightly different. Example using 3D camera. As Manim and ManimGL handle 3D differently, definitions are slightly different.
<div style="position:relative;padding-bottom:56.25%;"> <iframe style="width:100%;height:100%;position:absolute;left:0px;top:0px;" frameborder="0" width="100%" height="100%" allowfullscreen allow="autoplay" src="../_static/three_d_example.html"></iframe></div>
### With Manim ### With Manim
```{eval-rst} ```{eval-rst}
.. manim-slides: ../../../example.py:ThreeDExample
:hide_source:
:quality: high
.. literalinclude:: ../../../example.py .. literalinclude:: ../../../example.py
:language: python :language: python
:linenos: :linenos:
@ -120,9 +124,11 @@ directly write the `construct` method in the body of `MovingCameraSlide`.
A more advanced example is `ConvertExample`, which is used as demo slide and tutorial. A more advanced example is `ConvertExample`, which is used as demo slide and tutorial.
<div style="position:relative;padding-bottom:56.25%;"> <iframe style="width:100%;height:100%;position:absolute;left:0px;top:0px;" frameborder="0" width="100%" height="100%" allowfullscreen allow="autoplay" src="../_static/slides.html"></iframe></div>
```{eval-rst} ```{eval-rst}
.. manim-slides: ../../../example.py:ConvertExample
:hide_source:
:quality: high
.. literalinclude:: ../../../example.py .. literalinclude:: ../../../example.py
:language: python :language: python
:linenos: :linenos:

View File

@ -70,16 +70,29 @@ render scenes that are defined within doctests, for example::
... def construct(self): ... def construct(self):
... self.play(Create(dot)) ... self.play(Create(dot))
A third application is to render scenes from another specific file::
.. manim-slides:: file.py:FileExample
:hide_source:
:quality: high
.. warning::
The code will be executed with the current working directory
being the same as the one containing the source file. This being said,
you should probably not include examples that rely on external files, since
relative paths risk to be broken.
Options Options
------- -------
Options can be passed as follows:: Options can be passed as follows::
.. manim-slides:: <Class name> .. manim-slides:: <file>:<Class name>
:<option name>: <value> :<option name>: <value>
The following configuration options are supported by the The following configuration options are supported by the
directive: directive::
hide_source hide_source
If this flag is present without argument, If this flag is present without argument,
@ -110,6 +123,7 @@ import re
import sys import sys
from pathlib import Path from pathlib import Path
from timeit import timeit from timeit import timeit
from typing import Tuple
import jinja2 import jinja2
from docutils import nodes from docutils import nodes
@ -211,7 +225,17 @@ class ManimSlidesDirective(Directive):
global classnamedict global classnamedict
clsname = self.arguments[0] def split_file_cls(arg: str) -> Tuple[Path, str]:
if ":" in arg:
file, cls = arg.split(":", maxsplit=1)
_, file = self.state.document.settings.env.relfn2path(file)
return Path(file), cls
else:
return None, arg
arguments = [split_file_cls(arg) for arg in self.arguments]
clsname = arguments[0][1]
if clsname not in classnamedict: if clsname not in classnamedict:
classnamedict[clsname] = 1 classnamedict[clsname] = 1
else: else:
@ -271,20 +295,24 @@ class ManimSlidesDirective(Directive):
"output_file": output_file, "output_file": output_file,
} }
user_code = self.content if file := arguments[0][0]:
user_code = file.absolute().read_text().splitlines()
else:
user_code = self.content
if user_code[0].startswith(">>> "): # check whether block comes from doctest if user_code[0].startswith(">>> "): # check whether block comes from doctest
user_code = [ user_code = [
line[4:] for line in user_code if line.startswith((">>> ", "... ")) line[4:] for line in user_code if line.startswith((">>> ", "... "))
] ]
code = [ code = [
"from manim import *",
*user_code, *user_code,
f"{clsname}().render()", f"{clsname}().render()",
] ]
try: try:
with tempconfig(example_config): with tempconfig(example_config):
print(f"Rendering {clsname}...")
run_time = timeit(lambda: exec("\n".join(code), globals()), number=1) run_time = timeit(lambda: exec("\n".join(code), globals()), number=1)
video_dir = config.get_dir("video_dir") video_dir = config.get_dir("video_dir")
except Exception as e: except Exception as e:
@ -306,9 +334,6 @@ class ManimSlidesDirective(Directive):
RevealJS(presentation_configs=presentation_configs, controls="true").convert_to( RevealJS(presentation_configs=presentation_configs, controls="true").convert_to(
destfile destfile
) )
# shutil.copyfile(filesrc, destfile)
print("CLASS NAME:", clsname)
rendered_template = jinja2.Template(TEMPLATE).render( rendered_template = jinja2.Template(TEMPLATE).render(
clsname=clsname, clsname=clsname,
@ -400,6 +425,7 @@ TEMPLATE = r"""
.. raw:: html .. raw:: html
<!-- From: https://faq.dailymotion.com/hc/en-us/articles/360022841393-How-to-preserve-the-player-aspect-ratio-on-a-responsive-page -->
<div style="position:relative;padding-bottom:56.25%;"> <div style="position:relative;padding-bottom:56.25%;">
<iframe <iframe