Add Dispatch_V0.1.1

This commit is contained in:
2026-04-29 08:18:54 +04:00
commit a7ede6ded4
404 changed files with 39167 additions and 0 deletions

104
Dispatch_V0.1.1/window.py Normal file
View File

@@ -0,0 +1,104 @@
# -*- 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)