diff --git a/manim_slides/config.py b/manim_slides/config.py index d93e27c..69f30e2 100644 --- a/manim_slides/config.py +++ b/manim_slides/config.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Dict, List, Optional, Set, Tuple, Union from pydantic import BaseModel, FilePath, PositiveInt, root_validator, validator +from pydantic.color import Color from PySide6.QtCore import Qt from .defaults import FFMPEG_BIN @@ -150,6 +151,7 @@ class PresentationConfig(BaseModel): # type: ignore slides: List[SlideConfig] files: List[FilePath] resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080) + background_color: Color = "black" @root_validator def animation_indices_match_files( diff --git a/manim_slides/convert.py b/manim_slides/convert.py index 03f5522..4e867a7 100644 --- a/manim_slides/convert.py +++ b/manim_slides/convert.py @@ -321,9 +321,9 @@ class RevealJS(Converter): # Read more about this: # https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#autoplay_and_autoplay_blocking if slide_config.is_loop(): - yield f'
' + yield f'
' else: - yield f'
' + yield f'
' def load_template(self) -> str: """Returns the RevealJS HTML template as a string.""" diff --git a/manim_slides/present.py b/manim_slides/present.py index e635c1c..2e9730b 100644 --- a/manim_slides/present.py +++ b/manim_slides/present.py @@ -11,6 +11,7 @@ import cv2 import numpy as np from click import Context, Parameter from pydantic import ValidationError +from pydantic.color import Color from PySide6.QtCore import Qt, QThread, Signal, Slot from PySide6.QtGui import QCloseEvent, QIcon, QImage, QKeyEvent, QPixmap, QResizeEvent from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget @@ -105,6 +106,11 @@ class Presentation: """Returns the resolution.""" return self.config.resolution + @property + def background_color(self) -> Color: + """Returns the background color.""" + return self.config.background_color + @property def current_slide_index(self) -> int: return self.__current_slide_index @@ -411,6 +417,11 @@ class Display(QThread): # type: ignore """Returns the resolution of the current presentation.""" 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: """Runs a series of presentations until end or exit.""" while self.run_flag: @@ -649,7 +660,9 @@ class App(QWidget): # type: ignore 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}") + self.label.setStyleSheet( + f"background-color: {self.thread.current_background_color}" + ) self.pixmap = QPixmap(self.width(), self.height()) self.label.setPixmap(self.pixmap) @@ -729,6 +742,9 @@ class App(QWidget): # type: ignore self.display_width, self.display_height = self.thread.current_resolution if not self.isFullScreen(): self.resize(self.display_width, self.display_height) + self.label.setStyleSheet( + f"background-color: {self.thread.current_background_color}" + ) @click.command() @@ -924,8 +940,8 @@ def start_at_callback( "background_color", metavar="COLOR", type=str, - default="black", - 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)".', + 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)". If not set, it defaults to the background color configured in the Manim scene.', show_default=True, ) @click.option( @@ -980,7 +996,7 @@ def present( hide_mouse: bool, aspect_ratio: str, resize_mode: str, - background_color: str, + background_color: Optional[str], start_at: Tuple[Optional[int], Optional[int], Optional[int]], start_at_scene_number: Optional[int], start_at_slide_number: Optional[int], @@ -1005,6 +1021,10 @@ def present( for presentation_config in presentation_configs: presentation_config.resolution = resolution + if background_color is not None: + for presentation_config in presentation_configs: + presentation_config.background_color = background_color + presentations = [ Presentation(presentation_config) for presentation_config in presentation_configs @@ -1048,7 +1068,6 @@ def present( hide_mouse=hide_mouse, aspect_ratio=ASPECT_RATIO_MODES[aspect_ratio], resize_mode=RESIZE_MODES[resize_mode], - background_color=background_color, start_at_scene_number=start_at_scene_number, start_at_slide_number=start_at_slide_number, start_at_animation_number=start_at_animation_number, diff --git a/manim_slides/slide.py b/manim_slides/slide.py index 954d378..8a70e83 100644 --- a/manim_slides/slide.py +++ b/manim_slides/slide.py @@ -54,6 +54,14 @@ class Slide(Scene): # type:ignore self.__loop_start_animation: Optional[int] = None 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 def __resolution(self) -> Tuple[int, int]: """Returns the scene's resolution used during rendering.""" @@ -321,7 +329,10 @@ class Slide(Scene): # type:ignore with open(slide_path, "w") as f: f.write( 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) )