Files
radar_system/python_app/models/setting_fields.py
T
2026-10-06 12:32:26 +03:00

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))