From 9a573f29f158cd61b42d369b1db50d1b6aff564f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Sat, 11 Mar 2023 18:05:07 +0100 Subject: [PATCH] feat(cli): add start at index options (#156) * wip: start at index * feat(cli): add start at index options * fix(ci): correct callback * chore(ci): fix type hint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(cli): typo in variable name... --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- manim_slides/present.py | 157 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 4 deletions(-) diff --git a/manim_slides/present.py b/manim_slides/present.py index d626c49..35cd56d 100644 --- a/manim_slides/present.py +++ b/manim_slides/present.py @@ -73,7 +73,7 @@ class Presentation: self.slides: List[SlideConfig] = config.slides self.files: List[str] = config.files - self.current_slide_index: int = 0 + self.__current_slide_index: int = 0 self.current_animation: int = self.current_slide.start_animation self.current_file: str = "" @@ -87,6 +87,46 @@ class Presentation: self.reset() + def __len__(self) -> int: + return len(self.slides) + + @property + def current_slide_index(self) -> int: + return self.__current_slide_index + + @current_slide_index.setter + def current_slide_index(self, value: Optional[int]): + if value: + if -len(self) <= value < len(self): + self.__current_slide_index = value + self.current_animation = self.current_slide.start_animation + else: + logger.error( + f"Could not load slide number {value}, playing first slide instead." + ) + + def set_current_animation_and_update_slide_number(self, value: Optional[int]): + if value: + n_files = len(self.files) + if -n_files <= value < n_files: + if value < 0: + value += n_files + + for i, slide in enumerate(self.slides): + if value < slide.end_animation: + self.current_slide_index = i + self.current_animation = value + return + + assert ( + False + ), f"An error occurred when setting the current animation to {value}, please create an issue on GitHub!" + + else: + logger.error( + f"Could not load animation number {value}, playing first animation instead." + ) + @property def current_slide(self) -> SlideConfig: """Returns currently playing slide.""" @@ -217,7 +257,7 @@ class Presentation: return self.current_animation + 1 @property - def is_last_animation(self) -> int: + def is_last_animation(self) -> bool: """Returns True if current animation is the last one of current slide.""" if self.reverse: return self.current_animation == self.current_slide.start_animation @@ -284,12 +324,15 @@ class Display(QThread): # type: ignore def __init__( self, - presentations: List[PresentationConfig], + presentations: List[Presentation], config: Config = DEFAULT_CONFIG, start_paused: bool = False, skip_all: bool = False, record_to: Optional[str] = None, exit_after_last_slide: bool = False, + start_at_scene_number: Optional[int] = None, + start_at_slide_number: Optional[int] = None, + start_at_animation_number: Optional[int] = None, ) -> None: super().__init__() self.presentations = presentations @@ -301,12 +344,36 @@ class Display(QThread): # type: ignore self.state = State.PLAYING self.lastframe: Optional[np.ndarray] = None - self.current_presentation_index = 0 + + self.__current_presentation_index = 0 + self.current_presentation_index = start_at_scene_number # type: ignore + self.current_presentation.current_slide_index = start_at_slide_number # type: ignore + self.current_presentation.set_current_animation_and_update_slide_number( + start_at_animation_number + ) + self.run_flag = True self.key = -1 self.exit_after_last_slide = exit_after_last_slide + def __len__(self) -> int: + return len(self.presentations) + + @property + def current_presentation_index(self) -> int: + return self.__current_presentation_index + + @current_presentation_index.setter + def current_presentation_index(self, value: Optional[int]): + if value: + if -len(self) <= value < len(self): + self.__current_presentation_index = value + else: + logger.error( + f"Could not load scene number {value}, playing first scene instead." + ) + @property def current_presentation(self) -> Presentation: """Returns the current presentation.""" @@ -718,6 +785,35 @@ def get_scenes_presentation_config( return presentation_configs +def start_at_callback(ctx, param, values: str) -> Tuple[Optional[int], ...]: + if values == "(None, None, None)": + return (None, None, None) + + def str_to_int_or_none(value: str) -> Optional[int]: + if value.lower().strip() == "": + return None + else: + try: + return int(value) + except ValueError: + raise click.BadParameter( + f"start index can only be an integer or an empty string, not `{value}`", + ctx=ctx, + param=param, + ) + + values_tuple = values.split(",") + n_values = len(values_tuple) + if n_values == 3: + return tuple(map(str_to_int_or_none, values_tuple)) + + raise click.BadParameter( + f"exactly 3 arguments are expected but you gave {n_values}, please use commas to separate them", + ctx=ctx, + param=param, + ) + + @click.command() @click.argument("scenes", nargs=-1) @config_path_option @@ -789,6 +885,43 @@ def get_scenes_presentation_config( 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)".', show_default=True, ) +@click.option( + "--sa", + "--start-at", + "start_at", + metavar="", + type=str, + callback=start_at_callback, + default=(None, None, None), + help="Start presenting at (x, y, z), equivalent to --sacn x --sasn y --saan z, and overrides values if not None.", +) +@click.option( + "--sacn", + "--start-at-scene-number", + "start_at_scene_number", + metavar="INDEX", + type=int, + default=None, + help="Start presenting at a given scene number (0 is first, -1 is last).", +) +@click.option( + "--sasn", + "--start-at-slide-number", + "start_at_slide_number", + metavar="INDEX", + type=int, + default=None, + help="Start presenting at a given slide number (0 is first, -1 is last).", +) +@click.option( + "--saan", + "--start-at-animation-number", + "start_at_animation_number", + metavar="INDEX", + type=int, + default=0, + help="Start presenting at a given animation number (0 is first, -1 is last). This conflicts with slide number since animation number is absolute to the presentation.", +) @click.help_option("-h", "--help") @verbosity_option def present( @@ -805,6 +938,10 @@ def present( aspect_ratio: str, resize_mode: str, background_color: str, + start_at: Tuple[Optional[int], Optional[int], Optional[int]], + start_at_scene_number: Optional[int], + start_at_slide_number: Optional[int], + start_at_animation_number: Optional[int], ) -> None: """ Present SCENE(s), one at a time, in order. @@ -840,6 +977,15 @@ def present( "Recording only support '.avi' extension. For other video formats, please convert the resulting '.avi' file afterwards." ) + if start_at[0]: + start_at_scene_number = start_at[0] + + if start_at[1]: + start_at_slide_number = start_at[1] + + if start_at[2]: + start_at_animation_number = start_at[2] + app = QApplication(sys.argv) app.setApplicationName("Manim Slides") a = App( @@ -855,6 +1001,9 @@ def present( 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, ) a.show() sys.exit(app.exec_())