Исходный код EJIO.ctk_extender.widget.container

## /ctk_extender/widget/container.py

import customtkinter as ctk
from typing import Any, Self
from loguru import logger

from EJIO.utils import List
from .base import Widget
from .geometry import GeometryStrategy
from .err import WidgetContainerExtensionError

__all__ = [
    'WidgetContainer',
]


[документация] class WidgetContainer(Widget): """ Компонент-контейнер, объединяющий несколько виджетов внутри единого фрейма (CTkFrame). Автоматически каскадно управляет жизненным циклом и уничтожением всех дочерних элементов. """ def __init__( self, parent: ctk.CTk | ctk.CTkFrame | ctk.CTkTabview | Any, geometry: GeometryStrategy | None = None, ) -> None: # Потокобезопасный список для хранения дочерних компонентов Widget self._children: List[Widget] = List() super().__init__(parent, geometry)
[документация] def build(self) -> ctk.CTkFrame: """ Строит базовый контейнерный фрейм. Может быть переопределен в дочерних классах (например, для настройки бордеров или цвета). """ return ctk.CTkFrame(self.parent)
@property def children(self) -> List[Widget]: """Возвращает список всех дочерних виджетов в контейнере.""" return self._children
[документация] def add_widget[W: Widget](self, widget_cls: type[W], *args: Any, **kwargs: Any) -> W: """ Фабричный метод для динамического создания и добавления виджета внутрь контейнера. Автоматически передает ctk_instance текущего контейнера в качестве родителя. :param widget_cls: Класс создаваемого виджета (наследник Widget). :param args: Позиционные аргументы для конструктора виджета. :param kwargs: Именованные аргументы для конструктора виджета. :return: Созданный экземпляр виджета. """ try: # В качестве родителя для нового виджета передаем реальный CTkFrame этого контейнера new_widget = widget_cls(self.ctk_instance, *args, **kwargs) self._children.append(new_widget) return new_widget except Exception as e: logger.error(f"Failed to add widget {widget_cls.__name__} in container {self.__class__.__name__}: {e}") raise WidgetContainerExtensionError( f"Failed to add widget {widget_cls.__name__} in container {self.__class__.__name__}") from e
[документация] def remove_widget[W: Widget](self, widget: W) -> Self: """ Удаляет виджет из контейнера и уничтожает его ресурсы. :param widget: Экземпляр уничтожаемого виджета. """ if widget in self._children: self._children.remove(widget) widget.destroy() return self
[документация] def destroy(self) -> None: """ Каскадно уничтожает сам контейнер и все вложенные в него дочерние виджеты, полностью очищая оперативную память. """ logger.debug(f"Beginning cascade container destruction of {self.__class__.__name__}...") # 1. Сначала уничтожаем всех детей, чтобы они очистили свои подписки в EventController if hasattr(self, '_children') and self._children is not None: for child in list(self._children): try: child.destroy() except Exception as e: logger.error(f"Error during destruction of child widget {child.__class__.__name__}: {e}") self._children.clear() self._children.clear() # 2. Теперь вызываем базовое уничтожение самого фрейма и отписку его listeners super().destroy()
def __del__(self) -> None: """ Деструктор контейнера. Гарантирует, что при удалении контейнера не произойдет утечки памяти от забытых дочерних виджетов. """ self.destroy()