| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- # -*- coding: utf-8 -*-
- """
- LED Animations-Framework.
- Definiert die IAnimation-Basisklasse und die AnimationRegistry
- fuer die Verwaltung verfuegbarer LED-Animationen.
- """
- from __future__ import annotations
- from abc import ABC, abstractmethod
- from collections.abc import Iterator
- from trixy_core.led.layouts import LEDLayout
- from trixy_core.utils.debug import pdebug, pwarn
- # RGB Tupel Type-Alias
- RGB = tuple[int, int, int]
- class IAnimation(ABC):
- """Basis-Klasse fuer alle LED-Animationen.
- Animationen erzeugen eine Folge von Frames als Generator.
- Jeder Frame ist eine Liste von RGB-Tupeln (ein Tupel pro LED).
- Klassen-Attribute:
- NAME: Registry-Name der Animation (z.B. "solid", "breathe").
- CONTINUOUS: True fuer endlose Animationen, False fuer einmalige.
- """
- NAME: str = ""
- CONTINUOUS: bool = False
- @abstractmethod
- def frames(
- self,
- led_count: int,
- color: RGB,
- speed: int,
- layout: LEDLayout,
- **opts: object,
- ) -> Iterator[list[RGB]]:
- """Generiert die Animation als Folge von Frames.
- Jeder Frame ist eine komplette Liste von RGB-Werten fuer
- alle LEDs. Der Aufrufer steuert das Timing zwischen den
- Frames anhand des speed-Parameters.
- Args:
- led_count: Anzahl der LEDs im Strip/Ring.
- color: Hauptfarbe als (R, G, B) Tupel.
- speed: Geschwindigkeit in Millisekunden pro Frame-Schritt.
- layout: LED-Layout mit ring_indices und center_index.
- **opts: Zusaetzliche Parameter je nach Animation
- (z.B. center_color, tail, direction).
- Yields:
- Frame als Liste von RGB-Tupeln, ein Tupel pro LED.
- Bei CONTINUOUS=True: endloser Generator (laeuft bis Abbruch).
- Bei CONTINUOUS=False: endliche Sequenz.
- """
- ...
- class AnimationRegistry:
- """Registry fuer verfuegbare LED-Animationen.
- Verwaltet eine Zuordnung von Animations-Namen zu ihren Klassen.
- Beim Start werden die eingebauten Animationen automatisch
- registriert, zusaetzliche koennen zur Laufzeit hinzugefuegt werden.
- """
- def __init__(self) -> None:
- self._animations: dict[str, type[IAnimation]] = {}
- def register(self, cls: type[IAnimation]) -> None:
- """Registriert eine Animations-Klasse.
- Verwendet den NAME-Klassenattribut als Schluessel.
- Warnt bei Ueberschreibung eines bereits registrierten Namens.
- Args:
- cls: Animations-Klasse (muss NAME gesetzt haben).
- """
- name = cls.NAME
- if not name:
- pwarn(
- f"Animation {cls.__name__} hat keinen NAME — "
- f"wird nicht registriert"
- )
- return
- if name in self._animations:
- pdebug(
- f"Animation '{name}' wird ueberschrieben: "
- f"{self._animations[name].__name__} → {cls.__name__}"
- )
- self._animations[name] = cls
- pdebug(f"Animation registriert: '{name}' ({cls.__name__})")
- def get(self, name: str) -> type[IAnimation] | None:
- """Gibt die Animations-Klasse fuer den angegebenen Namen zurueck.
- Args:
- name: Registry-Name der Animation.
- Returns:
- Animations-Klasse oder None wenn nicht gefunden.
- """
- return self._animations.get(name)
- def list_names(self) -> list[str]:
- """Gibt alle registrierten Animations-Namen zurueck.
- Returns:
- Sortierte Liste der verfuegbaren Animations-Namen.
- """
- return sorted(self._animations.keys())
- def register_builtins(self) -> None:
- """Registriert alle eingebauten Animationen.
- Importiert die Animations-Module und registriert alle
- darin definierten IAnimation-Unterklassen. Fehlende Module
- werden mit einer Warnung uebersprungen.
- """
- builtin_modules = [
- ".static",
- ".continuous",
- ".circular",
- ".effects",
- ]
- for module_name in builtin_modules:
- try:
- import importlib
- full_name = f"trixy_core.led.animations{module_name}"
- module = importlib.import_module(full_name)
- # Alle IAnimation-Unterklassen im Modul registrieren
- for attr_name in dir(module):
- attr = getattr(module, attr_name)
- if (
- isinstance(attr, type)
- and issubclass(attr, IAnimation)
- and attr is not IAnimation
- and attr.NAME
- ):
- self.register(attr)
- except ImportError:
- pdebug(
- f"Animations-Modul '{module_name}' nicht gefunden — "
- f"uebersprungen"
- )
- except Exception as e:
- pwarn(
- f"Fehler beim Laden von Animations-Modul "
- f"'{module_name}': {e}"
- )
- __all__ = [
- "RGB",
- "IAnimation",
- "AnimationRegistry",
- ]
|