## /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()