Skip to content

Commit 16f740d

Browse files
chore(deps): make Qt backend optional (#350)
* chore(deps): make Qt backend optional TODO: - [ ] Add relevant entry in CHANGELOG - [ ] Update install documentation - [ ] Make sure `manim-slides convert` can run without any Qt backend - [ ] Make sure test suite works (partially) without any Qt backend - [ ] Make sure we can import `manim_slides` without any Qt backend * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(deps): some fixes but wip * chore(docs): update * chore(deps): support PyQt6 * chore(deps): make Qt backend optional TODO: - [ ] Add relevant entry in CHANGELOG - [ ] Update install documentation - [ ] Make sure `manim-slides convert` can run without any Qt backend - [ ] Make sure test suite works (partially) without any Qt backend - [ ] Make sure we can import `manim_slides` without any Qt backend * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(deps): some fixes but wip * chore(docs): update * chore(deps): support PyQt6 * fix(deps): ci and docs * fix(lib): missing package * chore(ci): does it work? * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(test): skip failing * chore(docs): update * chore(docs): update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(docs): typo * fix(test): quit instead of shutdown --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent f260d0d commit 16f740d

File tree

21 files changed

+935
-822
lines changed

21 files changed

+935
-822
lines changed

.github/workflows/pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
uses: nikeee/setup-pandoc@v1
5353

5454
- name: Install local Python package
55-
run: pdm install -dGdocs -dGgithub-action
55+
run: pdm sync -Gdocs -Ggithub-action
5656

5757
- name: Install IPython kernel
5858
run: pdm run ipython kernel install --name "manim-slides" --user

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
os: [macos-latest, ubuntu-latest, windows-latest]
16-
pyversion: ['3.8', '3.9', '3.10', '3.11']
16+
pyversion: ['3.9', '3.10', '3.11', '3.12']
1717
runs-on: ${{ matrix.os }}
1818
env:
1919
QT_QPA_PLATFORM: offscreen
@@ -69,7 +69,7 @@ jobs:
6969

7070
- name: Install Manim Slides
7171
run: |
72-
pdm install -dGgithub-action -dGtest
72+
pdm sync -Ggithub-action -Gtest
7373
7474
- name: Run pytest
7575
if: matrix.os != 'ubuntu-latest' || matrix.pyversion != '3.11'

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5252
[#335](https://github.com/jeertmans/manim-slides/pull/335)
5353
- Changed build backend to PDM and reflected on docs.
5454
[#354](https://github.com/jeertmans/manim-slides/pull/354)
55+
- Dropped Python 3.8 support.
56+
[#350](https://github.com/jeertmans/manim-slides/pull/350)
57+
- Made Qt backend optional and support PyQt6 too.
58+
[#350](https://github.com/jeertmans/manim-slides/pull/350)
5559
- Documentated how to create and use a custom HTML template.
5660
[#357](https://github.com/jeertmans/manim-slides/pull/357)
5761

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,15 @@ manim-slides render example.py BasicExample
8888
# or use ManimGL
8989
manim-slides render --GL example.py BasicExample
9090
```
91+
<!-- end usage -->
9192

9293
> [!NOTE]
93-
> Using `manim-slides render` makes sure the use the `manim`
94-
> (or `manimlib`) library that was installed in the Python same environment.
95-
> Put simply, this is a wrapper of `manim render [ARGS]...` (or `manimgl [ARGS]...`).
94+
> Using `manim-slides render` makes sure to use the `manim`
95+
> (or `manimlib`) library that was installed in the same Python environment.
96+
> Put simply, this is a wrapper around
97+
> `manim render [ARGS]...` (or `manimgl [ARGS]...`).
98+
99+
<!-- start more-usage -->
96100

97101
To start the presentation using `Scene1`, `Scene2` and so on, run:
98102

@@ -106,7 +110,7 @@ In our example:
106110
manim-slides BasicExample
107111
```
108112

109-
<!-- end usage -->
113+
<!-- end more-usage -->
110114

111115
<p align="center">
112116
<img alt="Example GIF" src="https://raw.githubusercontent.com/jeertmans/manim-slides/main/static/example.gif">

docs/source/contributing/workflow.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ pdm install -Gmanimgl # For ManimGL
4848
Additionnally, Manim Slides comes with groups of dependencies for development purposes:
4949

5050
```bash
51-
pdm install -dGdev # For linters and formatters
51+
pdm install -Gdev # For linters and formatters
5252
# or
53-
pdm install --dGdocs # To build the documentation locally
53+
pdm install -Gdocs # To build the documentation locally
5454
# or
55-
pdm install --dGtests # To run tests
55+
pdm install -Gtest # To run tests
5656
```
5757

5858
:::{note}

docs/source/installation.md

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,23 @@ If you install Manim from its git repository, as suggested by ManimGL,
3434
make sure to first check out a supported version (e.g., `git checkout tags/v1.6.1`
3535
for ManimGL), otherwise it might install an unsupported version of Manim!
3636
See [#314](https://github.com/jeertmans/manim-slides/issues/314).
37+
38+
Also, note that ManimGL uses outdated dependencies, and may
39+
not work out-of-the-box. One example is NumPy: ManimGL
40+
does not specify any restriction on this package, but
41+
only `numpy<1.25` will work, see
42+
[#2053](https://github.com/3b1b/manim/issues/2053).
3743
:::
3844

3945
<!-- end deps -->
4046

4147
## Pip Install
4248

43-
The recommended way to install the latest release is to use pip:
49+
The recommended way to install the latest release
50+
with all features is to use pipx:
4451

4552
```bash
46-
pipx install -U manim-slides
53+
pipx install -U "manim-slides[pyside6-full]"
4754
```
4855

4956
:::{tip}
@@ -52,6 +59,28 @@ like to upgrade to the latest version available,
5259
if Manim Slides is already installed.
5360
:::
5461

62+
:::{note}
63+
The quotes `"` are added because not all shell support unquoted
64+
brackets (e.g., zsh) or commas (e.g., Windows).
65+
:::
66+
67+
You can check that Manim Slides was correctly installed with:
68+
69+
```bash
70+
manim-slides --version
71+
```
72+
73+
## Custom install
74+
75+
If you want more control on what dependencies are installed,
76+
you can always install the bare minimal dependencies with:
77+
78+
```bash
79+
pipx install -U manim-slides
80+
```
81+
82+
And install additional dependencies later.
83+
5584
Optionally, you can also install Manim or ManimGL using extras[^1]:
5685

5786
```bash
@@ -60,11 +89,8 @@ pipx install -U "manim-slides[manim]" # For Manim
6089
pipx install -U "manim-slides[manimgl]" # For ManimGL
6190
```
6291

63-
You can check that Manim Slides was correctly installed with:
64-
65-
```bash
66-
manim-slides --version
67-
```
92+
For optional dependencies documentation, see
93+
[next section](#optional-dependencies).
6894

6995
:::{warning}
7096
If you are installing with pipx, this is mandatory to at least include
@@ -74,35 +100,58 @@ either `manim` or `manimgl`.
74100
[^1]: You still need to have Manim or ManimGL platform-specific dependencies
75101
installed on your computer.
76102

77-
## Optional Dependencies
103+
## Optional dependencies
78104

79105
Along with the optional dependencies for Manim and ManimGL,
80106
Manim Slides offers additional *extras*, that can be activated
81107
using optional dependencies:
82108

109+
- `full`, to include `magic`, `manim`, `manimgl`, and
110+
`sphinx-directive` extras (see below);
83111
- `magic`, to include a Jupyter magic to render
84112
animations inside notebooks. This automatically installs `manim`,
85113
and does not work with ManimGL;
86114
- `manim` and `manimgl`, for installing the corresponding
87115
dependencies;
116+
- `pyqt6` to include PyQt6 Qt bindings. Those bindings are available
117+
on most platforms and Python version, but produce a weird black
118+
screen between slide with `manim-slides present`,
119+
see [#QTBUG-118501](https://bugreports.qt.io/browse/QTBUG-118501);
120+
- `pyqt6-full` to include `full` and `pyqt6`;
121+
- `pyside6` to include PySide6 Qt bindings. Those bindings are available
122+
on most platforms and Python version, except on Python 3.12[^2];
123+
- `pyside6-full` to include `full` and `pyside6`;
88124
- `sphinx-directive`, to generate presentation inside your Sphinx
89125
documentation. This automatically installs `manim`,
90-
and does not work with ManimGL;
126+
and does not work with ManimGL.
91127

92128
Installing those extras can be done with the following syntax:
93129

94130
```bash
95131
pipx install -U "manim-slides[extra1,extra2]"
96132
```
97133

98-
:::{note}
99-
The quotes `"` are added because not all shell support unquoted
100-
brackets (e.g., zsh) or commas (e.g., Windows).
101-
:::
134+
[^2]: Actually, PySide6 can be installed on Python 3.12, but you will then
135+
observe the same visual bug as with PyQt6.
136+
137+
## When you need a Qt backend
138+
139+
Before `v5.1`, Manim Slides automatically included PySide6 as
140+
a Qt backend. As only `manim-slides present` and `manim-slides wizard`
141+
command need a graphical library, and installing PySide6 on all platforms
142+
and Python version can be sometimes complicated, Manim Slides chooses
143+
**not to include** any Qt backend.
144+
145+
The use can choose between PySide6 (best) and PyQt6, depending on their
146+
availability and licensing rules.
147+
148+
As of `v5.1`, you **need** to have Qt bindings installed to use
149+
`manim-slides present` or `manim-slides wizard`. The recommended way to
150+
install those are via optional dependencies, as explained above.
102151

103-
## Install From Repository
152+
## Install from source
104153

105154
An alternative way to install Manim Slides is to clone the git repository,
106-
and install from there: read the
155+
and build the package from source. Read the
107156
[contributing guide](./contributing/workflow)
108157
to know how to process.

docs/source/quickstart.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ see [installation](./installation).
1010
:end-before: <!-- end usage -->
1111
```
1212

13+
:::{note}
14+
Using `manim-slides render` makes sure to use the `manim`
15+
(or `manimlib`) library that was installed in the same Python environment.
16+
Put simply, this is a wrapper around
17+
`manim render [ARGS]...` (or `manimgl [ARGS]...`).
18+
:::
19+
20+
21+
```{include} ../../README.md
22+
:start-after: <!-- start more-usage -->
23+
:end-before: <!-- end more-usage -->
24+
```
25+
1326
The output slides should look this this:
1427

1528
```{eval-rst}

manim_slides/config.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
model_validator,
1818
)
1919
from pydantic_extra_types.color import Color
20-
from PySide6.QtCore import Qt
2120

2221
from .logger import logger
2322

@@ -38,6 +37,13 @@ def emit(self, *args: Any) -> None:
3837
receiver(*args)
3938

4039

40+
def key_id(name: str) -> PositiveInt:
41+
"""Avoid importing Qt too early."""
42+
from qtpy.QtCore import Qt
43+
44+
return getattr(Qt, f"Key_{name}")
45+
46+
4147
class Key(BaseModel): # type: ignore[misc]
4248
"""Represents a list of key codes, with optionally a name."""
4349

@@ -73,14 +79,22 @@ def connect(self, function: Receiver) -> None:
7379

7480

7581
class Keys(BaseModel): # type: ignore[misc]
76-
QUIT: Key = Key(ids=[Qt.Key_Q], name="QUIT")
77-
PLAY_PAUSE: Key = Key(ids=[Qt.Key_Space], name="PLAY / PAUSE")
78-
NEXT: Key = Key(ids=[Qt.Key_Right], name="NEXT")
79-
PREVIOUS: Key = Key(ids=[Qt.Key_Left], name="PREVIOUS")
80-
REVERSE: Key = Key(ids=[Qt.Key_V], name="REVERSE")
81-
REPLAY: Key = Key(ids=[Qt.Key_R], name="REPLAY")
82-
FULL_SCREEN: Key = Key(ids=[Qt.Key_F], name="TOGGLE FULL SCREEN")
83-
HIDE_MOUSE: Key = Key(ids=[Qt.Key_H], name="HIDE / SHOW MOUSE")
82+
QUIT: Key = Field(default_factory=lambda: Key(ids=[key_id("Q")], name="QUIT"))
83+
PLAY_PAUSE: Key = Field(
84+
default_factory=lambda: Key(ids=[key_id("Space")], name="PLAY / PAUSE")
85+
)
86+
NEXT: Key = Field(default_factory=lambda: Key(ids=[key_id("Right")], name="NEXT"))
87+
PREVIOUS: Key = Field(
88+
default_factory=lambda: Key(ids=[key_id("Left")], name="PREVIOUS")
89+
)
90+
REVERSE: Key = Field(default_factory=lambda: Key(ids=[key_id("V")], name="REVERSE"))
91+
REPLAY: Key = Field(default_factory=lambda: Key(ids=[key_id("R")], name="REPLAY"))
92+
FULL_SCREEN: Key = Field(
93+
default_factory=lambda: Key(ids=[key_id("F")], name="TOGGLE FULL SCREEN")
94+
)
95+
HIDE_MOUSE: Key = Field(
96+
default_factory=lambda: Key(ids=[key_id("H")], name="HIDE / SHOW MOUSE")
97+
)
8498

8599
@model_validator(mode="before")
86100
@classmethod
@@ -121,7 +135,7 @@ def dispatch(key: PositiveInt) -> None:
121135
class Config(BaseModel): # type: ignore[misc]
122136
"""General Manim Slides config."""
123137

124-
keys: Keys = Keys()
138+
keys: Keys = Field(default_factory=Keys)
125139

126140
@classmethod
127141
def from_file(cls, path: Path) -> "Config":
@@ -326,6 +340,3 @@ def copy_to(
326340
shutil.copy(rev_file, rev_dest)
327341

328342
return self
329-
330-
331-
DEFAULT_CONFIG = Config()

manim_slides/convert.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,14 +648,16 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None:
648648
"config_options",
649649
multiple=True,
650650
callback=validate_config_option,
651-
help="Configuration options passed to the converter. E.g., pass `-cslide_number=true` to display slide numbers.",
651+
help="Configuration options passed to the converter. "
652+
"E.g., pass ``-cslide_number=true`` to display slide numbers.",
652653
)
653654
@click.option(
654655
"--use-template",
655656
"template",
656657
metavar="FILE",
657658
type=click.Path(exists=True, dir_okay=False, path_type=Path),
658-
help="Use the template given by FILE instead of default one. To echo the default template, use `--show-template`.",
659+
help="Use the template given by FILE instead of default one. "
660+
"To echo the default template, use ``--show-template``.",
659661
)
660662
@show_template_option
661663
@show_config_options

manim_slides/present/__init__.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,10 @@
66
import click
77
from click import Context, Parameter
88
from pydantic import ValidationError
9-
from PySide6.QtCore import Qt
109

1110
from ..commons import config_path_option, folder_path_option, verbosity_option
1211
from ..config import Config, PresentationConfig
1312
from ..logger import logger
14-
from ..qt_utils import qapp
15-
from .player import Player
16-
17-
ASPECT_RATIO_MODES = {
18-
"keep": Qt.KeepAspectRatio,
19-
"ignore": Qt.IgnoreAspectRatio,
20-
}
2113

2214

2315
@click.command()
@@ -130,7 +122,8 @@ def str_to_int_or_none(value: str) -> Optional[int]:
130122
return tuple(map(str_to_int_or_none, values_tuple))
131123

132124
raise click.BadParameter(
133-
f"exactly 2 arguments are expected but you gave {n_values}, please use commas to separate them",
125+
f"exactly 2 arguments are expected but you gave {n_values}, "
126+
"please use commas to separate them",
134127
ctx=ctx,
135128
param=param,
136129
)
@@ -283,6 +276,8 @@ def present(
283276
if start_at[1]:
284277
start_at_slide_number = start_at[1]
285278

279+
from ..qt_utils import qapp
280+
286281
app = qapp()
287282
app.setApplicationName("Manim Slides")
288283

@@ -298,6 +293,15 @@ def present(
298293
else:
299294
screen = None
300295

296+
from qtpy.QtCore import Qt
297+
298+
aspect_ratio_modes = {
299+
"keep": Qt.KeepAspectRatio,
300+
"ignore": Qt.IgnoreAspectRatio,
301+
}
302+
303+
from .player import Player
304+
301305
player = Player(
302306
config,
303307
presentation_configs,
@@ -306,7 +310,7 @@ def present(
306310
skip_all=skip_all,
307311
exit_after_last_slide=exit_after_last_slide,
308312
hide_mouse=hide_mouse,
309-
aspect_ratio_mode=ASPECT_RATIO_MODES[aspect_ratio],
313+
aspect_ratio_mode=aspect_ratio_modes[aspect_ratio],
310314
presentation_index=start_at_scene_number,
311315
slide_index=start_at_slide_number,
312316
screen=screen,

0 commit comments

Comments
 (0)