Compare commits

...

17 Commits

Author SHA1 Message Date
d6ec0d3da9 chore(version): update version 2022-09-26 17:30:10 +02:00
546451e019 fix(lib): add a more meaningful error message (#30)
* fix(lib): add a more meaningful error message

This adds an error message to explain why calling `self.pause()` cannot work if no previous animation was played.

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

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

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-26 17:29:17 +02:00
2457ca8a05 fix(cli): properly rewind / previous slide after reverse (#28)
* fix(cli): properly rewind / previous slide after reverse

Closes #24

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

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

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-22 19:11:26 +02:00
9900b3123e fix(cli): revert breaking change 2022-09-22 18:28:27 +02:00
ee92e0aa88 chore(docs): improve CLI docs (#27)
* chore(docs): cleanup code & docs

* chore(docs): better document present command

* chore(docs): improve gh page
2022-09-22 18:16:28 +02:00
cbee6320f5 chore(version): update version 2022-09-21 16:11:20 +02:00
382084f9ef feat(cli): record presentation (#25)
* feat(cli): record presentation

As proposed in #21, it is now possible to record a presentation output to a video file, with option `--record-to="some_file.avi"`.

Closes #21

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

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

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-21 16:10:37 +02:00
068484b828 chore(docs): add base cli docs 2022-09-21 16:09:08 +02:00
91f8d97acf chore(cli): show default for all cli options (#26) 2022-09-21 15:41:19 +02:00
49cdedc6fe chore(README): add link to doc 2022-09-21 15:36:38 +02:00
fe1fa059f6 chore(ci): auto build docs 2022-09-21 15:30:07 +02:00
3f6d2e5e57 chore(docs): create first documentation 2022-09-21 15:26:10 +02:00
99ad798155 chore(version): update version 2022-09-21 10:08:57 +02:00
84c25f1ed5 fix(ui): enhance window quality (#22)
* fix(ui): enhance window quality

This fix always resizes the frame size, as this seems to be a good cross-platform fix to the quality issue that occurs when the frame does not match the window size.

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

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

* fix: remove unused import

* fix: actually only resize on Windows

* feat(cli): optional interpolation flag

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

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

* chore: add image fox windows quality fix

* chore(README): document Windows fix

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-21 09:15:12 +02:00
7fb3fa01dd chore(ci): check for unused code with vulture (#23)
* chore(ci): check for unused code with vulture

* fix: add paths for vulture
2022-09-20 09:36:39 +02:00
2d2a225afe [pre-commit.ci] pre-commit autoupdate (#19)
updates:
- [github.com/psf/black: 22.6.0 → 22.8.0](https://github.com/psf/black/compare/22.6.0...22.8.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-20 09:18:14 +02:00
b9d2cd92b5 chore(docs): improve docstrings (#18) 2022-09-17 15:49:10 +02:00
18 changed files with 339 additions and 54 deletions

50
.github/workflows/pages.yml vendored Normal file
View File

@ -0,0 +1,50 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Install Linux Dependencies
run: sudo apt install libcairo2-dev libpango1.0-dev ffmpeg
- name: Install Python dependencies
run: pip install manim sphinx sphinx_click furo
- name: Install local Python package
run: pip install -e .
- name: Build docs
run: cd docs && make html
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload docs/build/html dir
path: 'docs/build/html/'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

2
.gitignore vendored
View File

@ -17,3 +17,5 @@ slides/
videos/
images/
docs/build/

View File

@ -6,7 +6,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 22.8.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
@ -19,3 +19,7 @@ repos:
rev: 22.8.0
hooks:
- id: black
- repo: https://github.com/jendrikseipp/vulture
rev: v2.3
hooks:
- id: vulture

View File

@ -9,7 +9,7 @@ Tool for live presentations using either [Manim (community edition)](https://www
> **_NOTE:_** This project extends the work of [`manim-presentation`](https://github.com/galatolofederico/manim-presentation), with a lot more features!
- [Install](#install)
- [Installation](#installation)
* [Dependencies](#dependencies)
* [Pip install](#pip-install)
* [Install From Repository](#install-from-repository)
@ -18,6 +18,8 @@ Tool for live presentations using either [Manim (community edition)](https://www
* [Key Bindings](#key-bindings)
* [Other Examples](#other-examples)
- [Features and Comparison with Original manim-presentation](#features-and-comparison-with-original-manim-presentation)
- [F.A.Q](#faq)
* [How to increase quality on Windows](#how-to-increase-quality-on-windows)
- [Contributing](#contributing)
## Installation
@ -57,6 +59,8 @@ Using Manim Slides is a two-step process:
1. Render animations using `Slide` (resp. `ThreeDSlide`) as a base class instead of `Scene` (resp. `ThreeDScene`), and add calls to `self.pause()` everytime you want to create a new slide.
2. Run `manim-slides` on rendered animations and display them like a *Power Point* presentation.
The command-line documentation is available [online](https://eertmans.be/manim-slides/).
### Basic Example
@ -168,6 +172,16 @@ Below is a non-exhaustive list of features:
| Documented code | :heavy_check_mark: | :heavy_multiplication_x: |
| Tested on Unix, macOS, and Windows | :heavy_check_mark: | :heavy_multiplication_x: |
## F.A.Q
### How to increase quality on Windows
On Windows platform, one may encounter a lower image resolution than expected. Usually, this is observed because Windows rescales every application to fit the screen.
As found by [@arashash](https://github.com/arashash), in [#20](https://github.com/jeertmans/manim-slides/issues/20), the problem can be addressed by changing the scaling factor to 100%:
![Windows Fix Scaling](static/windows_quality_fix.png)
in *Settings*->*Display*.
## Contributing

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

26
docs/source/conf.py Normal file
View File

@ -0,0 +1,26 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "Manim Slides"
copyright = "2022, Jérome Eertmans"
author = "Jérome Eertmans"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = ["sphinx.ext.autodoc", "sphinx_click"]
templates_path = ["_templates"]
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "furo"
html_static_path = ["_static"]

20
docs/source/index.rst Normal file
View File

@ -0,0 +1,20 @@
.. manim-slides documentation master file, created by
sphinx-quickstart on Wed Sep 21 15:07:28 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
.. image:: _static/logo.png
:width: 600
:align: center
:alt: Manim Slide logo
Welcome to Manim Slide's CLI documentation!
===========================================
This page contains an exhaustive list of all the commands available with `manim-slides`.
If you need help installing or using Manim Slide, please refer to the `GitHub README <https://github.com/jeertmans/manim-slides>`_.
.. click:: manim_slides.main:cli
:prog: manim-slides
:nested: full

View File

@ -1 +1 @@
__version__ = "4.0.0"
__version__ = "4.1.1"

View File

@ -1,9 +1,12 @@
from typing import Callable
import click
from .defaults import CONFIG_PATH
def config_path_option(function):
def config_path_option(function) -> Callable:
"""Wraps a function to add configuration path option."""
return click.option(
"-c",
"--config",
@ -11,10 +14,12 @@ def config_path_option(function):
default=CONFIG_PATH,
type=click.Path(dir_okay=False),
help="Set path to configuration file.",
show_default=True,
)(function)
def config_options(function):
def config_options(function) -> Callable:
"""Wraps a function to add configuration options."""
function = config_path_option(function)
function = click.option(
"-f", "--force", is_flag=True, help="Overwrite any existing configuration file."

View File

@ -2,7 +2,7 @@ import os
from enum import Enum
from typing import List, Optional, Set
from pydantic import BaseModel, FilePath, root_validator, validator
from pydantic import BaseModel, root_validator, validator
from .defaults import LEFT_ARROW_KEY_CODE, RIGHT_ARROW_KEY_CODE
@ -84,6 +84,11 @@ class SlideConfig(BaseModel):
def start_animation_is_before_end(cls, values):
if values["start_animation"] >= values["end_animation"]:
if values["start_animation"] == values["end_animation"] == 0:
raise ValueError(
"You have to play at least one animation (e.g., `self.wait()`) before pausing. If you want to start paused, use the approriate command-line option when presenting."
)
raise ValueError(
"Start animation index must be strictly lower than end animation index"
)

View File

@ -3,7 +3,6 @@ import platform
import cv2
FONT_ARGS = (cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 1, cv2.LINE_AA)
PIXELS_PER_CHARACTER = 20
FOLDER_PATH: str = "./slides"
CONFIG_PATH: str = ".manim-slides.json"

View File

@ -10,6 +10,11 @@ from .wizard import init, wizard
@click.version_option(__version__, "-v", "--version")
@click.help_option("-h", "--help")
def cli():
"""
Manim Slides command-line utilities.
If not command is specified, defaults to `present`.
"""
pass

View File

@ -7,20 +7,29 @@ import time
from enum import IntEnum, auto, unique
from typing import List, Tuple
if platform.system() == "Windows":
import ctypes
import click
import cv2
import numpy as np
from pydantic import ValidationError
from tqdm import tqdm
from .commons import config_path_option
from .config import Config, PresentationConfig, SlideConfig, SlideType
from .defaults import CONFIG_PATH, FOLDER_PATH, FONT_ARGS
INTERPOLATION_FLAGS = {
"nearest": cv2.INTER_NEAREST,
"linear": cv2.INTER_LINEAR,
"cubic": cv2.INTER_CUBIC,
"area": cv2.INTER_AREA,
"lanczos4": cv2.INTER_LANCZOS4,
"linear-exact": cv2.INTER_LINEAR_EXACT,
"nearest-exact": cv2.INTER_NEAREST_EXACT,
}
WINDOW_NAME = "Manim Slides"
WINDOW_INFO_NAME = f"{WINDOW_NAME}: Info"
WINDOWS = platform.system() == "Windows"
@unique
@ -37,7 +46,7 @@ class State(IntEnum):
def now() -> int:
"""Returns time.time() in seconds."""
"""Returns time.time() in milliseconds."""
return round(time.time() * 1000)
@ -55,6 +64,7 @@ class Presentation:
self.current_slide_index = 0
self.current_animation = self.current_slide.start_animation
self.current_file = None
self.loaded_animation_cap = -1
self.cap = None # cap = cv2.VideoCapture
@ -104,6 +114,8 @@ class Presentation:
file = "{}_reversed{}".format(*os.path.splitext(file))
self.reversed_animation = animation
self.current_file = file
self.cap = cv2.VideoCapture(file)
self.loaded_animation_cap = animation
@ -122,6 +134,13 @@ class Presentation:
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
def cancel_reverse(self):
"""Cancels any effet produced by a reversed slide."""
if self.reverse:
self.reverse = False
self.reversed_animation = -1
self.release_cap()
def reverse_current_slide(self):
"""Reverses current slide."""
self.reverse = True
@ -130,9 +149,7 @@ class Presentation:
def load_next_slide(self):
"""Loads next slide."""
if self.reverse:
self.reverse = False
self.reversed_animation = -1
self.release_cap()
self.cancel_reverse()
self.rewind_current_slide()
elif self.current_slide.is_last():
self.current_slide.terminated = True
@ -144,6 +161,7 @@ class Presentation:
def load_previous_slide(self):
"""Loads previous slide."""
self.cancel_reverse()
self.current_slide_index = max(0, self.current_slide_index - 1)
self.rewind_current_slide()
@ -196,6 +214,11 @@ class Presentation:
else:
return self.next_animation == self.current_slide.end_animation
@property
def current_frame_number(self) -> int:
"""Returns current frame number."""
return int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES))
def update_state(self, state) -> Tuple[np.ndarray, State]:
"""
Updates the current state given the previous one.
@ -252,14 +275,22 @@ class Display:
start_paused=False,
fullscreen=False,
skip_all=False,
resolution=(1280, 720),
resolution=(1980, 1080),
interpolation_flag=cv2.INTER_LINEAR,
record_to=None,
):
self.presentations = presentations
self.start_paused = start_paused
self.config = config
self.skip_all = skip_all
self.fullscreen = fullscreen
self.is_windows = platform.system() == "Windows"
self.resolution = resolution
self.interpolation_flag = interpolation_flag
self.record_to = record_to
self.recordings = []
self.window_flags = (
cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_FREERATIO | cv2.WINDOW_NORMAL
)
self.state = State.PLAYING
self.lastframe = None
@ -274,43 +305,20 @@ class Display:
cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_FREERATIO | cv2.WINDOW_AUTOSIZE,
)
if self.is_windows:
user32 = ctypes.windll.user32
self.screen_width, self.screen_height = user32.GetSystemMetrics(
0
), user32.GetSystemMetrics(1)
if self.fullscreen:
cv2.namedWindow(WINDOW_NAME, cv2.WND_PROP_FULLSCREEN)
cv2.namedWindow(
WINDOW_NAME, cv2.WINDOW_GUI_NORMAL | cv2.WND_PROP_FULLSCREEN
)
cv2.setWindowProperty(
WINDOW_NAME, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN
)
else:
cv2.namedWindow(
WINDOW_NAME,
cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_FREERATIO | cv2.WINDOW_NORMAL,
)
cv2.resizeWindow(WINDOW_NAME, *resolution)
def resize_frame_to_screen(self, frame: np.ndarray):
"""
Resizes a given frame to match screen dimensions.
Only works on Windows.
"""
assert self.is_windows, "Only Windows platforms need this method"
frame_height, frame_width = frame.shape[:2]
scale_height = self.screen_height / frame_height
scale_width = self.screen_width / frame_width
scale = min(scale_height, scale_width)
return cv2.resize(frame, (int(scale * frame_height), int(scale * frame_width)))
cv2.namedWindow(WINDOW_NAME, self.window_flags)
cv2.resizeWindow(WINDOW_NAME, *self.resolution)
@property
def current_presentation(self) -> Presentation:
"""Returns the current presentation"""
"""Returns the current presentation."""
return self.presentations[self.current_presentation_index]
def run(self):
@ -341,10 +349,25 @@ class Display:
self.lag = now() - self.last_time
self.last_time = now()
if not self.record_to is None:
pres = self.current_presentation
self.recordings.append(
(pres.current_file, pres.current_frame_number, pres.fps)
)
frame = self.lastframe
if self.is_windows and self.fullscreen:
frame = self.resize_frame_to_screen(frame)
# If Window was manually closed (impossible in fullscreen),
# we reopen it
if cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) < 1:
cv2.namedWindow(WINDOW_NAME, self.window_flags)
cv2.resizeWindow(WINDOW_NAME, *self.resolution)
if WINDOWS: # Only resize on Windows
_, _, w, h = cv2.getWindowImageRect(WINDOW_NAME)
if (h, w) != frame.shape[:2]: # Only if shape is different
frame = cv2.resize(frame, (w, h), self.interpolation_flag)
cv2.imshow(WINDOW_NAME, frame)
@ -408,7 +431,7 @@ class Display:
elif self.config.BACK.match(key):
if self.current_presentation.current_slide_index == 0:
if self.current_presentation_index == 0:
self.current_presentation.rewind_current_slide()
self.current_presentation.load_previous_slide()
else:
self.current_presentation_index -= 1
self.current_presentation.load_last_slide()
@ -420,12 +443,42 @@ class Display:
self.current_presentation.reverse_current_slide()
self.state = State.PLAYING
elif self.config.REWIND.match(key):
self.current_presentation.cancel_reverse()
self.current_presentation.rewind_current_slide()
self.state = State.PLAYING
def quit(self):
"""Destroys all windows created by presentations and exits gracefully."""
cv2.destroyAllWindows()
if not self.record_to is None and len(self.recordings) > 0:
file, frame_number, fps = self.recordings[0]
cap = cv2.VideoCapture(file)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
_, frame = cap.read()
w, h = frame.shape[:2]
fourcc = cv2.VideoWriter_fourcc(*"XVID")
out = cv2.VideoWriter(self.record_to, fourcc, fps, (h, w))
out.write(frame)
for _file, frame_number, _ in tqdm(
self.recordings[1:], desc="Creating recording file", leave=False
):
if file != _file:
cap.release()
file = _file
cap = cv2.VideoCapture(_file)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
_, frame = cap.read()
out.write(frame)
cap.release()
out.release()
self.exit = True
@ -435,6 +488,7 @@ class Display:
default=FOLDER_PATH,
type=click.Path(exists=True, file_okay=False),
help="Set slides folder.",
show_default=True,
)
@click.help_option("-h", "--help")
def list_scenes(folder):
@ -454,7 +508,7 @@ def _list_scenes(folder) -> List[str]:
filepath = os.path.join(folder, file)
_ = PresentationConfig.parse_file(filepath)
scenes.append(os.path.basename(file)[:-5])
except Exception as e: # Could not parse this file as a proper presentation config
except Exception: # Could not parse this file as a proper presentation config
pass
return scenes
@ -468,6 +522,7 @@ def _list_scenes(folder) -> List[str]:
default=FOLDER_PATH,
type=click.Path(exists=True, file_okay=False),
help="Set slides folder.",
show_default=True,
)
@click.option("--start-paused", is_flag=True, help="Start paused.")
@click.option("--fullscreen", is_flag=True, help="Fullscreen mode.")
@ -477,17 +532,48 @@ def _list_scenes(folder) -> List[str]:
help="Skip all slides, useful the test if slides are working.",
)
@click.option(
"-r",
"--resolution",
type=(int, int),
default=(1280, 720),
help="Window resolution used if fullscreen is not set. You may manually resize the window afterward.",
default=(1920, 1080),
help="Window resolution WIDTH HEIGHT used if fullscreen is not set. You may manually resize the window afterward.",
show_default=True,
)
@click.option(
"-i",
"--interpolation-flag",
type=click.Choice(INTERPOLATION_FLAGS.keys(), case_sensitive=False),
default="linear",
help="Set the interpolation flag to be used when resizing image. See OpenCV cv::InterpolationFlags.",
show_default=True,
)
@click.option(
"--record-to",
type=click.Path(dir_okay=False),
default=None,
help="If set, the presentation will be recorded into a AVI video file with given name.",
)
@click.help_option("-h", "--help")
def present(
scenes, config_path, folder, start_paused, fullscreen, skip_all, resolution
scenes,
config_path,
folder,
start_paused,
fullscreen,
skip_all,
resolution,
interpolation_flag,
record_to,
):
"""Present the different scenes."""
"""
Present SCENE(s), one at a time, in order.
Each SCENE parameter must be the name of a Manim scene, with existing SCENE.json config file.
You can present the same SCENE multiple times by repeating the parameter.
Use `manim-slide list-scenes` to list all available scenes in a given folder.
"""
if len(scenes) == 0:
scene_choices = _list_scenes(folder)
@ -545,6 +631,13 @@ def present(
else:
config = Config()
if not record_to is None:
_, ext = os.path.splitext(record_to)
if ext.lower() != ".avi":
raise click.UsageError(
f"Recording only support '.avi' extension. For other video formats, please convert the resulting '.avi' file afterwards."
)
display = Display(
presentations,
config=config,
@ -552,5 +645,7 @@ def present(
fullscreen=fullscreen,
skip_all=skip_all,
resolution=resolution,
interpolation_flag=INTERPOLATION_FLAGS[interpolation_flag],
record_to=record_to,
)
display.run()

View File

@ -7,7 +7,7 @@ import numpy as np
from .commons import config_options
from .config import Config
from .defaults import CONFIG_PATH, FONT_ARGS, PIXELS_PER_CHARACTER
from .defaults import CONFIG_PATH, FONT_ARGS
WINDOW_NAME = "Manim Slides Configuration Wizard"
WINDOW_SIZE = (120, 620)
@ -38,6 +38,7 @@ def prompt(question: str) -> int:
@click.command()
@config_options
@click.help_option("-h", "--help")
def wizard(config_path, force, merge):
"""Launch configuration wizard."""
return _init(config_path, force, merge, skip_interactive=False)
@ -45,12 +46,14 @@ def wizard(config_path, force, merge):
@click.command()
@config_options
@click.help_option("-h", "--help")
def init(config_path, force, merge, skip_interactive=False):
"""Initialize a new default configuration file."""
return _init(config_path, force, merge, skip_interactive=True)
def _init(config_path, force, merge, skip_interactive=False):
"""Actual initialization code for configuration file, with optional interactive mode."""
if os.path.exists(config_path):
click.secho(f"The `{CONFIG_PATH}` configuration file exists")

2
pyproject.toml Normal file
View File

@ -0,0 +1,2 @@
[tool.vulture]
paths = ["manim_slides"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB