mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-07-02 12:57:26 +08:00
feat(lib): add playback rate config options (#320)
* feat(lib): add playback rate config options Basic playback rate config options, closes #309 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added the `manim-slides render` command
|
- Added the `manim-slides render` command
|
||||||
to render slides using correct Manim installation.
|
to render slides using correct Manim installation.
|
||||||
[#317](https://github.com/jeertmans/manim-slides/pull/317)
|
[#317](https://github.com/jeertmans/manim-slides/pull/317)
|
||||||
|
- Added the `playback-rate` and `reversed-playback-rate` options
|
||||||
|
to slide config.
|
||||||
|
[#320](https://github.com/jeertmans/manim-slides/pull/320)
|
||||||
|
|
||||||
## [v5](https://github.com/jeertmans/manim-slides/compare/v4.16.0...v5.0.0)
|
## [v5](https://github.com/jeertmans/manim-slides/compare/v4.16.0...v5.0.0)
|
||||||
|
|
||||||
|
@ -34,9 +34,7 @@ def cli(notify_outdated_version: bool) -> None:
|
|||||||
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
|
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
|
||||||
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
|
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
|
||||||
try:
|
try:
|
||||||
req_info: requests.models.Response = requests.get(
|
req_info: requests.models.Response = requests.get(manim_info_url, timeout=2)
|
||||||
manim_info_url, timeout=2
|
|
||||||
)
|
|
||||||
req_info.raise_for_status()
|
req_info.raise_for_status()
|
||||||
stable = req_info.json()["info"]["version"]
|
stable = req_info.json()["info"]["version"]
|
||||||
if stable != __version__:
|
if stable != __version__:
|
||||||
|
@ -140,6 +140,8 @@ class PreSlideConfig(BaseModel): # type: ignore
|
|||||||
end_animation: int
|
end_animation: int
|
||||||
loop: bool = False
|
loop: bool = False
|
||||||
auto_next: bool = False
|
auto_next: bool = False
|
||||||
|
playback_rate: float = 1.0
|
||||||
|
reversed_playback_rate: float = 1.0
|
||||||
|
|
||||||
@field_validator("start_animation", "end_animation")
|
@field_validator("start_animation", "end_animation")
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -190,6 +192,8 @@ class SlideConfig(BaseModel): # type: ignore[misc]
|
|||||||
rev_file: FilePath
|
rev_file: FilePath
|
||||||
loop: bool = False
|
loop: bool = False
|
||||||
auto_next: bool = False
|
auto_next: bool = False
|
||||||
|
playback_rate: float = 1.0
|
||||||
|
reversed_playback_rate: float = 1.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_pre_slide_config_and_files(
|
def from_pre_slide_config_and_files(
|
||||||
@ -200,6 +204,8 @@ class SlideConfig(BaseModel): # type: ignore[misc]
|
|||||||
rev_file=rev_file,
|
rev_file=rev_file,
|
||||||
loop=pre_slide_config.loop,
|
loop=pre_slide_config.loop,
|
||||||
auto_next=pre_slide_config.auto_next,
|
auto_next=pre_slide_config.auto_next,
|
||||||
|
playback_rate=pre_slide_config.playback_rate,
|
||||||
|
reversed_playback_rate=pre_slide_config.reversed_playback_rate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,6 +236,13 @@ class Player(QMainWindow): # type: ignore[misc]
|
|||||||
url = QUrl.fromLocalFile(self.current_file)
|
url = QUrl.fromLocalFile(self.current_file)
|
||||||
self.media_player.setSource(url)
|
self.media_player.setSource(url)
|
||||||
|
|
||||||
|
if self.playing_reversed_slide:
|
||||||
|
self.media_player.setPlaybackRate(
|
||||||
|
self.current_slide_config.reversed_playback_rate
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.media_player.setPlaybackRate(self.current_slide_config.playback_rate)
|
||||||
|
|
||||||
if start_paused:
|
if start_paused:
|
||||||
self.media_player.pause()
|
self.media_player.pause()
|
||||||
else:
|
else:
|
||||||
|
@ -255,7 +255,13 @@ class BaseSlide:
|
|||||||
self._current_animation += 1
|
self._current_animation += 1
|
||||||
|
|
||||||
def next_slide(
|
def next_slide(
|
||||||
self, *, loop: bool = False, auto_next: bool = False, **kwargs: Any
|
self,
|
||||||
|
*,
|
||||||
|
loop: bool = False,
|
||||||
|
auto_next: bool = False,
|
||||||
|
playback_rate: float = 1.0,
|
||||||
|
reversed_playback_rate: float = 1.0,
|
||||||
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a new slide with previous animations, and setup options
|
Create a new slide with previous animations, and setup options
|
||||||
@ -276,6 +282,14 @@ class BaseSlide:
|
|||||||
|
|
||||||
Note that this is only supported by ``manim-slides present``
|
Note that this is only supported by ``manim-slides present``
|
||||||
and ``manim-slides convert --to=html``.
|
and ``manim-slides convert --to=html``.
|
||||||
|
:param playback_rate:
|
||||||
|
Playback rate at which the video is played.
|
||||||
|
|
||||||
|
Note that this is only supported by ``manim-slides present``.
|
||||||
|
:param reversed_playback_rate:
|
||||||
|
Playback rate at which the reversed video is played.
|
||||||
|
|
||||||
|
Note that this is only supported by ``manim-slides present``.
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
Keyword arguments to be passed to
|
Keyword arguments to be passed to
|
||||||
:meth:`Scene.next_section<manim.scene.scene.Scene.next_section>`,
|
:meth:`Scene.next_section<manim.scene.scene.Scene.next_section>`,
|
||||||
@ -375,7 +389,12 @@ class BaseSlide:
|
|||||||
|
|
||||||
self._current_slide += 1
|
self._current_slide += 1
|
||||||
|
|
||||||
self._pre_slide_config_kwargs = dict(loop=loop, auto_next=auto_next)
|
self._pre_slide_config_kwargs = dict(
|
||||||
|
loop=loop,
|
||||||
|
auto_next=auto_next,
|
||||||
|
playback_rate=playback_rate,
|
||||||
|
reversed_playback_rate=reversed_playback_rate,
|
||||||
|
)
|
||||||
self._start_animation = self._current_animation
|
self._start_animation = self._current_animation
|
||||||
|
|
||||||
def _add_last_slide(self) -> None:
|
def _add_last_slide(self) -> None:
|
||||||
|
@ -80,10 +80,22 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
|
|||||||
self.next_slide(*args, **kwargs)
|
self.next_slide(*args, **kwargs)
|
||||||
|
|
||||||
def next_slide(
|
def next_slide(
|
||||||
self, *args: Any, loop: bool = False, auto_next: bool = False, **kwargs: Any
|
self,
|
||||||
|
*args: Any,
|
||||||
|
loop: bool = False,
|
||||||
|
auto_next: bool = False,
|
||||||
|
playback_rate: float = 1.0,
|
||||||
|
reversed_playback_rate: float = 1.0,
|
||||||
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
Scene.next_section(self, *args, **kwargs)
|
Scene.next_section(self, *args, **kwargs)
|
||||||
BaseSlide.next_slide(self, loop=loop, auto_next=auto_next)
|
BaseSlide.next_slide(
|
||||||
|
self,
|
||||||
|
loop=loop,
|
||||||
|
auto_next=auto_next,
|
||||||
|
playback_rate=playback_rate,
|
||||||
|
reversed_playback_rate=reversed_playback_rate,
|
||||||
|
)
|
||||||
|
|
||||||
def render(self, *args: Any, **kwargs: Any) -> None:
|
def render(self, *args: Any, **kwargs: Any) -> None:
|
||||||
"""MANIM render."""
|
"""MANIM render."""
|
||||||
|
@ -182,6 +182,20 @@ class TestSlide:
|
|||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
self.next_slide()
|
self.next_slide()
|
||||||
|
|
||||||
|
@assert_constructs
|
||||||
|
class TestPlaybackRate(Slide):
|
||||||
|
def construct(self) -> None:
|
||||||
|
text = Text("Some text")
|
||||||
|
|
||||||
|
self.add(text)
|
||||||
|
|
||||||
|
assert "playback_rate" not in self._pre_slide_config_kwargs
|
||||||
|
|
||||||
|
self.next_slide(playback_rate=2.0)
|
||||||
|
self.play(text.animate.scale(2))
|
||||||
|
|
||||||
|
assert self._pre_slide_config_kwargs["playback_rate"] == 2.0
|
||||||
|
|
||||||
@assert_constructs
|
@assert_constructs
|
||||||
class TestWipe(Slide):
|
class TestWipe(Slide):
|
||||||
def construct(self) -> None:
|
def construct(self) -> None:
|
||||||
|
Reference in New Issue
Block a user