mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-07-04 23:57:50 +08:00
Compare commits
26 Commits
v4.11.0
...
gui-scenes
Author | SHA1 | Date | |
---|---|---|---|
ea2ce6505f | |||
b3fd1d209e | |||
8c38db0989 | |||
6da0c36c96 | |||
3b01efa601 | |||
c9ef5e9a75 | |||
bfad43bd38 | |||
6f2cbc9b19 | |||
5bd88c2fd5 | |||
f0c17b1e2a | |||
fce9546a9b | |||
d6ad56120e | |||
5db0261b01 | |||
8ab33ef71f | |||
4da0e2cc2d | |||
0e82e28313 | |||
8b13106fcc | |||
bce4d8188f | |||
c420b47ad2 | |||
fad13f33dc | |||
d42a7f5ff1 | |||
88d598709a | |||
6903919767 | |||
f4971d3a43 | |||
e62a3b05ed | |||
152050612e |
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 4.11.0
|
current_version = 4.13.1
|
||||||
commit = True
|
commit = True
|
||||||
message = chore(version): bump {current_version} to {new_version}
|
message = chore(version): bump {current_version} to {new_version}
|
||||||
|
|
||||||
|
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
labels:
|
||||||
|
- dependencies
|
18
.github/workflows/pages.yml
vendored
18
.github/workflows/pages.yml
vendored
@ -46,22 +46,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
||||||
- name: Install Python dependencies
|
|
||||||
run: pip install manim sphinx sphinx_click furo
|
|
||||||
- name: Install local Python package
|
- name: Install local Python package
|
||||||
run: poetry install --with docs
|
run: poetry install --extras=manim --with docs
|
||||||
- name: Restore cached media
|
- name: Restore cached media
|
||||||
id: cache-media-restore
|
id: cache-media-restore
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
path: media
|
path: media
|
||||||
key: ${{ runner.os }}-media
|
key: ${{ runner.os }}-media
|
||||||
- name: Build animation and convert it into HTML slides
|
- name: Build animations
|
||||||
run: |
|
run: |
|
||||||
poetry run manim example.py ConvertExample BasicExample ThreeDExample
|
poetry run manim example.py ConvertExample BasicExample ThreeDExample
|
||||||
poetry run manim-slides convert ConvertExample docs/source/_static/slides.html -ccontrols=true
|
- name: Convert animations to HTML slides
|
||||||
poetry run manim-slides convert BasicExample docs/source/_static/basic_example.html -ccontrols=true
|
run: |
|
||||||
poetry run manim-slides convert ThreeDExample docs/source/_static/three_d_example.html -ccontrols=true
|
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
|
||||||
@ -82,6 +84,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
# Upload docs/build/html dir
|
# Upload docs/build/html dir
|
||||||
path: docs/build/html/
|
path: docs/build/html/
|
||||||
|
- name: Show docs/build/html/_static/ dir content (video only)
|
||||||
|
run: tree -L 3 docs/build/html/_static/ -P '*.mp4'
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
|
50
.github/workflows/python-publish.yml
vendored
50
.github/workflows/python-publish.yml
vendored
@ -1,4 +1,3 @@
|
|||||||
# Modified from: https://github.com/pypa/cibuildwheel
|
|
||||||
name: Upload Python Package
|
name: Upload Python Package
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -8,41 +7,28 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_wheels:
|
build_and_release:
|
||||||
name: Build wheels on ${{ matrix.os }}
|
name: Build and release
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-python@v2
|
- name: Install Poetry
|
||||||
|
run: pipx install poetry
|
||||||
|
|
||||||
- name: Install build package
|
- name: Install Python
|
||||||
run: python -m pip install -U build
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
cache: poetry
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
run: python -m build --sdist
|
run: poetry build
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- name: Publish to PyPI
|
||||||
with:
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||||
name: dist
|
env:
|
||||||
path: dist/*.tar.*
|
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
run: poetry publish
|
||||||
release:
|
|
||||||
name: Release
|
|
||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build_wheels]
|
|
||||||
steps:
|
|
||||||
- uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: dist
|
|
||||||
path: dist/
|
|
||||||
- name: Upload to PyPI
|
|
||||||
uses: pypa/gh-action-pypi-publish@master
|
|
||||||
with:
|
|
||||||
user: __token__
|
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
||||||
|
2
.github/workflows/test_examples.yml
vendored
2
.github/workflows/test_examples.yml
vendored
@ -1,6 +1,8 @@
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
|
- pyproject.toml
|
||||||
|
- poetry.lock
|
||||||
- '**.py'
|
- '**.py'
|
||||||
- .github/workflows/test_examples.yml
|
- .github/workflows/test_examples.yml
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
@ -12,7 +12,7 @@ repos:
|
|||||||
- id: isort
|
- id: isort
|
||||||
name: isort (python)
|
name: isort (python)
|
||||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||||
rev: v2.7.0
|
rev: v2.8.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pretty-format-yaml
|
- id: pretty-format-yaml
|
||||||
args: [--autofix]
|
args: [--autofix]
|
||||||
@ -20,15 +20,15 @@ repos:
|
|||||||
exclude: poetry.lock
|
exclude: poetry.lock
|
||||||
args: [--autofix]
|
args: [--autofix]
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 23.1.0
|
rev: 23.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
rev: v0.0.255
|
rev: v0.0.265
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v1.1.1
|
rev: v1.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies: [types-requests, types-setuptools]
|
additional_dependencies: [types-requests, types-setuptools]
|
||||||
|
62
README.md
62
README.md
@ -27,6 +27,9 @@ Tool for live presentations using either [Manim (community edition)](https://www
|
|||||||
- [F.A.Q](#faq)
|
- [F.A.Q](#faq)
|
||||||
* [How to increase quality on Windows](#how-to-increase-quality-on-windows)
|
* [How to increase quality on Windows](#how-to-increase-quality-on-windows)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
* [Reporting an Issue](#reporting-an-issue)
|
||||||
|
* [Seeking for Help](#seeking-for-help)
|
||||||
|
* [Contact](#contact)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -54,6 +57,16 @@ The recommended way to install the latest release is to use pip:
|
|||||||
pip install manim-slides
|
pip install manim-slides
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Optionally, you can also install Manim or ManimGL using extras[^1]:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install manim-slides[manim] # For Manim
|
||||||
|
# or
|
||||||
|
pip install manim-slides[manimgl] # For ManimGL
|
||||||
|
```
|
||||||
|
|
||||||
|
[^1]: NOTE: you still need to have Manim or ManimGL platform-specific dependencies installed on your computer.
|
||||||
|
|
||||||
### Install From Repository
|
### Install From Repository
|
||||||
|
|
||||||
An alternative way to install Manim Slides is to clone the git repository, and install from there: read the [contributing guide](https://eertmans.be/manim-slides/contributing/workflow.html) to know how.
|
An alternative way to install Manim Slides is to clone the git repository, and install from there: read the [contributing guide](https://eertmans.be/manim-slides/contributing/workflow.html) to know how.
|
||||||
@ -190,6 +203,55 @@ in *Settings*->*Display*.
|
|||||||
|
|
||||||
Contributions are more than welcome! Please read through [our contributing section](https://eertmans.be/manim-slides/contributing/index.html).
|
Contributions are more than welcome! Please read through [our contributing section](https://eertmans.be/manim-slides/contributing/index.html).
|
||||||
|
|
||||||
|
### Reporting an Issue
|
||||||
|
|
||||||
|
<!-- start reporting-an-issue -->
|
||||||
|
|
||||||
|
If you think you found a bug,
|
||||||
|
an error in the documentation,
|
||||||
|
or wish there was some feature that is currently missing,
|
||||||
|
we would love to hear from you!
|
||||||
|
|
||||||
|
The best way to reach us is via the
|
||||||
|
[GitHub issues](https://github.com/jeertmans/manim-slides/issues).
|
||||||
|
If your problem is not covered by an already existing (closed or open) issue,
|
||||||
|
then we suggest you create a
|
||||||
|
[new issue](https://github.com/jeertmans/manim-slides/issues/new/choose).
|
||||||
|
You can choose from a list of templates, or open a
|
||||||
|
[blank issue](https://github.com/jeertmans/manim-slides/issues/new)
|
||||||
|
if your issue does not fit one of the proposed topics.
|
||||||
|
|
||||||
|
The more precise you are in the description of your problem, the faster we will
|
||||||
|
be able to help you!
|
||||||
|
|
||||||
|
<!-- end reporting-an-issue -->
|
||||||
|
|
||||||
|
### Seeking for help
|
||||||
|
|
||||||
|
<!-- start seeking-for-help -->
|
||||||
|
|
||||||
|
Sometimes, you may have a question about Manim Slides,
|
||||||
|
not necessarily an issue.
|
||||||
|
|
||||||
|
There are two ways you can reach us for questions:
|
||||||
|
|
||||||
|
- via the `Question/Help/Support` topic when
|
||||||
|
[choosing an issue template](https://github.com/jeertmans/manim-slides/issues/new/choose);
|
||||||
|
- or via
|
||||||
|
[GitHub discussions](https://github.com/jeertmans/manim-slides/discussions).
|
||||||
|
|
||||||
|
<!-- end seeking-for-help -->
|
||||||
|
|
||||||
|
### Contact
|
||||||
|
|
||||||
|
<!-- start contact -->
|
||||||
|
|
||||||
|
Finally, if you do not have any GitHub account,
|
||||||
|
or just wish to contact the author of Manim Slides,
|
||||||
|
you can do so at: [jeertmans@icloud.com](mailto:jeertmans@icloud.com).
|
||||||
|
|
||||||
|
<!-- end contact -->
|
||||||
|
|
||||||
[pypi-version-badge]: https://img.shields.io/pypi/v/manim-slides?label=manim-slides
|
[pypi-version-badge]: https://img.shields.io/pypi/v/manim-slides?label=manim-slides
|
||||||
[pypi-version-url]: https://pypi.org/project/manim-slides/
|
[pypi-version-url]: https://pypi.org/project/manim-slides/
|
||||||
[pypi-python-version-badge]: https://img.shields.io/pypi/pyversions/manim-slides
|
[pypi-python-version-badge]: https://img.shields.io/pypi/pyversions/manim-slides
|
||||||
|
@ -2,10 +2,14 @@
|
|||||||
|
|
||||||
Thank you for your interest in Manim Slides! ✨
|
Thank you for your interest in Manim Slides! ✨
|
||||||
|
|
||||||
Manim Slides is an open source project, first created as a fork of [manim-presentation](https://github.com/galatolofederico/manim-presentation) (now deprecated in favor to Manim Slides), and we welcome contributions of all forms.
|
Manim Slides is an open source project, first created as a fork of
|
||||||
|
[manim-presentation](https://github.com/galatolofederico/manim-presentation)
|
||||||
This section is here to help fist-time contributors know how they can help this project grow. Whether you are already familiar with Manim or GitHub, it is worth taking a few minutes to read those documents!
|
(now deprecated in favor to Manim Slides),
|
||||||
|
and we welcome contributions of all forms.
|
||||||
|
|
||||||
|
This section is here to help fist-time contributors know how they can help this
|
||||||
|
project grow. Whether you are already familiar with Manim or GitHub,
|
||||||
|
it is worth taking a few minutes to read those documents!
|
||||||
|
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:hidden:
|
:hidden:
|
||||||
@ -19,3 +23,24 @@ internals
|
|||||||
|
|
||||||
[Internals](./internals)
|
[Internals](./internals)
|
||||||
: how Manim Slides is built and how the various parts of it work.
|
: how Manim Slides is built and how the various parts of it work.
|
||||||
|
|
||||||
|
## Reporting an Issue
|
||||||
|
|
||||||
|
```{include} ../../../README.md
|
||||||
|
:start-after: <!-- start reporting-an-issue -->
|
||||||
|
:end-before: <!-- end reporting-an-issue -->
|
||||||
|
```
|
||||||
|
|
||||||
|
## Seeking for Help
|
||||||
|
|
||||||
|
```{include} ../../../README.md
|
||||||
|
:start-after: <!-- start seeking-for-help -->
|
||||||
|
:end-before: <!-- end seeking-for-help -->
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
```{include} ../../../README.md
|
||||||
|
:start-after: <!-- start contact -->
|
||||||
|
:end-before: <!-- end contact -->
|
||||||
|
```
|
||||||
|
@ -11,7 +11,7 @@ This document is there to help you recreate a working environment for Manim Slid
|
|||||||
|
|
||||||
## Forking the repository and cloning it locally
|
## Forking the repository and cloning it locally
|
||||||
|
|
||||||
We used GitHub to host Manim Slides' repository, and we encourage contributors to use git.
|
We use GitHub to host Manim Slides' repository, and we encourage contributors to use git.
|
||||||
|
|
||||||
Useful links:
|
Useful links:
|
||||||
|
|
||||||
@ -30,6 +30,32 @@ With Poetry, installation becomes straightforward:
|
|||||||
poetry install
|
poetry install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This, however, only installs the minimal set of dependencies to run the package.
|
||||||
|
|
||||||
|
If you would like to install Manim or ManimGL, as documented in the [quickstart](../quickstart),
|
||||||
|
you can use the `--extras` option:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry install --extras manim # For Manim
|
||||||
|
# or
|
||||||
|
poetry install --extras manimgl # For ManimGL
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionnally, Manim Slides comes with group dependencies for development purposes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry install --with dev # For linters and formatters
|
||||||
|
# or
|
||||||
|
poetry install --with docs # To build the documentation locally
|
||||||
|
```
|
||||||
|
|
||||||
|
Another group is `test`, but it is only used for
|
||||||
|
[GitHub actions](https://github.com/jeertmans/manim-slides/blob/main/.github/workflows/test_examples.yml).
|
||||||
|
|
||||||
|
:::{note}
|
||||||
|
You can combine any number of groups or extras when installing the package locally.
|
||||||
|
:::
|
||||||
|
|
||||||
## Running commands
|
## Running commands
|
||||||
|
|
||||||
As modules were installed in a new Python environment, you cannot use them directly in the shell.
|
As modules were installed in a new Python environment, you cannot use them directly in the shell.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Application Programming Interface
|
# Application Programming Interface
|
||||||
|
|
||||||
Manim Slides' API is very limited: it simply consists in two classes, `Slide` and `ThreeDSlide`, which are subclasses of `Scene` and `ThreeDScene` from Manim.
|
Manim Slides' API is very limited: it simply consists of two classes, `Slide` and `ThreeDSlide`, which are subclasses of `Scene` and `ThreeDScene` from Manim.
|
||||||
|
|
||||||
Thefore, we only document here the methods we think the end-user will ever use, not the methods used internally when rendering.
|
Thefore, we only document here the methods we think the end-user will ever use, not the methods used internally when rendering.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Manim Slides allows you to convert presentations into one HTML file, with
|
|||||||
[RevealJS](https://revealjs.com/). This file can then be opened with any modern
|
[RevealJS](https://revealjs.com/). This file can then be opened with any modern
|
||||||
web browser, allowing for a nice portability of your presentations.
|
web browser, allowing for a nice portability of your presentations.
|
||||||
|
|
||||||
As for every command with Manim Slides, converting slides' fragments into one
|
As with every command with Manim Slides, converting slides' fragments into one
|
||||||
HTML file (and its assets) can be done in one command:
|
HTML file (and its assets) can be done in one command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -67,7 +67,7 @@ and the corresponding tree:
|
|||||||
## Without Manim Slides installed on the target machine
|
## Without Manim Slides installed on the target machine
|
||||||
|
|
||||||
An alternative to `manim-slides present` is `manim-slides convert`.
|
An alternative to `manim-slides present` is `manim-slides convert`.
|
||||||
Currently, only HTML conversion is available, but do not hesitate to propose
|
Currently, HTML and PPTX conversion are available, but do not hesitate to propose
|
||||||
other formats by creating a
|
other formats by creating a
|
||||||
[Feature Request](https://github.com/jeertmans/manim-slides/issues/new/choose),
|
[Feature Request](https://github.com/jeertmans/manim-slides/issues/new/choose),
|
||||||
or directly proposing a
|
or directly proposing a
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "4.11.0"
|
__version__ = "4.13.1"
|
||||||
|
@ -54,7 +54,7 @@ def verbosity_option(function: F) -> F:
|
|||||||
"-v",
|
"-v",
|
||||||
"--verbosity",
|
"--verbosity",
|
||||||
type=click.Choice(
|
type=click.Choice(
|
||||||
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
["PERF", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||||
case_sensitive=False,
|
case_sensitive=False,
|
||||||
),
|
),
|
||||||
help="Verbosity of CLI output",
|
help="Verbosity of CLI output",
|
||||||
|
@ -8,6 +8,7 @@ from pathlib import Path
|
|||||||
from typing import Dict, List, Optional, Set, Tuple, Union
|
from typing import Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, FilePath, PositiveInt, root_validator, validator
|
from pydantic import BaseModel, FilePath, PositiveInt, root_validator, validator
|
||||||
|
from pydantic.color import Color
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
from .defaults import FFMPEG_BIN
|
from .defaults import FFMPEG_BIN
|
||||||
@ -150,6 +151,7 @@ class PresentationConfig(BaseModel): # type: ignore
|
|||||||
slides: List[SlideConfig]
|
slides: List[SlideConfig]
|
||||||
files: List[FilePath]
|
files: List[FilePath]
|
||||||
resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
||||||
|
background_color: Color = "black"
|
||||||
|
|
||||||
@root_validator
|
@root_validator
|
||||||
def animation_indices_match_files(
|
def animation_indices_match_files(
|
||||||
|
@ -54,7 +54,7 @@ def validate_config_option(
|
|||||||
class Converter(BaseModel): # type: ignore
|
class Converter(BaseModel): # type: ignore
|
||||||
presentation_configs: List[PresentationConfig] = []
|
presentation_configs: List[PresentationConfig] = []
|
||||||
assets_dir: str = "{basename}_assets"
|
assets_dir: str = "{basename}_assets"
|
||||||
template: Optional[str] = None
|
template: Optional[Path] = None
|
||||||
|
|
||||||
def convert_to(self, dest: Path) -> None:
|
def convert_to(self, dest: Path) -> None:
|
||||||
"""Converts self, i.e., a list of presentations, into a given format."""
|
"""Converts self, i.e., a list of presentations, into a given format."""
|
||||||
@ -314,6 +314,8 @@ class RevealJS(Converter):
|
|||||||
file = presentation_config.files[slide_config.start_animation]
|
file = presentation_config.files[slide_config.start_animation]
|
||||||
file = assets_dir / file.name
|
file = assets_dir / file.name
|
||||||
|
|
||||||
|
logger.debug(f"Writing video section with file {file}")
|
||||||
|
|
||||||
# TODO: document this
|
# TODO: document this
|
||||||
# Videos are muted because, otherwise, the first slide never plays correctly.
|
# 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.
|
# This is due to a restriction in playing audio without the user doing anything.
|
||||||
@ -321,15 +323,14 @@ class RevealJS(Converter):
|
|||||||
# Read more about this:
|
# Read more about this:
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#autoplay_and_autoplay_blocking
|
# https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#autoplay_and_autoplay_blocking
|
||||||
if slide_config.is_loop():
|
if slide_config.is_loop():
|
||||||
yield f'<section data-background-size={self.background_size.value} data-background-video="{file}" data-background-video-muted data-background-video-loop></section>'
|
yield f'<section data-background-size={self.background_size.value} data-background-color="{presentation_config.background_color}" data-background-video="{file}" data-background-video-muted data-background-video-loop></section>'
|
||||||
else:
|
else:
|
||||||
yield f'<section data-background-size={self.background_size.value} data-background-video="{file}" data-background-video-muted></section>'
|
yield f'<section data-background-size={self.background_size.value} data-background-color="{presentation_config.background_color}" data-background-video="{file}" data-background-video-muted></section>'
|
||||||
|
|
||||||
def load_template(self) -> str:
|
def load_template(self) -> str:
|
||||||
"""Returns the RevealJS HTML template as a string."""
|
"""Returns the RevealJS HTML template as a string."""
|
||||||
if isinstance(self.template, str):
|
if isinstance(self.template, Path):
|
||||||
with open(self.template, "r") as f:
|
return self.template.read_text()
|
||||||
return f.read()
|
|
||||||
|
|
||||||
if sys.version_info < (3, 9):
|
if sys.version_info < (3, 9):
|
||||||
return resources.read_text(data, "revealjs_template.html")
|
return resources.read_text(data, "revealjs_template.html")
|
||||||
@ -350,6 +351,8 @@ class RevealJS(Converter):
|
|||||||
)
|
)
|
||||||
full_assets_dir = dirname / assets_dir
|
full_assets_dir = dirname / assets_dir
|
||||||
|
|
||||||
|
logger.debug(f"Assets will be saved to: {full_assets_dir}")
|
||||||
|
|
||||||
os.makedirs(full_assets_dir, exist_ok=True)
|
os.makedirs(full_assets_dir, exist_ok=True)
|
||||||
|
|
||||||
for presentation_config in self.presentation_configs:
|
for presentation_config in self.presentation_configs:
|
||||||
|
@ -5,7 +5,9 @@ https://github.com/ManimCommunity/manim/blob/d5b65b844b8ce8ff5151a2f56f9dc98cebb
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from rich.console import Console
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
|
from rich.theme import Theme
|
||||||
|
|
||||||
__all__ = ["logger", "make_logger"]
|
__all__ = ["logger", "make_logger"]
|
||||||
|
|
||||||
@ -33,9 +35,12 @@ def make_logger() -> logging.Logger:
|
|||||||
RichHandler.KEYWORDS = HIGHLIGHTED_KEYWORDS
|
RichHandler.KEYWORDS = HIGHLIGHTED_KEYWORDS
|
||||||
rich_handler = RichHandler(
|
rich_handler = RichHandler(
|
||||||
show_time=True,
|
show_time=True,
|
||||||
|
console=Console(theme=Theme({"logging.level.perf": "magenta"})),
|
||||||
)
|
)
|
||||||
|
logging.addLevelName(5, "PERF")
|
||||||
logger = logging.getLogger("manim-slides")
|
logger = logging.getLogger("manim-slides")
|
||||||
logger.addHandler(rich_handler)
|
logger.addHandler(rich_handler)
|
||||||
|
|
||||||
return logger
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,9 +11,10 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from click import Context, Parameter
|
from click import Context, Parameter
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
from pydantic.color import Color
|
||||||
from PySide6.QtCore import Qt, QThread, Signal, Slot
|
from PySide6.QtCore import Qt, QThread, Signal, Slot
|
||||||
from PySide6.QtGui import QCloseEvent, QIcon, QImage, QKeyEvent, QPixmap, QResizeEvent
|
from PySide6.QtGui import QCloseEvent, QIcon, QImage, QKeyEvent, QPixmap, QResizeEvent
|
||||||
from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
|
from PySide6.QtWidgets import QApplication, QFileDialog, QGridLayout, QLabel, QWidget
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from .commons import config_path_option, verbosity_option
|
from .commons import config_path_option, verbosity_option
|
||||||
@ -105,6 +106,11 @@ class Presentation:
|
|||||||
"""Returns the resolution."""
|
"""Returns the resolution."""
|
||||||
return self.config.resolution
|
return self.config.resolution
|
||||||
|
|
||||||
|
@property
|
||||||
|
def background_color(self) -> Color:
|
||||||
|
"""Returns the background color."""
|
||||||
|
return self.config.background_color
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_slide_index(self) -> int:
|
def current_slide_index(self) -> int:
|
||||||
return self.__current_slide_index
|
return self.__current_slide_index
|
||||||
@ -411,6 +417,11 @@ class Display(QThread): # type: ignore
|
|||||||
"""Returns the resolution of the current presentation."""
|
"""Returns the resolution of the current presentation."""
|
||||||
return self.current_presentation.resolution
|
return self.current_presentation.resolution
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_background_color(self) -> Color:
|
||||||
|
"""Returns the background color of the current presentation."""
|
||||||
|
return self.current_presentation.background_color
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Runs a series of presentations until end or exit."""
|
"""Runs a series of presentations until end or exit."""
|
||||||
while self.run_flag:
|
while self.run_flag:
|
||||||
@ -437,6 +448,21 @@ class Display(QThread): # type: ignore
|
|||||||
|
|
||||||
lag = now() - last_time
|
lag = now() - last_time
|
||||||
sleep_time = 1 / self.current_presentation.fps
|
sleep_time = 1 / self.current_presentation.fps
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
5,
|
||||||
|
f"Took {lag:.3f} seconds to process the current frame, that must play at a rate of one every {sleep_time:.3f} seconds.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if sleep_time - lag < 0:
|
||||||
|
logger.warn(
|
||||||
|
"The FPS rate could not be matched. "
|
||||||
|
"This is normal when manually transitioning between slides.\n"
|
||||||
|
"If you feel that the FPS are too low, "
|
||||||
|
"consider checking this issue:\n"
|
||||||
|
"https://github.com/jeertmans/manim-slides/issues/179."
|
||||||
|
)
|
||||||
|
|
||||||
sleep_time = max(sleep_time - lag, 0)
|
sleep_time = max(sleep_time - lag, 0)
|
||||||
time.sleep(sleep_time)
|
time.sleep(sleep_time)
|
||||||
last_time = now()
|
last_time = now()
|
||||||
@ -649,7 +675,9 @@ class App(QWidget): # type: ignore
|
|||||||
self.label.setScaledContents(True)
|
self.label.setScaledContents(True)
|
||||||
self.label.setAlignment(Qt.AlignCenter)
|
self.label.setAlignment(Qt.AlignCenter)
|
||||||
self.label.resize(self.display_width, self.display_height)
|
self.label.resize(self.display_width, self.display_height)
|
||||||
self.label.setStyleSheet(f"background-color: {background_color}")
|
self.label.setStyleSheet(
|
||||||
|
f"background-color: {self.thread.current_background_color}"
|
||||||
|
)
|
||||||
|
|
||||||
self.pixmap = QPixmap(self.width(), self.height())
|
self.pixmap = QPixmap(self.width(), self.height())
|
||||||
self.label.setPixmap(self.pixmap)
|
self.label.setPixmap(self.pixmap)
|
||||||
@ -729,6 +757,9 @@ class App(QWidget): # type: ignore
|
|||||||
self.display_width, self.display_height = self.thread.current_resolution
|
self.display_width, self.display_height = self.thread.current_resolution
|
||||||
if not self.isFullScreen():
|
if not self.isFullScreen():
|
||||||
self.resize(self.display_width, self.display_height)
|
self.resize(self.display_width, self.display_height)
|
||||||
|
self.label.setStyleSheet(
|
||||||
|
f"background-color: {self.thread.current_background_color}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@ -924,8 +955,8 @@ def start_at_callback(
|
|||||||
"background_color",
|
"background_color",
|
||||||
metavar="COLOR",
|
metavar="COLOR",
|
||||||
type=str,
|
type=str,
|
||||||
default="black",
|
default=None,
|
||||||
help='Set the background color for borders when using "keep" resize mode. Can be any valid CSS color, e.g., "green", "#FF6500" or "rgba(255, 255, 0, .5)".',
|
help='Set the background color for borders when using "keep" resize mode. Can be any valid CSS color, e.g., "green", "#FF6500" or "rgba(255, 255, 0, .5)". If not set, it defaults to the background color configured in the Manim scene.',
|
||||||
show_default=True,
|
show_default=True,
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
@ -980,7 +1011,7 @@ def present(
|
|||||||
hide_mouse: bool,
|
hide_mouse: bool,
|
||||||
aspect_ratio: str,
|
aspect_ratio: str,
|
||||||
resize_mode: str,
|
resize_mode: str,
|
||||||
background_color: str,
|
background_color: Optional[str],
|
||||||
start_at: Tuple[Optional[int], Optional[int], Optional[int]],
|
start_at: Tuple[Optional[int], Optional[int], Optional[int]],
|
||||||
start_at_scene_number: Optional[int],
|
start_at_scene_number: Optional[int],
|
||||||
start_at_slide_number: Optional[int],
|
start_at_slide_number: Optional[int],
|
||||||
@ -996,6 +1027,22 @@ def present(
|
|||||||
Use `manim-slide list-scenes` to list all available scenes in a given folder.
|
Use `manim-slide list-scenes` to list all available scenes in a given folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
app.setApplicationName("Manim Slides")
|
||||||
|
dialog = QFileDialog()
|
||||||
|
dialog.setDirectory(FOLDER_PATH if os.path.exists(FOLDER_PATH) else "")
|
||||||
|
dialog.setFileMode(QFileDialog.ExistingFiles)
|
||||||
|
dialog.setNameFilters(["JSON (*.json)", "* (*.*)"])
|
||||||
|
if dialog.exec():
|
||||||
|
filenames = dialog.selectedFiles()
|
||||||
|
print(filenames)
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# - get files in selected order
|
||||||
|
# - kill dialog
|
||||||
|
# - add cli option (+ envvar) to prompt gui instead of cli
|
||||||
|
# - use scenes selected from gui
|
||||||
|
|
||||||
if skip_all:
|
if skip_all:
|
||||||
exit_after_last_slide = True
|
exit_after_last_slide = True
|
||||||
|
|
||||||
@ -1005,6 +1052,10 @@ def present(
|
|||||||
for presentation_config in presentation_configs:
|
for presentation_config in presentation_configs:
|
||||||
presentation_config.resolution = resolution
|
presentation_config.resolution = resolution
|
||||||
|
|
||||||
|
if background_color is not None:
|
||||||
|
for presentation_config in presentation_configs:
|
||||||
|
presentation_config.background_color = background_color
|
||||||
|
|
||||||
presentations = [
|
presentations = [
|
||||||
Presentation(presentation_config)
|
Presentation(presentation_config)
|
||||||
for presentation_config in presentation_configs
|
for presentation_config in presentation_configs
|
||||||
@ -1048,7 +1099,6 @@ def present(
|
|||||||
hide_mouse=hide_mouse,
|
hide_mouse=hide_mouse,
|
||||||
aspect_ratio=ASPECT_RATIO_MODES[aspect_ratio],
|
aspect_ratio=ASPECT_RATIO_MODES[aspect_ratio],
|
||||||
resize_mode=RESIZE_MODES[resize_mode],
|
resize_mode=RESIZE_MODES[resize_mode],
|
||||||
background_color=background_color,
|
|
||||||
start_at_scene_number=start_at_scene_number,
|
start_at_scene_number=start_at_scene_number,
|
||||||
start_at_slide_number=start_at_slide_number,
|
start_at_slide_number=start_at_slide_number,
|
||||||
start_at_animation_number=start_at_animation_number,
|
start_at_animation_number=start_at_animation_number,
|
||||||
|
@ -54,6 +54,14 @@ class Slide(Scene): # type:ignore
|
|||||||
self.__loop_start_animation: Optional[int] = None
|
self.__loop_start_animation: Optional[int] = None
|
||||||
self.__pause_start_animation = 0
|
self.__pause_start_animation = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __background_color(self) -> str:
|
||||||
|
"""Returns the scene's background color."""
|
||||||
|
if MANIMGL:
|
||||||
|
return self.camera_config["background_color"].hex # type: ignore
|
||||||
|
else:
|
||||||
|
return config["background_color"].hex # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __resolution(self) -> Tuple[int, int]:
|
def __resolution(self) -> Tuple[int, int]:
|
||||||
"""Returns the scene's resolution used during rendering."""
|
"""Returns the scene's resolution used during rendering."""
|
||||||
@ -321,7 +329,10 @@ class Slide(Scene): # type:ignore
|
|||||||
with open(slide_path, "w") as f:
|
with open(slide_path, "w") as f:
|
||||||
f.write(
|
f.write(
|
||||||
PresentationConfig(
|
PresentationConfig(
|
||||||
slides=self.__slides, files=files, resolution=self.__resolution
|
slides=self.__slides,
|
||||||
|
files=files,
|
||||||
|
resolution=self.__resolution,
|
||||||
|
background_color=self.__background_color,
|
||||||
).json(indent=2)
|
).json(indent=2)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
BIN
paper/docs.png
BIN
paper/docs.png
Binary file not shown.
Before Width: | Height: | Size: 4.3 MiB After Width: | Height: | Size: 4.3 MiB |
@ -64,13 +64,16 @@ provide new features on a regular basis.
|
|||||||
|
|
||||||
# Easy to Use Commitment
|
# Easy to Use Commitment
|
||||||
|
|
||||||
Manim Slides is commited to be an easy-to-use tool, when minimal installation
|
Manim Slides is commited to be an easy-to-use tool, with minimal installation
|
||||||
procedure and few modifications required. It can either be used locally with its
|
procedure and few modifications required. It can either be used locally with its
|
||||||
graphical user interface (GUI), or shared via HTML thanks to the RevealJS
|
graphical user interface (GUI), or shared via one of the two formats it can
|
||||||
Javascript package [@revealjs].
|
convert to:
|
||||||
|
|
||||||
|
* an HTML page thanks to the RevealJS Javascript package [@revealjs];
|
||||||
|
* or a PowerPoint (`.pptx`) file.
|
||||||
|
|
||||||
This work has a very similar syntax to Manim and offers a comprehensive
|
This work has a very similar syntax to Manim and offers a comprehensive
|
||||||
documentation hosted on [GitHub pages](https://eertmans.be/manim-slides/), see
|
documentation hosted on [GitHub pages](https://jeertmans.github.io/manim-slides/), see
|
||||||
\autoref{fig:docs}.
|
\autoref{fig:docs}.
|
||||||
|
|
||||||

|

|
||||||
@ -79,10 +82,10 @@ documentation hosted on [GitHub pages](https://eertmans.be/manim-slides/), see
|
|||||||
|
|
||||||
We have used manim-presentation for our presentation at the COST
|
We have used manim-presentation for our presentation at the COST
|
||||||
Interact, hosted in Lyon, 2022, and
|
Interact, hosted in Lyon, 2022, and
|
||||||
[available online](https://eertmans.be/research/cost-interact-presentation/).
|
[available online](https://web.archive.org/web/20230507184944/https://eertmans.be/posts/cost-interact-presentation/).
|
||||||
This experience highly motivated the development of Manim Slides, and our
|
This experience highly motivated the development of Manim Slides, and our
|
||||||
EuCAP 2023 presentation slides are already
|
EuCAP 2023 presentation slides are already
|
||||||
[available online](https://eertmans.be/research/eucap-presentation/), thanks
|
[available online](https://web.archive.org/web/20230507211243/https://eertmans.be/posts/eucap-presentation/), thanks
|
||||||
to Manim Slides' HTML feature.
|
to Manim Slides' HTML feature.
|
||||||
|
|
||||||
Also, one of our users created a short
|
Also, one of our users created a short
|
||||||
@ -92,8 +95,8 @@ and posted it on YouTube.
|
|||||||
# Stability and releases
|
# Stability and releases
|
||||||
|
|
||||||
Manim Slides is continously tested on most recent Python versions, both ManimCE
|
Manim Slides is continously tested on most recent Python versions, both ManimCE
|
||||||
and ManimGL, and on all major platforms: **Ubuntu**, **macOS** and **Windows**. As of Manim
|
and ManimGL, and on all major platforms: **Ubuntu**, **macOS** and **Windows**. Due to Manim
|
||||||
Slide's exposed API begin very minimal, and the variaty of tests that are
|
Slide's exposed API being very minimal, and the variety of tests that are
|
||||||
performed, this tool can be considered stable over time.
|
performed, this tool can be considered stable over time.
|
||||||
|
|
||||||
New releases are very frequent, as they mostly introduce enhancements or small
|
New releases are very frequent, as they mostly introduce enhancements or small
|
||||||
@ -111,12 +114,31 @@ presenting Manim content in front of an audience much easier than before,
|
|||||||
allowing presenters to focus more on the content of their slides, rather than on
|
allowing presenters to focus more on the content of their slides, rather than on
|
||||||
how to actually present them efficiently.
|
how to actually present them efficiently.
|
||||||
|
|
||||||
|
## Target Audience
|
||||||
|
|
||||||
|
Manim Slides was developed with the goal of making educational content more
|
||||||
|
accessible than ever. We believe that researchers, professors, teaching
|
||||||
|
assistants and anyone else who needs to teach scientific content can benefit
|
||||||
|
from using this tool. The ability to pace your presentation yourself is
|
||||||
|
essential, and Manim Slides gives you that ability.
|
||||||
|
|
||||||
|
## A Need for Portability
|
||||||
|
|
||||||
|
One of the major concerns with presenting content in a non-standard format
|
||||||
|
(i.e., not just a plain PDF) is the issue of portability.
|
||||||
|
Depending on the programs available, the power of the target computer,
|
||||||
|
or the access to the internet, not all solutions are equal.
|
||||||
|
From the same configuration file, Manim Slides offers a series of solutions to
|
||||||
|
share your slides, which we discuss on our
|
||||||
|
[Sharing your slides](https://jeertmans.github.io/manim-slides/reference/sharing.html)
|
||||||
|
page.
|
||||||
|
|
||||||
# Acknowledgements
|
# Acknowledgements
|
||||||
|
|
||||||
We acknowledge the work of [@manim-presentation] that paved the initial structure
|
We acknowledge the work of [@manim-presentation] that paved the initial structure
|
||||||
of Manim Slides with the manim-presentation Python package.
|
of Manim Slides with the manim-presentation Python package.
|
||||||
|
|
||||||
We also acknowledge Grant Sanderson for its termendous work on Manim, as well as
|
We also acknowledge Grant Sanderson for his tremendous work on Manim, as
|
||||||
well as the Manim Community contributors.
|
well as the Manim Community contributors.
|
||||||
|
|
||||||
Finally, we also acknowledge contributions from the GitHub contributors on the
|
Finally, we also acknowledge contributions from the GitHub contributors on the
|
||||||
|
850
poetry.lock
generated
850
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -43,12 +43,14 @@ packages = [
|
|||||||
]
|
]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/jeertmans/manim-slides"
|
repository = "https://github.com/jeertmans/manim-slides"
|
||||||
version = "4.11.0"
|
version = "4.13.1"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
click = "^8.1.3"
|
click = "^8.1.3"
|
||||||
click-default-group = "^1.2.2"
|
click-default-group = "^1.2.2"
|
||||||
lxml = "^4.9.2"
|
lxml = "^4.9.2"
|
||||||
|
manim = {version = "^0.17.0", optional = true}
|
||||||
|
manimgl = {version = "^1.6.1", optional = true}
|
||||||
numpy = "^1.19"
|
numpy = "^1.19"
|
||||||
opencv-python = "^4.6.0.66"
|
opencv-python = "^4.6.0.66"
|
||||||
pydantic = "^1.10.2"
|
pydantic = "^1.10.2"
|
||||||
@ -59,6 +61,10 @@ requests = "^2.28.1"
|
|||||||
rich = "^13.3.2"
|
rich = "^13.3.2"
|
||||||
tqdm = "^4.64.1"
|
tqdm = "^4.64.1"
|
||||||
|
|
||||||
|
[tool.poetry.extras]
|
||||||
|
manim = ["manim"]
|
||||||
|
manimgl = ["manimgl"]
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = "^22.10.0"
|
black = "^22.10.0"
|
||||||
bump2version = "^1.0.1"
|
bump2version = "^1.0.1"
|
||||||
|
Reference in New Issue
Block a user