Files
Dispatch/Dispatch_V0.1.1/gui/containers/stack_container.py
2026-04-29 08:18:54 +04:00

188 lines
8.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# gui/containers/stack_container.py
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QStackedWidget, QGridLayout, QLayout, QWidget
from .percent_sized_widget import PercentSizedWidget
from .content_host import ContentHost
class StackContainer(PercentSizedWidget):
"""Контейнер-обёртка над QStackedWidget с размерами в процентах от родителя."""
def __init__(
self,
width_percent: int | float | None = None,
height_percent: int | float | None = None,
margin: int | tuple[int, int, int, int] = 0,
content_margins: int | tuple[int, int, int, int] | None = None,
spacing: int = 0,
content_width_percent: int | None = None,
content_height_percent: int | None = None,
content_width: int | None = None,
content_height: int | None = None,
content_fit: bool = True,
parent: QWidget | None = None,
style: str | None = None,
active_style: str | None = None,
is_active: bool | None = None,
):
super().__init__(width_percent, height_percent, parent)
self._init_stylable()
self._auto_add_children = True
self._stack = QStackedWidget(self)
self._content_host = ContentHost(
width_percent=content_width_percent,
height_percent=content_height_percent,
orientation="v",
margin=0,
spacing=spacing,
parent=self,
)
self._content_host.set_content_fit(content_fit)
self._content_host.add_widget(self._stack)
self._outer_layout = QGridLayout(self)
self._outer_layout.setSpacing(0)
applied_margins = content_margins if content_margins is not None else margin
if isinstance(applied_margins, (list, tuple)) and len(applied_margins) == 4:
self._outer_layout.setContentsMargins(*applied_margins)
else:
self._outer_layout.setContentsMargins(applied_margins, applied_margins, applied_margins, applied_margins)
if content_width is not None or content_height is not None:
w = content_width if content_width is not None else self._content_host.sizeHint().width()
h = content_height if content_height is not None else self._content_host.sizeHint().height()
self._content_host.set_fixed_size(w, h)
self._outer_layout.addWidget(self._content_host, 0, 0)
self._outer_layout.setRowStretch(0, 1)
self._outer_layout.setColumnStretch(0, 1)
if spacing:
self._outer_layout.setSpacing(spacing)
if style is not None or active_style is not None or is_active is not None:
self._apply_style(style_key=style, active_key=active_style, is_active=is_active)
# Методы работы со страницами (интерфейс похож на QStackedWidget)
def add_widget(self, widget: QWidget) -> int:
index = self._stack.addWidget(widget)
if isinstance(widget, PercentSizedWidget):
widget.schedule_percent_update()
return index
def set_current_index(self, index: int) -> None:
self._stack.setCurrentIndex(index)
def set_current_widget(self, widget: QWidget) -> None:
self._stack.setCurrentWidget(widget)
def current_index(self) -> int:
return self._stack.currentIndex()
def current_widget(self) -> QWidget | None:
return self._stack.currentWidget()
def count(self) -> int:
return self._stack.count()
def widget(self, index: int) -> QWidget | None:
return self._stack.widget(index)
def remove_widget(self, widget: QWidget) -> None:
self._stack.removeWidget(widget)
def get_available_size_for_content(self) -> tuple[int, int]:
"""Полезная внутренняя область (без учёта margin)."""
margins = self._outer_layout.contentsMargins()
w = self.width() - margins.left() - margins.right()
h = self.height() - margins.top() - margins.bottom()
return max(0, w), max(0, h)
def get_layout(self) -> QLayout:
return self._outer_layout
def set_margins(self, margin: int | tuple[int, int, int, int]) -> None:
if isinstance(margin, (list, tuple)) and len(margin) == 4:
self._outer_layout.setContentsMargins(*margin)
else:
self._outer_layout.setContentsMargins(margin, margin, margin, margin)
def set_spacing(self, spacing: int) -> None:
self._outer_layout.setSpacing(spacing)
def set_alignment(self, alignment: str) -> None:
raise NotImplementedError("Qt alignment for containers is disabled; use content springs.")
@property
def stacked_widget(self) -> QStackedWidget:
return self._stack
# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
#
# 1) Назначение модуля:
# Контейнер-обёртка над QStackedWidget с процентным sizing.
# Реализует переключение «страниц» (одна видна
# в каждый момент). Используется для панели плагинов, табов и т.п.
#
# 2) Зависимости модуля:
# Импорты: Qt, QStackedWidget, QGridLayout, QLayout, QWidget (PySide6)
# Хост/базовый класс: StylableMixin + PercentSizedWidget (MRO)
# Внутренние: ContentHost (content_host.py), StylableMixin (stylable_mixin.py)
# Внешние библиотеки: PySide6
#
# 3) Экспорт:
# Класс StackContainer — стековый контейнер.
# Методы: add_widget(), set_current_index(), set_current_widget(),
# current_index(), current_widget(), count(), widget(),
# remove_widget(), get_available_size_for_content(),
# get_layout(), set_margins(), set_spacing(), set_alignment()
# Свойство: stacked_widget (доступ к QStackedWidget).
#
# 4) Состояние (поля):
# _stack : QStackedWidget — Qt stacked widget.
# _content_host : ContentHost — промежуточный хост.
# _outer_layout : QGridLayout — внешний layout (spring-based).
# _auto_add_children : bool = True — авто-добавление потомков.
#
# 5) Последовательность действий и вызовов:
# __init__(params) -> super().__init__(w%, h%, parent)
# -> _init_stylable() -> создание QStackedWidget
# -> создание ContentHost -> _content_host.add_widget(_stack)
# -> создание _outer_layout (QGridLayout)
# -> setContentsMargins(applied_margins)
# -> _apply_style() если style задан
# add_widget(w) -> _stack.addWidget(w) → возвращает int index
# set_current_index(i) -> _stack.setCurrentIndex(i)
#
# 6) Побочные эффекты:
# QStackedWidget скрывает все страницы, кроме текущей.
# _auto_add_children = True — дочерние виджеты авто-добавляются.
# set_alignment() бросает NotImplementedError.
# _apply_style() применяет stylesheet.
#
# 7) Границы ответственности:
# НЕ управляет навигацией между страницами — только хранит и показывает.
# НЕ создаёт кнопок/табов для переключения — это делает вызывающий код.
# НЕ поддерживает Qt alignment (только springs).
#
# 8) Обработка ошибок:
# set_alignment() → NotImplementedError.
# Защита от нетипичных значений alignment — параметр удалён.
#
# 9) Инварианты и контракты:
# - add_widget возвращает index добавленной страницы.
# - content_margins приоритетнее margin.
#
# 10) Правило сопровождения:
# Интерфейс CRUD-страниц (add/remove/set_current) повторяет
# QStackedWidget — при обновлении Qt API проверять совместимость.
# Не добавлять alignment в конструктор — запрещено правилами.