Исходный код EJIO.ctk_extender.components.copyable_field

## /ctk_extender/components/copyable_field.py

import customtkinter as ctk
from typing import Any, Literal
from ..widget import WidgetContainer, Label, GridStrategy
from EJIO.utils import _

__all__ = [
    'CopyableField',
]

FieldLayout = Literal['row', 'column']#TODO: переделать попап на уведомление


[документация] class CopyableField(WidgetContainer): """ Информационное поле вывода данных с кнопкой копирования в буфер обмена. Поддерживает каноничное позиционирование подписи сверху (column) или в ряд (row). """ __slots__ = ('_label_text', '_value_text', '_layout', '_entry_widget') def __init__( self, parent: Any, label_text: str, value_text: str, layout: FieldLayout = 'row', geometry: Any = None, **kwargs: Any ) -> None: self._label_text: str = label_text self._value_text: str = value_text self._layout: FieldLayout = layout self._entry_widget: ctk.CTkEntry | None = None super().__init__(parent, geometry, **kwargs)
[документация] def build(self) -> ctk.CTkFrame: """Строит контейнер поля копирования с динамической сеткой.""" # 1. Базовый несущий фрейм-контейнер frame_instance = super().build() frame_instance.configure(fg_color="transparent") # 2. Добавляем компонент подписи (Label) через фабрику контейнера lbl = self.add_widget(Label, text=self._label_text) # 3. Создаем внутренний горизонтальный ряд для инпута и кнопки # Это гарантирует, что кнопка 📋 всегда прижата к инпуту справа field_row = ctk.CTkFrame(frame_instance, fg_color="transparent") self._entry_widget = ctk.CTkEntry(field_row, font=("Consolas", 12)) self._entry_widget.insert(0, self._value_text) self._entry_widget.configure(state="disabled", text_color=ctk.ThemeManager.theme["CTkEntry"]["text_color"]) self._entry_widget.pack(side="left", fill="x", expand=True, padx=(0, 5)) self._entry_widget.bind("<Button-1>", lambda e: self.copy_to_clipboard()) copy_btn = ctk.CTkButton( field_row, text="📋", width=36, height=28, fg_color="#4B5563", hover_color="#374151", command=self.copy_to_clipboard ) copy_btn.pack(side="right") # 4. Выстраиваем сетку grid в зависимости от выбранного режима разметки layout if self._layout == 'row': # Компоненты выстраиваются в одну линию lbl.set_geometry(GridStrategy(row=0, column=0, sticky="w", padx=(0, 10), pady=2)) field_row.grid(row=0, column=1, sticky="ew", pady=2) frame_instance.grid_columnconfigure(1, weight=1) else: # Подпись уходит наверх, поле с кнопкой — под неё lbl.set_geometry(GridStrategy(row=0, column=0, sticky="w", pady=(0, 2))) field_row.grid(row=1, column=0, sticky="ew", pady=(0, 2)) frame_instance.grid_columnconfigure(0, weight=1) return frame_instance
[документация] def copy_to_clipboard(self) -> None: """Потокобезопасно копирует текст в буфер обмена операционной системы.""" from ..windows import popup_info try: root = self.ctk_instance.winfo_toplevel() root.clipboard_clear() root.clipboard_append(self._value_text) # Выводим уведомление с таймером на 1.2 секунды popup_info(_("ctk_extender.builtins.component.copyable_field.success"), _("ctk_extender.builtins.component.copyable_field.text_copied"), timeout=1.2) except Exception: pass
[документация] def destroy(self) -> None: self._entry_widget = None super().destroy()