mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-05-18 03:05:21 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
9810425ff2 | |||
3dc543e3a6 | |||
c0c73ad4d4 | |||
a82ca81dc5 | |||
a68a4e1517 | |||
519dd47ac6 | |||
0565a99639 |
11
.github/workflows/pages.yml
vendored
11
.github/workflows/pages.yml
vendored
@ -30,10 +30,13 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Poetry
|
||||
run: pipx install poetry
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.9'
|
||||
cache: 'poetry'
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Install Linux Dependencies
|
||||
@ -41,11 +44,11 @@ jobs:
|
||||
- name: Install Python dependencies
|
||||
run: pip install manim sphinx sphinx_click furo
|
||||
- name: Install local Python package
|
||||
run: pip install -e .
|
||||
run: poetry install --with docs
|
||||
- name: Build animation and convert it into HTML slides
|
||||
run: |
|
||||
manim example.py ConvertExample
|
||||
manim-slides convert ConvertExample docs/source/_static/slides.html -cembedded=true -ccontrols=true
|
||||
poetry run manim example.py ConvertExample
|
||||
poetry run manim-slides convert ConvertExample docs/source/_static/slides.html -ccontrols=true
|
||||
- name: Build docs
|
||||
run: cd docs && make html
|
||||
- name: Upload artifact
|
||||
|
104
.github/workflows/test_examples.yml
vendored
104
.github/workflows/test_examples.yml
vendored
@ -2,6 +2,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.py'
|
||||
- '.github/workflows/test_examples.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
name: Test Examples
|
||||
@ -9,6 +10,8 @@ name: Test Examples
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
MANIM_SLIDES_VERBOSITY: debug
|
||||
PYTHONFAULTHANDLER: 1
|
||||
DISPLAY: ":99"
|
||||
|
||||
jobs:
|
||||
build-examples:
|
||||
@ -24,9 +27,6 @@ jobs:
|
||||
# Your graphics drivers do not support OpenGL 2.0.
|
||||
- os: windows-latest
|
||||
manim: manimgl
|
||||
# manimgl seems to have problems with Python 3.11
|
||||
- manim: manimgl
|
||||
pyversion: '3.11'
|
||||
# We only test Python 3.11 on Windows and MacOS
|
||||
- os: windows-latest
|
||||
pyversion: '3.8'
|
||||
@ -46,64 +46,72 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Poetry
|
||||
run: pipx install poetry
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.pyversion }}
|
||||
- name: Append to Path on MacOS and Ubuntu
|
||||
if: matrix.os == 'macos-latest' || matrix.os == 'ubuntu-latest'
|
||||
cache: 'poetry'
|
||||
|
||||
# Path related stuff
|
||||
- name: Append to Path on MacOS
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
||||
echo "/Users/runner/Library/Python/${{ matrix.pyversion }}/bin" >> $GITHUB_PATH
|
||||
- name: Append to Path on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
||||
- name: Append to Path on Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: echo "${HOME}/.local/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Install MacOS dependencies (manim only)
|
||||
|
||||
# OS depedencies
|
||||
- name: Install manim dependencies on MacOs
|
||||
if: matrix.os == 'macos-latest' && matrix.manim == 'manim'
|
||||
run: brew install py3cairo
|
||||
- name: Install MacOS dependencies
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: brew install ffmpeg py3cairo
|
||||
- name: Install manimgl dependencies on MacOS
|
||||
if: matrix.os == 'macos-latest' && matrix.manim == 'manimgl'
|
||||
run: brew install ffmpeg
|
||||
- name: Install Ubuntu dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev xvfb
|
||||
- name: Install manim dependencies on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manim'
|
||||
run: |
|
||||
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
||||
- name: Install manimgl dependencies on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
||||
run: |
|
||||
sudo apt-get install libpango1.0-dev ffmpeg freeglut3-dev
|
||||
- name: Install xvfb on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
||||
run: |
|
||||
sudo apt-get install xvfb
|
||||
nohup Xvfb $DISPLAY &
|
||||
- name: Install Windows dependencies
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: choco install ffmpeg
|
||||
- name: Install manim on MacOs
|
||||
if: matrix.manim == 'manim' && matrix.os == 'macos-latest'
|
||||
run: pip3 install --user manim
|
||||
- name: Install manim on Ubuntu and Windows
|
||||
if: matrix.manim == 'manim' && (matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest')
|
||||
run: python -m pip install --user manim
|
||||
- name: Install manimgl on MacOs
|
||||
if: matrix.manim == 'manimgl' && matrix.os == 'macos-latest'
|
||||
run: pip3 install --user manimgl
|
||||
- name: Install manimgl on Ubuntu and Windows
|
||||
if: matrix.manim == 'manimgl' && matrix.os != 'macos-latest'
|
||||
run: python -m pip install --user manimgl
|
||||
- name: Install manim-slides on MacOS
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: pip3 install --user .
|
||||
- name: Install manim-slides on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: xvfb-run -a -s "-screen 0 1400x900x24" python -m pip install --user .
|
||||
- name: Install manim-slides on Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: pip3 install -e .
|
||||
- name: Build slides with manim
|
||||
|
||||
# Install Manim Slides
|
||||
- name: Install Manim Slides
|
||||
run: |
|
||||
poetry config experimental.new-installer false
|
||||
poetry install --with test
|
||||
|
||||
# Render slides
|
||||
- name: Render slides
|
||||
if: matrix.manim == 'manim'
|
||||
run: python -m manim -ql example.py Example ThreeDExample ConvertExample
|
||||
- name: Build slides with manimgl on Ubuntu
|
||||
if: matrix.manim == 'manimgl' && matrix.os == 'ubuntu-latest'
|
||||
run: xvfb-run -a -s "-screen 0 1400x900x24" manim-render -l example.py Example ThreeDExample
|
||||
- name: Build slides with manimgl on MacOS or Windows
|
||||
if: matrix.manim == 'manimgl' && (matrix.os == 'macos-latest' || matrix.os == 'windows-latest')
|
||||
run: manimgl -l example.py Example ThreeDExample
|
||||
- name: Test slides on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: xvfb-run -a -s "-screen 0 1400x900x24" manim-slides Example ThreeDExample --skip-all
|
||||
- name: Test slides on MacOS or Windows
|
||||
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
|
||||
run: manim-slides Example ThreeDExample --skip-all
|
||||
run: poetry run manim -ql example.py Example ThreeDExample
|
||||
- name: Render slides
|
||||
if: matrix.manim == 'manimgl'
|
||||
run: poetry run -v manimgl -l example.py Example ThreeDExample
|
||||
|
||||
# Play slides
|
||||
- name: Test slides
|
||||
run: poetry run manim-slides Example ThreeDExample --skip-all
|
||||
|
||||
# Test slides to html
|
||||
- name: Test convert on Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manim'
|
||||
run: manim-slides convert --to=html ConvertExample index.html
|
||||
run: |
|
||||
poetry run manim -ql example.py ConvertExample
|
||||
poetry run manim-slides convert --to=html ConvertExample index.html
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -23,3 +23,7 @@ docs/build/
|
||||
docs/source/_static/slides_assets/
|
||||
|
||||
docs/source/_static/slides.html
|
||||
|
||||
slides_assets/
|
||||
|
||||
slides.html
|
||||
|
41
README.md
41
README.md
@ -16,8 +16,9 @@ Tool for live presentations using either [Manim (community edition)](https://www
|
||||
- [Usage](#usage)
|
||||
* [Basic Example](#basic-example)
|
||||
* [Key Bindings](#key-bindings)
|
||||
* [Interactive Tutorial](#interactive-tutorial)
|
||||
* [Other Examples](#other-examples)
|
||||
- [Features and Comparison with Original manim-presentation](#features-and-comparison-with-original-manim-presentation)
|
||||
- [Comparison with Similar Tools](#comparison-with-similar-tools)
|
||||
- [F.A.Q](#faq)
|
||||
* [How to increase quality on Windows](#how-to-increase-quality-on-windows)
|
||||
- [Contributing](#contributing)
|
||||
@ -134,6 +135,12 @@ manim-slides init
|
||||
|
||||
> **_NOTE:_** `manim-slides` uses key codes, which are platform dependent. Using the configuration wizard is therefore highly recommended.
|
||||
|
||||
## Interactive Tutorial
|
||||
|
||||
Click on the image to watch a slides presentation that explains you how to use Manim Slides.
|
||||
|
||||
[](https://eertmans.be/manim-slides/)
|
||||
|
||||
## Other Examples
|
||||
|
||||
Other examples are available in the [`example.py`](https://github.com/jeertmans/manim-slides/blob/main/example.py) file, if you downloaded the git repository.
|
||||
@ -143,27 +150,21 @@ Below is a small recording of me playing with the slides back and forth.
|
||||

|
||||
|
||||
|
||||
## Features and Comparison with original manim-presentation
|
||||
## Comparison with Similar Tools
|
||||
|
||||
Below is a non-exhaustive list of features:
|
||||
There exists are variety of tools that allows to create slides presentations containing Manim animations.
|
||||
|
||||
| Feature | `manim-slides` | `manim-presentation` |
|
||||
|:--------|:--------------:|:--------------------:|
|
||||
| Support for Manim | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Support for ManimGL | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Configurable key bindings | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Configurable paths | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Play / Pause slides | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Next / Previous slide | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Replay slide | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Reverse slide | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Multiple key per actions | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| One command line tool | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Robust config file parsing | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Support for 3D Scenes | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Documented code | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Tested on Unix, macOS, and Windows | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Hide mouse cursor | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
Below is a comparison of the most used ones with Manim Slides:
|
||||
|
||||
| Project name | Manim Slides | Manim Presentation | Manim Editor | Jupyter Notebooks |
|
||||
|:------------:|:------------:|:------------------:|:------------:|:-----------------:|
|
||||
| Link | [](https://github.com/jeertmans/manim-slides) | [](https://github.com/galatolofederico/manim-presentation) | [](https://github.com/ManimCommunity/manim_editor) | [](https://github.com/jupyter/notebook) |
|
||||
| Activity | [](https://github.com/jeertmans/manim-slides) | [](https://github.com/galatolofederico/manim-presentation) | [](https://github.com/ManimCommunity/manim_editor) | [](https://github.com/jupyter/notebook) |
|
||||
| Usage | Command-line | Command-line | Web Browser | Notebook |
|
||||
| Note | Requires minimal modif. in scenes files | Requires minimal modif. in scenes files | Requires the usage of sections, and configuration through graphical interface | Relies on `nbconvert` to create slides from a Notebook |
|
||||
| Support for ManimGL | Yes | No | No | No |
|
||||
| Web Browser presentations | Yes | No | Yes | No |
|
||||
| Offline presentations | Yes, with Qt | Yes, with OpenCV | No | No
|
||||
|
||||
## F.A.Q
|
||||
|
||||
|
@ -1 +1 @@
|
||||
__version__ = "4.7.0"
|
||||
__version__ = "4.8.0"
|
||||
|
@ -1,12 +1,12 @@
|
||||
import os
|
||||
import webbrowser
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, Dict, Generator, List, Type
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Type, Union
|
||||
|
||||
import click
|
||||
import pkg_resources
|
||||
from click import Context, Parameter
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, PositiveInt, ValidationError
|
||||
|
||||
from .commons import folder_path_option, verbosity_option
|
||||
from .config import PresentationConfig
|
||||
@ -34,14 +34,21 @@ def validate_config_option(
|
||||
class Converter(BaseModel): # type: ignore
|
||||
presentation_configs: List[PresentationConfig] = []
|
||||
assets_dir: str = "{basename}_assets"
|
||||
template: Optional[str] = None
|
||||
|
||||
def convert_to(self, dest: str) -> None:
|
||||
"""Converts self, i.e., a list of presentations, into a given format."""
|
||||
raise NotImplementedError
|
||||
|
||||
def load_template(self) -> str:
|
||||
"""Returns the template as a string.
|
||||
|
||||
An empty string is returned if no template is used."""
|
||||
return ""
|
||||
|
||||
def open(self, file: str) -> bool:
|
||||
"""Opens a file, generated with converter, using appropriate application."""
|
||||
return webbrowser.open(file)
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, s: str) -> Type["Converter"]:
|
||||
@ -51,11 +58,126 @@ class Converter(BaseModel): # type: ignore
|
||||
}[s]
|
||||
|
||||
|
||||
class JSBool(str, Enum):
|
||||
class Str(str):
|
||||
"""A simple string, but quoted when needed."""
|
||||
|
||||
# This fixes pickling issue on Python 3.8
|
||||
__reduce_ex__ = str.__reduce_ex__
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Ensures that the string is correctly quoted."""
|
||||
if self in ["true", "false", "null"]:
|
||||
return super().__str__()
|
||||
else:
|
||||
return f"'{super().__str__()}'"
|
||||
|
||||
|
||||
Function = str # Basically, anything
|
||||
|
||||
|
||||
class JsTrue(str, Enum):
|
||||
true = "true"
|
||||
|
||||
|
||||
class JsFalse(str, Enum):
|
||||
false = "false"
|
||||
|
||||
|
||||
class JsBool(Str, Enum): # type: ignore
|
||||
true = "true"
|
||||
false = "false"
|
||||
|
||||
|
||||
class JsNull(Str, Enum): # type: ignore
|
||||
null = "null"
|
||||
|
||||
|
||||
class ControlsLayout(Str, Enum): # type: ignore
|
||||
edges = "edges"
|
||||
bottom_right = "bottom-right"
|
||||
|
||||
|
||||
class ControlsBackArrows(Str, Enum): # type: ignore
|
||||
faded = "faded"
|
||||
hidden = "hidden"
|
||||
visibly = "visibly"
|
||||
|
||||
|
||||
class SlideNumber(Str, Enum): # type: ignore
|
||||
true = "true"
|
||||
false = "false"
|
||||
hdotv = "h.v"
|
||||
handv = "h/v"
|
||||
c = "c"
|
||||
candt = "c/t"
|
||||
|
||||
|
||||
class ShowSlideNumber(Str, Enum): # type: ignore
|
||||
all = "all"
|
||||
print = "print"
|
||||
speaker = "speaker"
|
||||
|
||||
|
||||
class KeyboardCondition(Str, Enum): # type: ignore
|
||||
null = "null"
|
||||
focused = "focused"
|
||||
|
||||
|
||||
class NavigationMode(Str, Enum): # type: ignore
|
||||
default = "default"
|
||||
linear = "linear"
|
||||
grid = "grid"
|
||||
|
||||
|
||||
class AutoPlayMedia(Str, Enum): # type: ignore
|
||||
null = "null"
|
||||
true = "true"
|
||||
false = "false"
|
||||
|
||||
|
||||
PreloadIframes = AutoPlayMedia
|
||||
|
||||
|
||||
class AutoAnimateMatcher(Str, Enum): # type: ignore
|
||||
null = "null"
|
||||
|
||||
|
||||
class AutoAnimateEasing(Str, Enum): # type: ignore
|
||||
ease = "ease"
|
||||
|
||||
|
||||
AutoSlide = Union[PositiveInt, JsFalse]
|
||||
|
||||
|
||||
class AutoSlideMethod(Str, Enum): # type: ignore
|
||||
null = "null"
|
||||
|
||||
|
||||
MouseWheel = Union[JsNull, float]
|
||||
|
||||
|
||||
class Transition(Str, Enum): # type: ignore
|
||||
none = "none"
|
||||
fade = "fade"
|
||||
slide = "slide"
|
||||
convex = "convex"
|
||||
concave = "concave"
|
||||
zoom = "zoom"
|
||||
|
||||
|
||||
class TransitionSpeed(Str, Enum): # type: ignore
|
||||
default = "default"
|
||||
fast = "fast"
|
||||
slow = "slow"
|
||||
|
||||
|
||||
BackgroundTransition = Transition
|
||||
|
||||
|
||||
class Display(Str, Enum): # type: ignore
|
||||
block = "block"
|
||||
|
||||
|
||||
class RevealTheme(str, Enum):
|
||||
black = "black"
|
||||
white = "white"
|
||||
@ -71,21 +193,90 @@ class RevealTheme(str, Enum):
|
||||
|
||||
|
||||
class RevealJS(Converter):
|
||||
background_color: str = "black"
|
||||
controls: JSBool = JSBool.false
|
||||
embedded: JSBool = JSBool.false
|
||||
fragments: JSBool = JSBool.false
|
||||
height: str = "100%"
|
||||
loop: JSBool = JSBool.false
|
||||
progress: JSBool = JSBool.false
|
||||
reveal_version: str = "3.7.0"
|
||||
# Presentation size options from RevealJS
|
||||
width: Union[Str, int] = Str("100%")
|
||||
height: Union[Str, int] = Str("100%")
|
||||
margin: float = 0.04
|
||||
min_scale: float = 0.2
|
||||
max_scale: float = 2.0
|
||||
# Configuration options from RevealJS
|
||||
controls: JsBool = JsBool.false
|
||||
controls_tutorial: JsBool = JsBool.true
|
||||
controls_layout: ControlsLayout = ControlsLayout.bottom_right
|
||||
controls_back_arrows: ControlsBackArrows = ControlsBackArrows.faded
|
||||
progress: JsBool = JsBool.false
|
||||
slide_number: SlideNumber = SlideNumber.false
|
||||
show_slide_number: Union[ShowSlideNumber, Function] = ShowSlideNumber.all
|
||||
hash_one_based_index: JsBool = JsBool.false
|
||||
hash: JsBool = JsBool.false
|
||||
respond_to_hash_changes: JsBool = JsBool.false
|
||||
history: JsBool = JsBool.false
|
||||
keyboard: JsBool = JsBool.true
|
||||
keyboard_condition: Union[KeyboardCondition, Function] = KeyboardCondition.null
|
||||
disable_layout: JsBool = JsBool.false
|
||||
overview: JsBool = JsBool.true
|
||||
center: JsBool = JsBool.true
|
||||
touch: JsBool = JsBool.true
|
||||
loop: JsBool = JsBool.false
|
||||
rtl: JsBool = JsBool.false
|
||||
navigation_mode: NavigationMode = NavigationMode.default
|
||||
shuffle: JsBool = JsBool.false
|
||||
fragments: JsBool = JsBool.true
|
||||
fragment_in_url: JsBool = JsBool.true
|
||||
embedded: JsBool = JsBool.false
|
||||
help: JsBool = JsBool.true
|
||||
pause: JsBool = JsBool.true
|
||||
show_notes: JsBool = JsBool.false
|
||||
auto_play_media: AutoPlayMedia = AutoPlayMedia.null
|
||||
preload_iframes: PreloadIframes = PreloadIframes.null
|
||||
auto_animate: JsBool = JsBool.true
|
||||
auto_animate_matcher: Union[AutoAnimateMatcher, Function] = AutoAnimateMatcher.null
|
||||
auto_animate_easing: AutoAnimateEasing = AutoAnimateEasing.ease
|
||||
auto_animate_duration: float = 1.0
|
||||
auto_animate_unmatched: JsBool = JsBool.true
|
||||
auto_animate_styles: List[str] = [
|
||||
"opacity",
|
||||
"color",
|
||||
"background-color",
|
||||
"padding",
|
||||
"font-size",
|
||||
"line-height",
|
||||
"letter-spacing",
|
||||
"border-width",
|
||||
"border-color",
|
||||
"border-radius",
|
||||
"outline",
|
||||
"outline-offset",
|
||||
]
|
||||
auto_slide: AutoSlide = 0
|
||||
auto_slide_stoppable: JsBool = JsBool.true
|
||||
auto_slide_method: Union[AutoSlideMethod, Function] = AutoSlideMethod.null
|
||||
default_timing: Union[JsNull, int] = JsNull.null
|
||||
mouse_wheel: JsBool = JsBool.false
|
||||
preview_links: JsBool = JsBool.false
|
||||
post_message: JsBool = JsBool.true
|
||||
post_message_events: JsBool = JsBool.false
|
||||
focus_body_on_page_visibility_change: JsBool = JsBool.true
|
||||
transition: Transition = Transition.none
|
||||
transition_speed: TransitionSpeed = TransitionSpeed.default
|
||||
background_transition: BackgroundTransition = BackgroundTransition.none
|
||||
pdf_max_pages_per_slide: Union[int, str] = "Number.POSITIVE_INFINITY"
|
||||
pdf_separate_fragments: JsBool = JsBool.true
|
||||
pdf_page_height_offset: int = -1
|
||||
view_distance: int = 3
|
||||
mobile_view_distance: int = 2
|
||||
display: Display = Display.block
|
||||
hide_inactive_cursor: JsBool = JsBool.true
|
||||
hide_cursor_time: int = 5000
|
||||
# Add. options
|
||||
background_color: str = "black" # TODO: use pydantic.color.Color
|
||||
reveal_version: str = "4.4.0"
|
||||
reveal_theme: RevealTheme = RevealTheme.black
|
||||
shuffle: JSBool = JSBool.false
|
||||
title: str = "Manim Slides"
|
||||
width: str = "100%"
|
||||
|
||||
class Config:
|
||||
use_enum_values = True
|
||||
extra = "forbid"
|
||||
|
||||
def get_sections_iter(self) -> Generator[str, None, None]:
|
||||
"""Generates a sequence of sections, one per slide, that will be included into the html template."""
|
||||
@ -94,17 +285,27 @@ class RevealJS(Converter):
|
||||
file = presentation_config.files[slide_config.start_animation]
|
||||
file = os.path.join(self.assets_dir, os.path.basename(file))
|
||||
|
||||
# TODO: document this
|
||||
# Videos are muted because, otherwise, the first slide never plays correctly.
|
||||
# This is due to a restriction in playing audio without the user doing anything.
|
||||
# Later, this might be useful to only mute the first video, or to make it optional.
|
||||
if slide_config.is_loop():
|
||||
yield f'<section data-background-video="{file}" data-background-video-loop></section>'
|
||||
yield f'<section data-background-video="{file}" data-background-video-muted data-background-video-loop></section>'
|
||||
else:
|
||||
yield f'<section data-background-video="{file}"></section>'
|
||||
yield f'<section data-background-video="{file}" data-background-video-muted></section>'
|
||||
|
||||
def load_template(self) -> str:
|
||||
"""Returns the RevealJS HTML template as a string."""
|
||||
if isinstance(self.template, str):
|
||||
with open(self.template, "r") as f:
|
||||
return f.read()
|
||||
return pkg_resources.resource_string(
|
||||
__name__, "data/revealjs_template.html"
|
||||
).decode()
|
||||
|
||||
def open(self, file: str) -> bool:
|
||||
return webbrowser.open(file)
|
||||
|
||||
def convert_to(self, dest: str) -> None:
|
||||
"""Converts this configuration into a RevealJS HTML presentation, saved to DEST."""
|
||||
dirname = os.path.dirname(dest)
|
||||
@ -138,19 +339,13 @@ def show_config_options(function: Callable[..., Any]) -> Callable[..., Any]:
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
|
||||
to = ctx.params.get("to")
|
||||
to = ctx.params.get("to", "html")
|
||||
|
||||
if to:
|
||||
converter = Converter.from_string(to)(scenes=[])
|
||||
for key, value in converter.dict().items():
|
||||
click.echo(f"{key}: {repr(value)}")
|
||||
converter = Converter.from_string(to)(presentation_configs=[])
|
||||
for key, value in converter.dict().items():
|
||||
click.echo(f"{key}: {repr(value)}")
|
||||
|
||||
ctx.exit()
|
||||
|
||||
else:
|
||||
raise click.UsageError(
|
||||
"Using --show-config option requires to first specify --to option."
|
||||
)
|
||||
ctx.exit()
|
||||
|
||||
return click.option(
|
||||
"--show-config",
|
||||
@ -163,6 +358,35 @@ def show_config_options(function: Callable[..., Any]) -> Callable[..., Any]:
|
||||
)(function)
|
||||
|
||||
|
||||
def show_template_option(function: Callable[..., Any]) -> Callable[..., Any]:
|
||||
"""Wraps a function to add a `--show-template` option."""
|
||||
|
||||
def callback(ctx: Context, param: Parameter, value: bool) -> None:
|
||||
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
|
||||
to = ctx.params.get("to", "html")
|
||||
template = ctx.params.get("template", None)
|
||||
|
||||
converter = Converter.from_string(to)(
|
||||
presentation_configs=[], template=template
|
||||
)
|
||||
click.echo(converter.load_template())
|
||||
|
||||
ctx.exit()
|
||||
|
||||
return click.option(
|
||||
"--show-template",
|
||||
is_flag=True,
|
||||
help="Show the template (currently) used for a given conversion format and exit.",
|
||||
default=None,
|
||||
expose_value=False,
|
||||
show_envvar=True,
|
||||
callback=callback,
|
||||
)(function)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("scenes", nargs=-1)
|
||||
@folder_path_option
|
||||
@ -187,8 +411,16 @@ def show_config_options(function: Callable[..., Any]) -> Callable[..., Any]:
|
||||
"config_options",
|
||||
multiple=True,
|
||||
callback=validate_config_option,
|
||||
help="Configuration options passed to the converter. E.g., pass `-cbackground_color=red` to set the background color to red (if supported).",
|
||||
help="Configuration options passed to the converter. E.g., pass `-cslide_number=true` to display slide numbers.",
|
||||
)
|
||||
@click.option(
|
||||
"--use-template",
|
||||
"template",
|
||||
metavar="FILE",
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
help="Use the template given by FILE instead of default one. To echo the default template, use `--show-template`.",
|
||||
)
|
||||
@show_template_option
|
||||
@show_config_options
|
||||
@verbosity_option
|
||||
def convert(
|
||||
@ -199,6 +431,7 @@ def convert(
|
||||
open_result: bool,
|
||||
force: bool,
|
||||
config_options: Dict[str, str],
|
||||
template: Optional[str],
|
||||
) -> None:
|
||||
"""
|
||||
Convert SCENE(s) into a given format and writes the result in DEST.
|
||||
@ -206,11 +439,29 @@ def convert(
|
||||
|
||||
presentation_configs = get_scenes_presentation_config(scenes, folder)
|
||||
|
||||
converter = Converter.from_string(to)(
|
||||
presentation_configs=presentation_configs, **config_options
|
||||
)
|
||||
try:
|
||||
converter = Converter.from_string(to)(
|
||||
presentation_configs=presentation_configs,
|
||||
template=template,
|
||||
**config_options,
|
||||
)
|
||||
|
||||
converter.convert_to(dest)
|
||||
converter.convert_to(dest)
|
||||
|
||||
if open_result:
|
||||
converter.open(dest)
|
||||
if open_result:
|
||||
converter.open(dest)
|
||||
|
||||
except ValidationError as e:
|
||||
|
||||
errors = e.errors()
|
||||
|
||||
msg = [
|
||||
f"{len(errors)} error(s) occured with configuration options for '{to}', see below."
|
||||
]
|
||||
|
||||
for error in errors:
|
||||
option = error["loc"][0]
|
||||
_msg = error["msg"]
|
||||
msg.append(f"Option '{option}': {_msg}")
|
||||
|
||||
raise click.UsageError("\n".join(msg))
|
||||
|
@ -1,185 +1,291 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<title>{title}</title>
|
||||
<title>{title}</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/css/reveal.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/css/theme/{reveal_theme}.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/{reveal_version}/reveal.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/{reveal_version}/theme/{reveal_theme}.min.css">
|
||||
|
||||
<!-- Theme used for syntax highlighting of code -->
|
||||
<!-- <link rel="stylesheet" href="lib/css/zenburn.css"> -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/zenburn.min.css">
|
||||
<!-- Theme used for syntax highlighting of code -->
|
||||
<!-- <link rel="stylesheet" href="lib/css/zenburn.css"> -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/zenburn.min.css">
|
||||
|
||||
<!-- Printing and PDF exports -->
|
||||
<script>
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = window.location.search.match(/print-pdf/gi) ? 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/css/print/pdf.css' : 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/css/print/paper.css';
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
</script>
|
||||
<!-- <link rel="stylesheet" href="index.css"> -->
|
||||
</head>
|
||||
<!-- <link rel="stylesheet" href="index.css"> -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
{sections}
|
||||
</div>
|
||||
</div>
|
||||
<body>
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
{sections}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<script src="lib/js/head.min.js"></script>-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/headjs@1.0.3/dist/1.0.0/head.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/js/reveal.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/{reveal_version}/reveal.min.js"></script>
|
||||
|
||||
<!-- <script src="index.js"></script> -->
|
||||
<script>
|
||||
// More info about config & dependencies:
|
||||
// - https://github.com/hakimel/reveal.js#configuration
|
||||
// - https://github.com/hakimel/reveal.js#dependencies
|
||||
Reveal.initialize({{
|
||||
// Display controls in the bottom right corner
|
||||
controls: {controls},
|
||||
<!-- To include plugins, see: https://revealjs.com/plugins/ -->
|
||||
|
||||
width: '{width}',
|
||||
height: '{height}',
|
||||
<!-- <script src="index.js"></script> -->
|
||||
<script>
|
||||
Reveal.initialize({{
|
||||
|
||||
// Display a presentation progress bar
|
||||
progress: {progress},
|
||||
// The "normal" size of the presentation, aspect ratio will
|
||||
// be preserved when the presentation is scaled to fit different
|
||||
// resolutions. Can be specified using percentage units.
|
||||
width: {width},
|
||||
height: {height},
|
||||
|
||||
// Set default timing of 2 minutes per slide
|
||||
defaultTiming: 120,
|
||||
// Factor of the display size that should remain empty around
|
||||
// the content
|
||||
margin: {margin},
|
||||
|
||||
// Display the page number of the current slide
|
||||
slideNumber: true,
|
||||
// Bounds for smallest/largest possible scale to apply to content
|
||||
minScale: {min_scale},
|
||||
maxScale: {max_scale},
|
||||
|
||||
// Push each slide change to the browser history
|
||||
history: false,
|
||||
// Display presentation control arrows
|
||||
controls: {controls},
|
||||
|
||||
// Enable keyboard shortcuts for navigation
|
||||
keyboard: true,
|
||||
// Help the user learn the controls by providing hints, for example by
|
||||
// bouncing the down arrow when they first encounter a vertical slide
|
||||
controlsTutorial: {controls_tutorial},
|
||||
|
||||
// Enable the slide overview mode
|
||||
overview: true,
|
||||
// Determines where controls appear, "edges" or "bottom-right"
|
||||
controlsLayout: {controls_layout},
|
||||
|
||||
// Vertical centering of slides
|
||||
center: true,
|
||||
// Visibility rule for backwards navigation arrows; "faded", "hidden"
|
||||
// or "visible"
|
||||
controlsBackArrows: {controls_back_arrows},
|
||||
|
||||
// Enables touch navigation on devices with touch input
|
||||
touch: true,
|
||||
// Display a presentation progress bar
|
||||
progress: {progress},
|
||||
|
||||
// Loop the presentation
|
||||
loop: {loop},
|
||||
// Display the page number of the current slide
|
||||
// - true: Show slide number
|
||||
// - false: Hide slide number
|
||||
//
|
||||
// Can optionally be set as a string that specifies the number formatting:
|
||||
// - "h.v": Horizontal . vertical slide number (default)
|
||||
// - "h/v": Horizontal / vertical slide number
|
||||
// - "c": Flattened slide number
|
||||
// - "c/t": Flattened slide number / total slides
|
||||
//
|
||||
// Alternatively, you can provide a function that returns the slide
|
||||
// number for the current slide. The function should take in a slide
|
||||
// object and return an array with one string [slideNumber] or
|
||||
// three strings [n1,delimiter,n2]. See #formatSlideNumber().
|
||||
slideNumber: {slide_number},
|
||||
|
||||
// Change the presentation direction to be RTL
|
||||
rtl: false,
|
||||
// Can be used to limit the contexts in which the slide number appears
|
||||
// - "all": Always show the slide number
|
||||
// - "print": Only when printing to PDF
|
||||
// - "speaker": Only in the speaker view
|
||||
showSlideNumber: {show_slide_number},
|
||||
|
||||
// Randomizes the order of slides each time the presentation loads
|
||||
shuffle: {shuffle},
|
||||
// Use 1 based indexing for # links to match slide number (default is zero
|
||||
// based)
|
||||
hashOneBasedIndex: {hash_one_based_index},
|
||||
|
||||
// Turns fragments on and off globally
|
||||
fragments: {fragments},
|
||||
// Add the current slide number to the URL hash so that reloading the
|
||||
// page/copying the URL will return you to the same slide
|
||||
hash: {hash},
|
||||
|
||||
// Flags if the presentation is running in an embedded mode,
|
||||
// i.e. contained within a limited portion of the screen
|
||||
embedded: {embedded},
|
||||
// Flags if we should monitor the hash and change slides accordingly
|
||||
respondToHashChanges: {respond_to_hash_changes},
|
||||
|
||||
// Flags if we should show a help overlay when the questionmark
|
||||
// key is pressed
|
||||
help: true,
|
||||
// Push each slide change to the browser history. Implies `hash: true`
|
||||
history: {history},
|
||||
|
||||
// Flags if speaker notes should be visible to all viewers
|
||||
showNotes: false,
|
||||
// Enable keyboard shortcuts for navigation
|
||||
keyboard: {keyboard},
|
||||
|
||||
// Global override for autolaying embedded media (video/audio/iframe)
|
||||
// - null: Media will only autoplay if data-autoplay is present
|
||||
// - true: All media will autoplay, regardless of individual setting
|
||||
// - false: No media will autoplay, regardless of individual setting
|
||||
autoPlayMedia: null,
|
||||
// Optional function that blocks keyboard events when retuning false
|
||||
//
|
||||
// If you set this to 'focused', we will only capture keyboard events
|
||||
// for embedded decks when they are in focus
|
||||
keyboardCondition: {keyboard_condition},
|
||||
|
||||
// Number of milliseconds between automatically proceeding to the
|
||||
// next slide, disabled when set to 0, this value can be overwritten
|
||||
// by using a data-autoslide attribute on your slides
|
||||
autoSlide: 0,
|
||||
// Disables the default reveal.js slide layout (scaling and centering)
|
||||
// so that you can use custom CSS layout
|
||||
disableLayout: {disable_layout},
|
||||
|
||||
// Stop auto-sliding after user input
|
||||
autoSlideStoppable: true,
|
||||
// Enable the slide overview mode
|
||||
overview: {overview},
|
||||
|
||||
// Use this method for navigation when auto-sliding
|
||||
autoSlideMethod: Reveal.navigateNext,
|
||||
// Vertical centering of slides
|
||||
center: {center},
|
||||
|
||||
// Enable slide navigation via mouse wheel
|
||||
mouseWheel: false,
|
||||
// Enables touch navigation on devices with touch input
|
||||
touch: {touch},
|
||||
|
||||
// Hides the address bar on mobile devices
|
||||
hideAddressBar: true,
|
||||
// Loop the presentation
|
||||
loop: {loop},
|
||||
|
||||
// Opens links in an iframe preview overlay
|
||||
previewLinks: true,
|
||||
// Change the presentation direction to be RTL
|
||||
rtl: {rtl},
|
||||
|
||||
// Transition style
|
||||
transition: 'none', // none/fade/slide/convex/concave/zoom
|
||||
// Changes the behavior of our navigation directions.
|
||||
//
|
||||
// "default"
|
||||
// Left/right arrow keys step between horizontal slides, up/down
|
||||
// arrow keys step between vertical slides. Space key steps through
|
||||
// all slides (both horizontal and vertical).
|
||||
//
|
||||
// "linear"
|
||||
// Removes the up/down arrows. Left/right arrows step through all
|
||||
// slides (both horizontal and vertical).
|
||||
//
|
||||
// "grid"
|
||||
// When this is enabled, stepping left/right from a vertical stack
|
||||
// to an adjacent vertical stack will land you at the same vertical
|
||||
// index.
|
||||
//
|
||||
// Consider a deck with six slides ordered in two vertical stacks:
|
||||
// 1.1 2.1
|
||||
// 1.2 2.2
|
||||
// 1.3 2.3
|
||||
//
|
||||
// If you're on slide 1.3 and navigate right, you will normally move
|
||||
// from 1.3 -> 2.1. If "grid" is used, the same navigation takes you
|
||||
// from 1.3 -> 2.3.
|
||||
navigationMode: {navigation_mode},
|
||||
|
||||
// Transition speed
|
||||
transitionSpeed: 'default', // default/fast/slow
|
||||
// Randomizes the order of slides each time the presentation loads
|
||||
shuffle: {shuffle},
|
||||
|
||||
// Transition style for full page slide backgrounds
|
||||
backgroundTransition: 'none', // none/fade/slide/convex/concave/zoom
|
||||
// Turns fragments on and off globally
|
||||
fragments: {fragments},
|
||||
|
||||
// Number of slides away from the current that are visible
|
||||
viewDistance: 3,
|
||||
// Flags whether to include the current fragment in the URL,
|
||||
// so that reloading brings you to the same fragment position
|
||||
fragmentInURL: {fragment_in_url},
|
||||
|
||||
// Parallax background image
|
||||
parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
|
||||
// Flags if the presentation is running in an embedded mode,
|
||||
// i.e. contained within a limited portion of the screen
|
||||
embedded: {embedded},
|
||||
|
||||
// Parallax background size
|
||||
parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px"
|
||||
// Flags if we should show a help overlay when the question-mark
|
||||
// key is pressed
|
||||
help: {help},
|
||||
|
||||
// Number of pixels to move the parallax background per slide
|
||||
// - Calculated automatically unless specified
|
||||
// - Set to 0 to disable movement along an axis
|
||||
parallaxBackgroundHorizontal: null,
|
||||
parallaxBackgroundVertical: null,
|
||||
// Flags if it should be possible to pause the presentation (blackout)
|
||||
pause: {pause},
|
||||
|
||||
// Flags if speaker notes should be visible to all viewers
|
||||
showNotes: {show_notes},
|
||||
|
||||
// Global override for autolaying embedded media (video/audio/iframe)
|
||||
// - null: Media will only autoplay if data-autoplay is present
|
||||
// - true: All media will autoplay, regardless of individual setting
|
||||
// - false: No media will autoplay, regardless of individual setting
|
||||
autoPlayMedia: {auto_play_media},
|
||||
|
||||
// Global override for preloading lazy-loaded iframes
|
||||
// - null: Iframes with data-src AND data-preload will be loaded when within
|
||||
// the viewDistance, iframes with only data-src will be loaded when visible
|
||||
// - true: All iframes with data-src will be loaded when within the viewDistance
|
||||
// - false: All iframes with data-src will be loaded only when visible
|
||||
preloadIframes: {preload_iframes},
|
||||
|
||||
// Can be used to globally disable auto-animation
|
||||
autoAnimate: {auto_animate},
|
||||
|
||||
// Optionally provide a custom element matcher that will be
|
||||
// used to dictate which elements we can animate between.
|
||||
autoAnimateMatcher: {auto_animate_matcher},
|
||||
|
||||
// Default settings for our auto-animate transitions, can be
|
||||
// overridden per-slide or per-element via data arguments
|
||||
autoAnimateEasing: {auto_animate_easing},
|
||||
autoAnimateDuration: {auto_animate_duration},
|
||||
autoAnimateUnmatched: {auto_animate_unmatched},
|
||||
|
||||
// CSS properties that can be auto-animated. Position & scale
|
||||
// is matched separately so there's no need to include styles
|
||||
// like top/right/bottom/left, width/height or margin.
|
||||
autoAnimateStyles: {auto_animate_styles},
|
||||
|
||||
// Controls automatic progression to the next slide
|
||||
// - 0: Auto-sliding only happens if the data-autoslide HTML attribute
|
||||
// is present on the current slide or fragment
|
||||
// - 1+: All slides will progress automatically at the given interval
|
||||
// - false: No auto-sliding, even if data-autoslide is present
|
||||
autoSlide: {auto_slide},
|
||||
|
||||
// Stop auto-sliding after user input
|
||||
autoSlideStoppable: {auto_slide_stoppable},
|
||||
|
||||
// Use this method for navigation when auto-sliding (defaults to navigateNext)
|
||||
autoSlideMethod: {auto_slide_method},
|
||||
|
||||
// Specify the average time in seconds that you think you will spend
|
||||
// presenting each slide. This is used to show a pacing timer in the
|
||||
// speaker view
|
||||
defaultTiming: {default_timing},
|
||||
|
||||
// Enable slide navigation via mouse wheel
|
||||
mouseWheel: {mouse_wheel},
|
||||
|
||||
// Opens links in an iframe preview overlay
|
||||
// Add `data-preview-link` and `data-preview-link="false"` to customise each link
|
||||
// individually
|
||||
previewLinks: {preview_links},
|
||||
|
||||
// Exposes the reveal.js API through window.postMessage
|
||||
postMessage: {post_message},
|
||||
|
||||
// Dispatches all reveal.js events to the parent window through postMessage
|
||||
postMessageEvents: {post_message_events},
|
||||
|
||||
// Focuses body when page changes visibility to ensure keyboard shortcuts work
|
||||
focusBodyOnPageVisibilityChange: {focus_body_on_page_visibility_change},
|
||||
|
||||
// Transition style
|
||||
transition: {transition}, // none/fade/slide/convex/concave/zoom
|
||||
|
||||
// Transition speed
|
||||
transitionSpeed: {transition_speed}, // default/fast/slow
|
||||
|
||||
// Transition style for full page slide backgrounds
|
||||
backgroundTransition: {background_transition}, // none/fade/slide/convex/concave/zoom
|
||||
|
||||
// The maximum number of pages a single slide can expand onto when printing
|
||||
// to PDF, unlimited by default
|
||||
pdfMaxPagesPerSlide: {pdf_max_pages_per_slide},
|
||||
|
||||
// Prints each fragment on a separate slide
|
||||
pdfSeparateFragments: {pdf_separate_fragments},
|
||||
|
||||
// Offset used to reduce the height of content within exported PDF pages.
|
||||
// This exists to account for environment differences based on how you
|
||||
// print to PDF. CLI printing options, like phantomjs and wkpdf, can end
|
||||
// on precisely the total height of the document whereas in-browser
|
||||
// printing has to end one pixel before.
|
||||
pdfPageHeightOffset: {pdf_page_height_offset},
|
||||
|
||||
// Number of slides away from the current that are visible
|
||||
viewDistance: {view_distance},
|
||||
|
||||
// Number of slides away from the current that are visible on mobile
|
||||
// devices. It is advisable to set this to a lower number than
|
||||
// viewDistance in order to save resources.
|
||||
mobileViewDistance: {mobile_view_distance},
|
||||
|
||||
// The display mode that will be used to show slides
|
||||
display: {display},
|
||||
|
||||
// Hide cursor if inactive
|
||||
hideInactiveCursor: {hide_inactive_cursor},
|
||||
|
||||
// Time before the cursor is hidden (in ms)
|
||||
hideCursorTime: {hide_cursor_time}
|
||||
|
||||
|
||||
// The display mode that will be used to show slides
|
||||
display: 'block',
|
||||
|
||||
/*
|
||||
multiplex: {{
|
||||
// Example values. To generate your own, see the socket.io server instructions.
|
||||
secret: '13652805320794272084', // Obtained from the socket.io server. Gives this (the master) control of the presentation
|
||||
id: '1ea875674b17ca76', // Obtained from socket.io server
|
||||
url: 'https://reveal-js-multiplex-ccjbegmaii.now.sh' // Location of socket.io server
|
||||
}},
|
||||
*/
|
||||
|
||||
dependencies: [
|
||||
{{ src: 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/plugin/markdown/marked.js' }},
|
||||
{{ src: 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/plugin/markdown/markdown.js' }},
|
||||
{{ src: 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/plugin/notes/notes.js', async: true }},
|
||||
{{ src: 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/plugin/highlight/highlight.js', async: true, callback: function () {{ hljs.initHighlightingOnLoad(); }} }},
|
||||
//{{ src: '//cdn.socket.io/socket.io-1.3.5.js', async: true }},
|
||||
//{{ src: 'plugin/multiplex/master.js', async: true }},
|
||||
// and if you want speaker notes
|
||||
{{ src: 'https://cdn.jsdelivr.net/npm/reveal.js@{reveal_version}/plugin/notes-server/client.js', async: true }}
|
||||
|
||||
],
|
||||
markdown: {{
|
||||
// renderer: myrenderer,
|
||||
smartypants: true
|
||||
}}
|
||||
}});
|
||||
Reveal.configure({{
|
||||
// PDF Configurations
|
||||
pdfMaxPagesPerSlide: 1
|
||||
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -2,7 +2,7 @@ import os
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
from enum import IntEnum, auto, unique
|
||||
from enum import Enum, IntEnum, auto, unique
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import click
|
||||
@ -28,9 +28,17 @@ WINDOW_NAME = "Manim Slides"
|
||||
WINDOW_INFO_NAME = f"{WINDOW_NAME}: Info"
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
|
||||
|
||||
class AspectRatio(Enum):
|
||||
ignore = Qt.IgnoreAspectRatio
|
||||
keep = Qt.KeepAspectRatio
|
||||
auto = "auto"
|
||||
|
||||
|
||||
ASPECT_RATIO_MODES = {
|
||||
"ignore": Qt.IgnoreAspectRatio,
|
||||
"keep": Qt.KeepAspectRatio,
|
||||
"ignore": AspectRatio.ignore,
|
||||
"keep": AspectRatio.keep,
|
||||
"auto": AspectRatio.auto,
|
||||
}
|
||||
|
||||
RESIZE_MODES = {
|
||||
@ -514,7 +522,7 @@ class App(QWidget): # type: ignore
|
||||
fullscreen: bool = False,
|
||||
resolution: Tuple[int, int] = (1980, 1080),
|
||||
hide_mouse: bool = False,
|
||||
aspect_ratio: Qt.AspectRatioMode = Qt.IgnoreAspectRatio,
|
||||
aspect_ratio: AspectRatio = AspectRatio.auto,
|
||||
resize_mode: Qt.TransformationMode = Qt.SmoothTransformation,
|
||||
background_color: str = "black",
|
||||
**kwargs: Any,
|
||||
@ -533,6 +541,9 @@ class App(QWidget): # type: ignore
|
||||
self.setCursor(Qt.BlankCursor)
|
||||
|
||||
self.label = QLabel(self)
|
||||
|
||||
if self.aspect_ratio == AspectRatio.auto:
|
||||
self.label.setScaledContents(True)
|
||||
self.label.setAlignment(Qt.AlignCenter)
|
||||
self.label.resize(self.display_width, self.display_height)
|
||||
self.label.setStyleSheet(f"background-color: {background_color}")
|
||||
@ -584,10 +595,11 @@ class App(QWidget): # type: ignore
|
||||
self.deleteLater()
|
||||
|
||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||
self.pixmap = self.pixmap.scaled(
|
||||
self.width(), self.height(), self.aspect_ratio, self.resize_mode
|
||||
)
|
||||
self.label.setPixmap(self.pixmap)
|
||||
if not self.label.hasScaledContents():
|
||||
self.pixmap = self.pixmap.scaled(
|
||||
self.width(), self.height(), self.aspect_ratio.value, self.resize_mode
|
||||
)
|
||||
self.label.setPixmap(self.pixmap)
|
||||
self.label.resize(self.width(), self.height())
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
@ -601,9 +613,11 @@ class App(QWidget): # type: ignore
|
||||
bytes_per_line = ch * w
|
||||
qt_img = QImage(cv_img.data, w, h, bytes_per_line, QImage.Format_BGR888)
|
||||
|
||||
if w != self.width() or h != self.height():
|
||||
if not self.label.hasScaledContents() and (
|
||||
w != self.width() or h != self.height()
|
||||
):
|
||||
qt_img = qt_img.scaled(
|
||||
self.width(), self.height(), self.aspect_ratio, self.resize_mode
|
||||
self.width(), self.height(), self.aspect_ratio.value, self.resize_mode
|
||||
)
|
||||
|
||||
self.label.setPixmap(QPixmap.fromImage(qt_img))
|
||||
@ -755,8 +769,8 @@ def get_scenes_presentation_config(
|
||||
@click.option(
|
||||
"--aspect-ratio",
|
||||
type=click.Choice(ASPECT_RATIO_MODES.keys(), case_sensitive=False),
|
||||
default="ignore",
|
||||
help="Set the aspect ratio mode to be used when rescaling video.",
|
||||
default="auto",
|
||||
help="Set the aspect ratio mode to be used when rescaling video. `'auto'` option is equivalent to `'ignore'`, but can be much faster due to not calling `scaled()` method on every frame.",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
|
2576
poetry.lock
generated
Normal file
2576
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,15 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "setuptools-scm"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
[tool.poetry]
|
||||
name = "manim-slides"
|
||||
description = "Tool for live presentations using manim"
|
||||
authors = [
|
||||
{ name = "Jérome Eertmans", email = "jeertmans@icloud.com" }
|
||||
"Jérome Eertmans <jeertmans@icloud.com>"
|
||||
]
|
||||
version = "4.7.0"
|
||||
license = {file = "LICENSE.md"}
|
||||
version = "4.8.0"
|
||||
license = "GPL-3.0-only"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
homepage = "https://github.com/jeertmans/manim-slides"
|
||||
documentation = "https://eertmans.be/manim-slides"
|
||||
repository = "https://github.com/jeertmans/manim-slides"
|
||||
|
||||
keywords = ["manim", "slides", "plugin", "manimgl"]
|
||||
|
||||
@ -28,24 +26,43 @@ classifiers = [
|
||||
"Topic :: Scientific/Engineering",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"click>=8.0",
|
||||
"click-default-group>=1.2",
|
||||
"numpy>=1.19.3",
|
||||
"opencv-python>=4.6",
|
||||
"pydantic>=1.9.1",
|
||||
"pyside6>=6",
|
||||
"requests>=2.26.0",
|
||||
"tqdm>=4.62.3",
|
||||
exclude = ["docs/","static/"]
|
||||
packages = [
|
||||
{ include = "manim_slides" },
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["manim_slides"]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/jeertmans/manim-slides"
|
||||
documentation = "https://eertmans.be/manim-slides"
|
||||
repository = "https://github.com/jeertmans/manim-slides"
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8.1,<3.12"
|
||||
click = ">=8.0"
|
||||
click-default-group = ">=1.2"
|
||||
numpy = ">=1.19"
|
||||
opencv-python = ">=4.6"
|
||||
pydantic = ">=1.9"
|
||||
pyside6 = ">=6"
|
||||
requests = ">=2.26"
|
||||
tqdm = ">=4.62"
|
||||
|
||||
[project.scripts]
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
manim = "^0.17.0"
|
||||
sphinx = "^5.3.0"
|
||||
sphinx-click = "^4.4.0"
|
||||
furo = "^2022.9.29"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^22.10.0"
|
||||
mypy = "^0.991"
|
||||
isort = "^5.10.1"
|
||||
flake8 = "^6.0.0"
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
manim = "^0.17.0"
|
||||
manimgl = "^1.6.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools","poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.plugins]
|
||||
[tool.poetry.plugins."console_scripts"]
|
||||
manim-slides = "manim_slides.__main__:cli"
|
||||
|
BIN
static/docs.png
Normal file
BIN
static/docs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 209 KiB |
Reference in New Issue
Block a user