# -*- coding: utf-8 -*- # gui/components/model_view/_mv_presentation.py """Фасад видимости сцены для применения декларативных спецификаций представления.""" from __future__ import annotations from typing import TYPE_CHECKING from error_logger import log_exception if TYPE_CHECKING: from gui.components.model_view_widget import ModelViewWidget class ScenePresentationMixin: """Применение спецификаций видимости сцены через публичный API ModelView.""" @staticmethod def _safe_call(fn, *args, **kwargs): try: return fn(*args, **kwargs) except Exception as exc: log_exception(__name__, "ScenePresentationMixin._safe_call", exc) return None @staticmethod def _rack_filter_value(spec) -> str: rack_filter = getattr(spec, "rack_filter", "") return str(getattr(rack_filter, "value", rack_filter) or "") def apply_scene_spec( self: "ModelViewWidget", spec, *, start_select_rack: bool = False, select_rack_id: str | None = None, ) -> None: """Применить ``SceneVisibilitySpec``, созданный политикой представления.""" if self is None or spec is None: return zone_contour_id = getattr(spec, "zone_contour_id", None) if zone_contour_id: self._safe_call(self.show_zone_contour, zone_contour_id) elif bool(getattr(spec, "zone_solids_visible", False)): self._safe_call(self.show_all_zones) if bool(getattr(spec, "use_facility_contour_mode", False)) and hasattr( self, "set_zone_facility_contour_mode" ): zone_ids = [str(zid) for zid in getattr(self, "_zones", {}).keys()] for zid in zone_ids: has_racks = self.has_racks_in_zone(zid) if hasattr(self, "has_racks_in_zone") else False self._safe_call(self.set_zone_facility_contour_mode, zid, has_racks) if bool(getattr(spec, "hide_empty_zones", False)) and hasattr(self, "has_racks_in_zone"): zone_ids = [str(zid) for zid in getattr(self, "_zones", {}).keys()] for zid in zone_ids: if not self.has_racks_in_zone(zid): self._safe_call(self.set_zone_visibility, zid, False) else: self._safe_call(self.set_zone_facility_contour_mode, zid, False) self._safe_call(self.set_zone_visibility, zid, False) self._safe_call(self.set_zone_pick_enabled, bool(getattr(spec, "zone_pick_enabled", True))) rack_filter = self._rack_filter_value(spec) rack_zone_id = getattr(spec, "rack_zone_id", None) rack_id = getattr(spec, "rack_id", None) if rack_filter == "single_rack": isolated = False if hasattr(self, "show_only_rack") and rack_id: isolated = bool(self._safe_call(self.show_only_rack, rack_id, rack_zone_id)) if not isolated and rack_zone_id: self._safe_call(self.show_only_zone_racks, rack_zone_id) elif rack_filter == "zone_only": self._safe_call(self.show_only_zone_racks, rack_zone_id) else: self._safe_call(self.show_all_racks) rack_select_mode = bool(getattr(spec, "rack_select_mode", False)) if rack_select_mode and start_select_rack and rack_zone_id: self._safe_call(self.start_select_rack_mode, rack_zone_id) elif not rack_select_mode: self._safe_call(self.stop_select_rack_mode) if select_rack_id and hasattr(self, "select_rack_by_id"): self._safe_call(self.select_rack_by_id, select_rack_id, rack_zone_id) if bool(getattr(spec, "clear_hover", False)): mgr = getattr(self, "_interaction_manager", None) if mgr is not None: self._safe_call(mgr.reset) self._safe_call(self.set_top_view_navigation, bool(getattr(spec, "top_view_navigation", False))) if hasattr(self, "update_scene"): self._safe_call(self.update_scene) # --------------------------------------------------------------------------- # Module workflow notes # --------------------------------------------------------------------------- # 1) Задача модуля: # Применение наборов параметров отображения сцены и безопасный вызов методов представления. # # 2) Последовательность действий и вызовов: # A. Класс ScenePresentationMixin: точки входа # Публичные методы сценария: # - ScenePresentationMixin.apply_scene_spec(...) # # B. ScenePresentationMixin: основной сценарий: # ScenePresentationMixin.apply_scene_spec(...) # Назначение: Применить ``SceneVisibilitySpec``, созданный политикой представления. # Последовательность внутренних вызовов: # -> ScenePresentationMixin._safe_call(...) # -> ScenePresentationMixin._rack_filter_value(...) # # C. ScenePresentationMixin: вспомогательные расчёты: # ScenePresentationMixin._safe_call(...) # Назначение: выполняет шаг "safe call" в рамках текущего сценария модуля. # ScenePresentationMixin._rack_filter_value(...) # Назначение: выполняет шаг "rack filter value" в рамках текущего сценария модуля. # # 3) Важные ограничения и инварианты: # - Модуль выполняется в составе ModelViewWidget и использует согласованные поля состояния self._... . # - Межмодульная связность: только через фасад model_view; прямые обращения между zone, rack, shelf, cell запрещены. # - Очистка состояния должна быть идемпотентной: повторный вызов не меняет корректное состояние в ошибочное. # # 4) Правило сопровождения: # - Любое изменение сценария должно сопровождаться обновлением этого блока с сохранением фактического порядка вызовов. # - При добавлении метода указывать его место в цепочке сценария (запуск, основной шаг, завершение, вспомогательная логика).