# ============================= # app/simulation/ui/__init__.py # ============================= from __future__ import annotations from typing import List, Optional, Type import importlib, inspect, pkgutil, pathlib class UITab: """ Basis für alle Tabs. Erwarte: - class-attr: NAME, TITLE, PRIO - __init__(parent, sim) erzeugt self.frame (tk.Frame/ttk.Frame) - optionale Methoden: apply(), save_into_config(out), load_from_config(cfg) """ NAME: str = "tab" TITLE: str = "Tab" PRIO: int = 100 # No-ops für Save/Load def apply(self): pass def save_into_config(self, out): pass def load_from_config(self, cfg): pass def discover_ui_tabs(parent, sim, pkg_name: str = "app.simulation.ui") -> List[UITab]: """Lädt alle Unter-Module von pkg_name, instanziiert Klassen, die UITab erben.""" tabs: List[UITab] = [] pkg = importlib.import_module(pkg_name) pkg_path = pathlib.Path(pkg.__file__).parent for _, modname, ispkg in pkgutil.iter_modules([str(pkg_path)]): if ispkg: # (optional: Subpackages zulassen – hier überspringen) continue full = f"{pkg_name}.{modname}" try: m = importlib.import_module(full) except Exception as exc: print(f"[ui-loader] Importfehler {full}: {exc}") continue for _, obj in inspect.getmembers(m, inspect.isclass): if obj is UITab or not issubclass(obj, UITab): continue try: inst = obj(parent, sim) except Exception as exc: print(f"[ui-loader] Instanzierung fehlgeschlagen {obj.__name__}: {exc}") continue tabs.append(inst) tabs.sort(key=lambda t: (getattr(t, "PRIO", 100), getattr(t, "NAME", t.__class__.__name__))) return tabs