105 lines
5.2 KiB
Python
105 lines
5.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
# dispatch/window.py
|
||
|
||
"""Главное окно независимого приложения Dispatch.
|
||
|
||
Назначение модуля:
|
||
Минимальный графический каркас, выполняющий единственную обязанность —
|
||
разместить корневой виджет модуля `hub.ticket.TicketPlugin` в окне
|
||
верхнего уровня. Управление содержимым модуля Ticket остаётся
|
||
полностью внутри самого модуля.
|
||
|
||
Архитектурные ограничения:
|
||
- Окно не содержит предметной логики и не обращается к внутренним
|
||
полям модуля Ticket: взаимодействие выполняется только через его
|
||
публичный API (`TicketPlugin`).
|
||
- Стилевое оформление берётся из внешнего реестра `APP_STYLES`;
|
||
локальные QSS-литералы запрещены (правило 6.3).
|
||
- Отказ модуля Ticket изолирован: при ошибке инициализации окно
|
||
продолжает существовать, ошибка фиксируется в журнале.
|
||
"""
|
||
|
||
from PySide6.QtCore import Qt, QEvent, QTimer
|
||
from PySide6.QtWidgets import QMainWindow
|
||
|
||
from error_logger import log_exception
|
||
from gui.containers.percent_sized_widget import PercentSizedWidget
|
||
from gui.styles import APP_STYLES
|
||
from gui.theme_bus import theme_bus as _default_theme_bus
|
||
from application import TaskApplicationService
|
||
from ticket_plugin import TicketPlugin
|
||
|
||
from null_hardware_gateway import NullHardwareGateway
|
||
|
||
|
||
class DispatchMainWindow(QMainWindow):
|
||
"""Главное окно приложения Dispatch с единственным модулем Ticket."""
|
||
|
||
def __init__(self, theme_bus=None):
|
||
super().__init__()
|
||
self._theme_bus = theme_bus or _default_theme_bus
|
||
self._ticket_plugin: TicketPlugin | None = None
|
||
self._setup_window()
|
||
self._setup_ui()
|
||
self._connect_signals()
|
||
|
||
def _setup_window(self) -> None:
|
||
"""Настроить параметры окна верхнего уровня."""
|
||
self.setWindowTitle("Dispatch — Ticket")
|
||
self.setMinimumSize(1200, 800)
|
||
self.setStyleSheet(APP_STYLES.get("MAIN_WINDOW_DARK", ""))
|
||
self.showMaximized()
|
||
|
||
def _setup_ui(self) -> None:
|
||
"""Разместить корневой виджет модуля Ticket в качестве центрального.
|
||
|
||
В составе Dispatch модуль работы с COM-портом отключён: приложение
|
||
собирает application-сервис Ticket с null-шлюзом и передаёт его
|
||
в `TicketPlugin`, который владеет жизненным циклом сервиса.
|
||
"""
|
||
try:
|
||
null_gateway = NullHardwareGateway(parent=self)
|
||
application_service = TaskApplicationService(
|
||
hardware_gateway=null_gateway,
|
||
parent=self,
|
||
)
|
||
self._ticket_plugin = TicketPlugin(application_service=application_service)
|
||
self.setCentralWidget(self._ticket_plugin)
|
||
except Exception as exc:
|
||
log_exception(__name__, "DispatchMainWindow._setup_ui", exc)
|
||
raise
|
||
|
||
def _connect_signals(self) -> None:
|
||
"""Подключить шину тем для согласованного оформления окна."""
|
||
self._theme_bus.theme_changed.connect(self._on_theme_changed)
|
||
|
||
def _on_theme_changed(self, theme: str) -> None:
|
||
"""Применить тему окна по ключам из внешнего реестра стилей."""
|
||
is_light = str(theme or "").strip().lower() == "light"
|
||
key = "MAIN_WINDOW_LIGHT" if is_light else "MAIN_WINDOW_DARK"
|
||
self.setStyleSheet(APP_STYLES.get(key, ""))
|
||
|
||
def changeEvent(self, event):
|
||
"""Пересчитать процентную разметку при восстановлении окна."""
|
||
if event.type() == QEvent.Type.WindowStateChange:
|
||
if not (self.windowState() & Qt.WindowState.WindowMinimized):
|
||
QTimer.singleShot(0, self._refresh_percent_layout)
|
||
return super().changeEvent(event)
|
||
|
||
def _refresh_percent_layout(self) -> None:
|
||
"""Принудительно пересчитать процентные размеры дочерних виджетов."""
|
||
for widget in self.findChildren(PercentSizedWidget):
|
||
try:
|
||
widget.schedule_percent_update()
|
||
except Exception as exc:
|
||
log_exception(__name__, "DispatchMainWindow._refresh_percent_layout", exc)
|
||
|
||
def closeEvent(self, event) -> None:
|
||
"""Гарантировать корректную остановку модуля Ticket при закрытии."""
|
||
if self._ticket_plugin is not None:
|
||
try:
|
||
self._ticket_plugin.cleanup()
|
||
except Exception as exc:
|
||
log_exception(__name__, "DispatchMainWindow.closeEvent", exc)
|
||
super().closeEvent(event)
|