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

146 lines
7.3 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/content_host.py
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QSizePolicy
from .percent_sized_widget import PercentSizedWidget
class ContentHost(PercentSizedWidget):
"""Внутренний хост контента с процентными размерами и базовым layout."""
def __init__(
self,
width_percent: int | None = None,
height_percent: int | None = None,
orientation: str = "v",
margin: int | tuple[int, int, int, int] = 0,
spacing: int = 0,
parent: QWidget | None = None,
):
super().__init__(width_percent, height_percent, parent)
if orientation == "h":
self._layout = QHBoxLayout(self)
else:
self._layout = QVBoxLayout(self)
self._layout.setSpacing(spacing)
if isinstance(margin, (list, tuple)) and len(margin) == 4:
self._layout.setContentsMargins(*margin)
else:
self._layout.setContentsMargins(margin, margin, margin, margin)
# Контент-хост по умолчанию растягивается внутри контейнера
self.set_content_fit(True)
def set_content_fit(self, expand: bool) -> None:
"""Управление растягиванием контента внутри контейнера."""
if expand:
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
else:
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
def _auto_add_to_parent(self) -> None:
"""Контент-хост добавляется вручную контейнером."""
return
def add_widget(self, widget: QWidget, alignment=None) -> None:
"""Добавляет виджет в layout контейнера."""
self._layout.addWidget(widget)
self._notify_children_layout_changed()
def insert_widget(self, index: int, widget: QWidget) -> None:
"""Вставляет виджет в layout контейнера по индексу."""
self._layout.insertWidget(index, widget)
self._notify_children_layout_changed()
def remove_widget(self, widget: QWidget) -> None:
"""Удаляет виджет из layout контейнера."""
self._layout.removeWidget(widget)
self._notify_children_layout_changed()
def add_widget_with_stretch(self, widget: QWidget, stretch: int, alignment=None) -> None:
"""Добавляет виджет с stretch в layout контейнера."""
self._layout.addWidget(widget, stretch)
self._notify_children_layout_changed()
def add_stretch(self, stretch: int = 1) -> None:
self._layout.addStretch(stretch)
def get_layout(self):
return self._layout
def _notify_children_layout_changed(self) -> None:
"""Канал «состав детей»: после изменения списка детей host'а просим
каждого percent-sized потомка перепланировать свой пересчёт.
Коалесцирование обеспечивается флагом _update_pending в каждом потомке —
несколько add_widget(...) подряд дают только один singleShot(0) на потомка.
"""
for index in range(self._layout.count()):
item = self._layout.itemAt(index)
if item is None:
continue
child_widget = item.widget()
if isinstance(child_widget, PercentSizedWidget):
child_widget.schedule_percent_update()
# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
#
# 1) Назначение модуля:
# Внутренний хост контента — промежуточный виджет, который размещается
# внутри контейнеров (SContainer, GridContainer, StackContainer и пр.)
# и содержит реальные дочерние виджеты. Поддерживает процентные размеры,
# вертикальную/горизонтальную ориентацию layout'а, отступы и spacing.
# Является деталью реализации контейнеров, а не частью публичного API.
#
# 2) Зависимости модуля:
# Импорты: Qt, QVBoxLayout, QHBoxLayout, QWidget, QSizePolicy (PySide6)
# Хост/базовый класс: PercentSizedWidget (percent_sized_widget.py)
# Внешние библиотеки: PySide6
#
# 3) Экспорт:
# Класс ContentHost — внутренний хост контента с layout.
# Методы: set_content_fit(), add_widget(), add_widget_with_stretch(),
# add_stretch(), get_layout()
#
# 4) Состояние (поля):
# _layout : QVBoxLayout | QHBoxLayout — layout для размещения потомков,
# выбирается по параметру orientation ("v"/"h").
#
# 5) Последовательность действий и вызовов:
# __init__(params) -> super().__init__(width_percent, height_percent)
# -> создание QVBoxLayout/QHBoxLayout
# -> setSpacing() -> setContentsMargins()
# -> set_content_fit(True) — SizePolicy = Expanding по умолчанию
# add_widget(widget) -> _layout.addWidget(widget)
# add_widget_with_stretch(widget, stretch) -> _layout.addWidget(widget, stretch)
#
# 6) Побочные эффекты:
# Устанавливает SizePolicy на self при вызове set_content_fit().
# _auto_add_to_parent() переопределён как no-op — ContentHost добавляется
# контейнером вручную, а не через механизм авто-добавления PercentSizedWidget.
#
# 7) Границы ответственности:
# НЕ управляет собственными процентами — это делает PercentSizedWidget.
# НЕ стилизуется (не наследует StylableMixin).
# НЕ дублирует логику контейнера; лишь предоставляет layout.
#
# 8) Обработка ошибок:
# Нет явной обработки; некорректные margin/spacing молча приведут
# к ошибке Qt. Если margin — не tuple(4), воспринимается как int.
#
# 9) Инварианты и контракты:
# - orientation ∈ {"v", "h"}, иначе умолчание — вертикальный.
# - margin: int или tuple(4). Иной тип вызовет ошибку setContentsMargins.
# - _auto_add_to_parent всегда возвращает None.
#
# 10) Правило сопровождения:
# Не расширять публичный интерфейс ContentHost — он должен оставаться
# тонкой обёрткой. Новые фичи (стилизация, alignment) добавлять
# в контейнеры, использующие ContentHost, а не сюда.