mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-08-06 06:12:56 +08:00
feat(lib): change config file from json to toml (#224)
This PR introduces a **BREAKING CHANGE**: the general config file is now written in the TOML format. Keys are also placed under a shared subsection. Previous config files will be ignore. Migration from the previous format can be easily performed with `manim-slides init` and copy/pasting the key codes if necessary.
This commit is contained in:
@ -8,6 +8,7 @@ from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
import rtoml
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
Field,
|
||||
@ -46,7 +47,7 @@ def merge_basenames(files: List[FilePath]) -> Path:
|
||||
class Key(BaseModel): # type: ignore
|
||||
"""Represents a list of key codes, with optionally a name."""
|
||||
|
||||
ids: Set[PositiveInt]
|
||||
ids: List[PositiveInt] = Field(unique=True)
|
||||
name: Optional[str] = None
|
||||
|
||||
@field_validator("ids")
|
||||
@ -57,7 +58,7 @@ class Key(BaseModel): # type: ignore
|
||||
return ids
|
||||
|
||||
def set_ids(self, *ids: int) -> None:
|
||||
self.ids = set(ids)
|
||||
self.ids = list(set(ids))
|
||||
|
||||
def match(self, key_id: int) -> bool:
|
||||
m = key_id in self.ids
|
||||
@ -68,9 +69,7 @@ class Key(BaseModel): # type: ignore
|
||||
return m
|
||||
|
||||
|
||||
class Config(BaseModel): # type: ignore
|
||||
"""General Manim Slides config"""
|
||||
|
||||
class Keys(BaseModel): # type: ignore
|
||||
QUIT: Key = Key(ids=[Qt.Key_Q], name="QUIT")
|
||||
CONTINUE: Key = Key(ids=[Qt.Key_Right], name="CONTINUE / NEXT")
|
||||
BACK: Key = Key(ids=[Qt.Key_Left], name="BACK")
|
||||
@ -79,17 +78,6 @@ class Config(BaseModel): # type: ignore
|
||||
PLAY_PAUSE: Key = Key(ids=[Qt.Key_Space], name="PLAY / PAUSE")
|
||||
HIDE_MOUSE: Key = Key(ids=[Qt.Key_H], name="HIDE / SHOW MOUSE")
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path: Path) -> "Config":
|
||||
"""Reads a configuration from a file."""
|
||||
with open(path, "r") as f:
|
||||
return cls.model_validate_json(f.read()) # type: ignore
|
||||
|
||||
def to_file(self, path: Path) -> None:
|
||||
"""Dumps the configuration to a file."""
|
||||
with open(path, "w") as f:
|
||||
f.write(self.model_dump_json(indent=2))
|
||||
|
||||
@model_validator(mode="before")
|
||||
def ids_are_unique_across_keys(cls, values: Dict[str, Key]) -> Dict[str, Key]:
|
||||
ids: Set[int] = set()
|
||||
@ -103,15 +91,35 @@ class Config(BaseModel): # type: ignore
|
||||
|
||||
return values
|
||||
|
||||
def merge_with(self, other: "Config") -> "Config":
|
||||
def merge_with(self, other: "Keys") -> "Keys":
|
||||
for key_name, key in self:
|
||||
other_key = getattr(other, key_name)
|
||||
key.ids.update(other_key.ids)
|
||||
print(set(key.ids))
|
||||
key.ids = list(set(key.ids).union(other_key.ids))
|
||||
key.name = other_key.name or key.name
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class Config(BaseModel): # type: ignore
|
||||
"""General Manim Slides config"""
|
||||
|
||||
keys: Keys = Keys()
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path: Path) -> "Config":
|
||||
"""Reads a configuration from a file."""
|
||||
return cls.model_validate(rtoml.load(path)) # type: ignore
|
||||
|
||||
def to_file(self, path: Path) -> None:
|
||||
"""Dumps the configuration to a file."""
|
||||
rtoml.dump(self.model_dump(), path, pretty=True)
|
||||
|
||||
def merge_with(self, other: "Config") -> "Config":
|
||||
self.keys = self.keys.merge_with(other.keys)
|
||||
return self
|
||||
|
||||
|
||||
class SlideType(str, Enum):
|
||||
slide = "slide"
|
||||
loop = "loop"
|
||||
|
@ -1,3 +1,3 @@
|
||||
FOLDER_PATH: str = "./slides"
|
||||
CONFIG_PATH: str = ".manim-slides.json"
|
||||
CONFIG_PATH: str = ".manim-slides.toml"
|
||||
FFMPEG_BIN: str = "ffmpeg"
|
||||
|
@ -539,23 +539,24 @@ class Display(QThread): # type: ignore
|
||||
"""Handles key strokes."""
|
||||
|
||||
key = self.key
|
||||
keys = self.config.keys
|
||||
|
||||
if self.config.QUIT.match(key):
|
||||
if keys.QUIT.match(key):
|
||||
self.run_flag = False
|
||||
elif self.state == State.PLAYING and self.config.PLAY_PAUSE.match(key):
|
||||
elif self.state == State.PLAYING and keys.PLAY_PAUSE.match(key):
|
||||
self.state = State.PAUSED
|
||||
elif self.state == State.PAUSED and self.config.PLAY_PAUSE.match(key):
|
||||
elif self.state == State.PAUSED and keys.PLAY_PAUSE.match(key):
|
||||
self.state = State.PLAYING
|
||||
elif self.state == State.WAIT and (
|
||||
self.config.CONTINUE.match(key) or self.config.PLAY_PAUSE.match(key)
|
||||
keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key)
|
||||
):
|
||||
self.current_presentation.load_next_slide()
|
||||
self.state = State.PLAYING
|
||||
elif (
|
||||
self.state == State.PLAYING and self.config.CONTINUE.match(key)
|
||||
self.state == State.PLAYING and keys.CONTINUE.match(key)
|
||||
) or self.skip_all:
|
||||
self.current_presentation.load_next_slide()
|
||||
elif self.config.BACK.match(key):
|
||||
elif keys.BACK.match(key):
|
||||
if self.current_presentation.current_slide_index == 0:
|
||||
if self.current_presentation_index == 0:
|
||||
self.current_presentation.load_previous_slide()
|
||||
@ -566,10 +567,10 @@ class Display(QThread): # type: ignore
|
||||
else:
|
||||
self.current_presentation.load_previous_slide()
|
||||
self.state = State.PLAYING
|
||||
elif self.config.REVERSE.match(key):
|
||||
elif keys.REVERSE.match(key):
|
||||
self.current_presentation.reverse_current_slide()
|
||||
self.state = State.PLAYING
|
||||
elif self.config.REWIND.match(key):
|
||||
elif keys.REWIND.match(key):
|
||||
self.current_presentation.cancel_reverse()
|
||||
self.current_presentation.rewind_current_slide()
|
||||
self.state = State.PLAYING
|
||||
@ -705,7 +706,7 @@ class App(QWidget): # type: ignore
|
||||
|
||||
def keyPressEvent(self, event: QKeyEvent) -> None:
|
||||
key = event.key()
|
||||
if self.config.HIDE_MOUSE.match(key):
|
||||
if self.config.keys.HIDE_MOUSE.match(key):
|
||||
if self.hide_mouse:
|
||||
self.setCursor(Qt.ArrowCursor)
|
||||
self.hide_mouse = False
|
||||
|
Reference in New Issue
Block a user