64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""Resolved profile bindings, derived from the GUI dataclass field declarations."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, fields, is_dataclass
|
|
from typing import Iterator
|
|
|
|
from python_app.models.gui_profile_schema import GuiStateModel, Scalar, Setting
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class BoundSetting:
|
|
"""A declaration resolved to its persistent field and processing page."""
|
|
|
|
path: tuple[str, ...]
|
|
spec: Setting
|
|
mode: str | None
|
|
|
|
def read(self, gui: GuiStateModel) -> Scalar:
|
|
value: object = gui
|
|
for name in self.path:
|
|
value = getattr(value, name)
|
|
if not isinstance(value, (str, int, float)):
|
|
raise TypeError(f"Setting {'.'.join(self.path)} must be a scalar")
|
|
return value
|
|
|
|
def write(self, gui: GuiStateModel, value: Scalar) -> None:
|
|
owner = gui
|
|
for name in self.path[:-1]:
|
|
owner = getattr(owner, name)
|
|
setattr(owner, self.path[-1], value)
|
|
|
|
|
|
def _settings(model: object, path: tuple[str, ...] = ()) -> Iterator[BoundSetting]:
|
|
if not is_dataclass(model) or isinstance(model, type):
|
|
return
|
|
for member in fields(model):
|
|
member_path = (*path, member.name)
|
|
spec = member.metadata.get("setting")
|
|
if isinstance(spec, Setting):
|
|
mode = member_path[1] if member_path[0] == "processing" and len(member_path) == 3 else None
|
|
yield BoundSetting(member_path, spec, mode)
|
|
else:
|
|
yield from _settings(getattr(model, member.name), member_path)
|
|
|
|
|
|
GUI_FIELDS = tuple(_settings(GuiStateModel()))
|
|
|
|
|
|
def active_live_fields(mode: str) -> tuple[BoundSetting, ...]:
|
|
"""Use the selected page for shared fields; retain other fields' own values."""
|
|
selected: dict[str, BoundSetting] = {}
|
|
for binding in GUI_FIELDS:
|
|
name = binding.spec.live
|
|
if name and (name not in selected or binding.mode == mode):
|
|
selected[name] = binding
|
|
return tuple(selected.values())
|
|
|
|
|
|
def active_web_fields(mode: str) -> tuple[BoundSetting, ...]:
|
|
"""Expose only settings belonging to the current processing page."""
|
|
return tuple(binding for binding in GUI_FIELDS
|
|
if binding.spec.web and binding.mode in (None, mode))
|