Add Dispatch_V0.1.1
This commit is contained in:
265
Dispatch_V0.1.1/gui/components/label.py
Normal file
265
Dispatch_V0.1.1/gui/components/label.py
Normal file
@@ -0,0 +1,265 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# gui/components/label.py
|
||||
"""Стандартный компонент метки."""
|
||||
|
||||
from PySide6.QtWidgets import QLabel, QSizePolicy
|
||||
from PySide6.QtCore import Qt, Slot
|
||||
|
||||
from gui.containers.s_container import SContainer
|
||||
from gui.styles import APP_STYLES
|
||||
from gui.theme_bus import theme_bus
|
||||
|
||||
|
||||
class Label(SContainer):
|
||||
"""Стандартная метка с выбором стиля по теме внутри SContainer."""
|
||||
|
||||
_ALIGN_MAP = {
|
||||
"top": Qt.AlignmentFlag.AlignTop,
|
||||
"bottom": Qt.AlignmentFlag.AlignBottom,
|
||||
"left": Qt.AlignmentFlag.AlignLeft,
|
||||
"right": Qt.AlignmentFlag.AlignRight,
|
||||
"hcenter": Qt.AlignmentFlag.AlignHCenter,
|
||||
"vcenter": Qt.AlignmentFlag.AlignVCenter,
|
||||
"center": Qt.AlignmentFlag.AlignCenter,
|
||||
}
|
||||
|
||||
def __init__(self, text: str = "", **kwargs):
|
||||
width_percent = kwargs.get("width_percent", None)
|
||||
height_percent = kwargs.get("height_percent", None)
|
||||
margin = kwargs.get("margin", 0)
|
||||
alignment = kwargs.get("alignment", Qt.AlignCenter)
|
||||
style = kwargs.get("style", "WIDGET_LABEL")
|
||||
active_style = kwargs.get("active_style", None)
|
||||
is_active = kwargs.get("is_active", None)
|
||||
content_fit = kwargs.get("content_fit", True)
|
||||
word_wrap = kwargs.get("word_wrap", False)
|
||||
parent = kwargs.get("parent", None)
|
||||
|
||||
super().__init__(
|
||||
width_percent=width_percent,
|
||||
height_percent=height_percent,
|
||||
margin=margin,
|
||||
style=style,
|
||||
active_style=active_style,
|
||||
is_active=is_active,
|
||||
content_fit=content_fit,
|
||||
parent=parent,
|
||||
)
|
||||
|
||||
self._theme = "dark"
|
||||
self._is_active: bool = False
|
||||
self._base_style_key = style
|
||||
self._style_key_normal = None
|
||||
self._style_key_active = None
|
||||
|
||||
self._label = QLabel(text)
|
||||
self._apply_alignment(alignment)
|
||||
if word_wrap:
|
||||
self._label.setWordWrap(True)
|
||||
self._label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
else:
|
||||
self._label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
super().add_widget(self._label)
|
||||
|
||||
if active_style is not None:
|
||||
self._style_key_normal = style
|
||||
self._style_key_active = active_style
|
||||
if is_active is not None:
|
||||
self._is_active = bool(is_active)
|
||||
|
||||
self._theme = "dark" if self.palette().window().color().lightness() < 128 else "light"
|
||||
self.style()
|
||||
theme_bus.theme_changed.connect(self.set_theme)
|
||||
|
||||
def style(
|
||||
self,
|
||||
style_key: str | None = None,
|
||||
active_key: str | None = None,
|
||||
is_active: bool | None = None,
|
||||
) -> None:
|
||||
"""Короткий метод применения стиля. Можно задать ключи и активность явно."""
|
||||
if style_key is not None:
|
||||
self._base_style_key = style_key
|
||||
if active_key is not None:
|
||||
self._style_key_normal = style_key
|
||||
self._style_key_active = active_key
|
||||
else:
|
||||
self._style_key_normal = None
|
||||
self._style_key_active = None
|
||||
|
||||
if is_active is not None:
|
||||
self._is_active = bool(is_active)
|
||||
|
||||
if self._style_key_normal is not None:
|
||||
active_key = self._style_key_active or self._style_key_normal
|
||||
key = active_key if self._is_active else self._style_key_normal
|
||||
themed = f"{key}_{self._theme.upper()}"
|
||||
if themed in APP_STYLES:
|
||||
key = themed
|
||||
self._label.setStyleSheet(APP_STYLES.get(key, ""))
|
||||
return
|
||||
|
||||
base_key = self._base_style_key
|
||||
key = base_key
|
||||
|
||||
if self._theme == "light":
|
||||
if self._is_active and f"{base_key}_LIGHT_ACTIVE" in APP_STYLES:
|
||||
key = f"{base_key}_LIGHT_ACTIVE"
|
||||
elif f"{base_key}_LIGHT" in APP_STYLES:
|
||||
key = f"{base_key}_LIGHT"
|
||||
else:
|
||||
if self._is_active and f"{base_key}_DARK_ACTIVE" in APP_STYLES:
|
||||
key = f"{base_key}_DARK_ACTIVE"
|
||||
elif f"{base_key}_DARK" in APP_STYLES:
|
||||
key = f"{base_key}_DARK"
|
||||
|
||||
self._label.setStyleSheet(APP_STYLES.get(key, ""))
|
||||
|
||||
@Slot(str)
|
||||
def set_theme(self, theme: str) -> None:
|
||||
"""Внешний слот: принимает 'dark' или 'light'."""
|
||||
theme = (theme or "").strip().lower()
|
||||
if theme not in ("dark", "light"):
|
||||
return
|
||||
if self._theme == theme:
|
||||
return
|
||||
self._theme = theme
|
||||
self.style()
|
||||
|
||||
def set_text(self, text: str) -> None:
|
||||
self._label.setText(text)
|
||||
|
||||
def set_pixmap(self, pixmap) -> None:
|
||||
self._label.setPixmap(pixmap)
|
||||
|
||||
def get_text(self) -> str:
|
||||
return self._label.text()
|
||||
|
||||
def _apply_alignment(self, alignment) -> None:
|
||||
"""Внутренний метод применения alignment с поддержкой строк."""
|
||||
if isinstance(alignment, str):
|
||||
key = alignment.strip().lower()
|
||||
alignment = self._ALIGN_MAP.get(key)
|
||||
if alignment is None:
|
||||
raise ValueError(f"Unknown alignment '{key}'. Allowed: {list(self._ALIGN_MAP.keys())}")
|
||||
self._label.setAlignment(alignment)
|
||||
|
||||
def set_alignment(self, alignment: str | Qt.Alignment) -> None:
|
||||
"""Установить выравнивание текста (строка или Qt.Alignment)."""
|
||||
self._apply_alignment(alignment)
|
||||
|
||||
def set_word_wrap(self, enabled: bool) -> None:
|
||||
"""Включить / выключить перенос текста по словам."""
|
||||
self._label.setWordWrap(enabled)
|
||||
if enabled:
|
||||
self._label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
|
||||
def set_enabled(self, enabled: bool) -> None:
|
||||
self._label.setEnabled(enabled)
|
||||
super().setEnabled(enabled)
|
||||
|
||||
def set_min_width(self, width: int) -> None:
|
||||
self._label.setMinimumWidth(width)
|
||||
super().setMinimumWidth(width)
|
||||
|
||||
def set_min_height(self, height: int) -> None:
|
||||
self._label.setMinimumHeight(height)
|
||||
super().setMinimumHeight(height)
|
||||
|
||||
def set_max_width(self, width: int) -> None:
|
||||
self._label.setMaximumWidth(width)
|
||||
super().setMaximumWidth(width)
|
||||
|
||||
def set_max_height(self, height: int) -> None:
|
||||
self._label.setMaximumHeight(height)
|
||||
super().setMaximumHeight(height)
|
||||
|
||||
def set_fixed_size(self, width: int, height: int) -> None:
|
||||
self._label.setMinimumSize(width, height)
|
||||
self._label.setMaximumSize(width, height)
|
||||
super().setMinimumSize(width, height)
|
||||
super().setMaximumSize(width, height)
|
||||
|
||||
def set_tooltip(self, text: str) -> None:
|
||||
self._label.setToolTip(text)
|
||||
|
||||
def set_size_policy(self, horizontal, vertical) -> None:
|
||||
self._label.setSizePolicy(horizontal, vertical)
|
||||
super().setSizePolicy(horizontal, vertical)
|
||||
|
||||
def add_widget(self, widget, alignment=None):
|
||||
raise NotImplementedError("Label can contain only one QLabel")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Module workflow notes
|
||||
# ---------------------------------------------------------------------------
|
||||
#
|
||||
# 1) Назначение модуля:
|
||||
# Стандартная текстовая метка в SContainer с поддержкой
|
||||
# централизованных стилей APP_STYLES, темизации и строкового
|
||||
# выравнивания.
|
||||
#
|
||||
# 2) Зависимости модуля:
|
||||
# Импорты: QLabel, QSizePolicy (PySide6.QtWidgets),
|
||||
# Qt, Slot (PySide6.QtCore),
|
||||
# SContainer (gui.containers.s_container),
|
||||
# APP_STYLES (gui.styles),
|
||||
# theme_bus (gui.theme_bus)
|
||||
# Хост-класс / базовый класс: SContainer
|
||||
# Внешние библиотеки: PySide6 (обязательна)
|
||||
#
|
||||
# 3) Экспорт:
|
||||
# Класс Label — публичный виджет метки.
|
||||
# Методы: style(), set_theme(), set_text(), get_text(),
|
||||
# set_alignment(), set_enabled(),
|
||||
# set_min/max_width/height(), set_fixed_size(),
|
||||
# set_tooltip(), set_size_policy().
|
||||
#
|
||||
# 4) Состояние (поля):
|
||||
# _theme: str — текущая тема
|
||||
# _is_active: bool — признак активного состояния
|
||||
# _base_style_key: str — базовый ключ стиля ("WIDGET_LABEL")
|
||||
# _style_key_normal: str|None — явный нормальный стиль
|
||||
# _style_key_active: str|None — явный активный стиль
|
||||
# _label: QLabel — внутренний виджет метки
|
||||
# _ALIGN_MAP: dict — маппинг строк → Qt.Alignment
|
||||
#
|
||||
# 5) Последовательность действий и вызовов:
|
||||
# __init__(text="", **kwargs)
|
||||
# -> извлечение параметров из kwargs
|
||||
# -> super().__init__(...)
|
||||
# -> QLabel(text) -> _apply_alignment -> setSizePolicy
|
||||
# -> super().add_widget(_label)
|
||||
# -> style() -> theme_bus.theme_changed.connect(set_theme)
|
||||
# _apply_alignment(alignment)
|
||||
# -> если str → маппинг через _ALIGN_MAP → _label.setAlignment()
|
||||
# -> если Qt.Alignment → прямое применение
|
||||
# -> ValueError при невалидной строке
|
||||
#
|
||||
# 6) Побочные эффекты:
|
||||
# - Устанавливает stylesheet на QLabel.
|
||||
# - Подключается к theme_bus.theme_changed.
|
||||
#
|
||||
# 7) Границы ответственности:
|
||||
# Модуль НЕ поддерживает rich-text/HTML самостоятельно (хотя QLabel
|
||||
# может). НЕ обрабатывает клики. add_widget() заблокирован.
|
||||
#
|
||||
# 8) Обработка ошибок:
|
||||
# add_widget() бросает NotImplementedError.
|
||||
# _apply_alignment() бросает ValueError при невалидной строке.
|
||||
# set_theme() молча игнорирует невалидные значения.
|
||||
#
|
||||
# 9) Инварианты и контракты:
|
||||
# - Контейнер содержит ровно один QLabel.
|
||||
# - _theme ∈ {"dark", "light"}.
|
||||
# - Стиль: явный ключ → base_key + суффикс.
|
||||
#
|
||||
# 10) Правило сопровождения:
|
||||
# Для новых стилей — добавлять в APP_STYLES с суффиксами
|
||||
# _DARK/_LIGHT + _ACTIVE. __init__ принимает **kwargs —
|
||||
# при добавлении новых параметров обновлять извлечение.
|
||||
Reference in New Issue
Block a user