Исходный код EJIO.ctk_extender.windows.popup.factory
## /ctk_extender/windows/popup/factory.py
import datetime
from collections.abc import Callable
from pathlib import Path
from typing import Any
from EJIO.utils import Color, _
from tkinter import filedialog
from .info import InfoPopup
from .warning import WarningPopup
from .confirm import ConfirmPopup
from .image import ImagePopup
from .color import ColorPickerPopup
from .date import DatePickerPopup
__all__ = [
'popup_info',
'popup_warning',
'popup_confirm',
'popup_image',
'popup_color',
'popup_date',
'popup_directory',
'popup_file',
]
def _get_active_master() -> Any:
"""
Внутренний хелпер: безопасно достает истинный корневой ctk.CTk объект приложения,
защищая окна от метаклассовых и иерархических коллизий.
"""
try:
from ...app.base import Application
app = Application()
# Извлекаем имя главного окна, которое было передано при старте Application("main_window")
main_win_name = app._main_window_name
# Получаем объект окна через контроллер и вытаскиваем его ctk_instance
from ...window.controller import WindowController
window_obj = WindowController.get_active(main_win_name)
if window_obj is not None:
# Получаем ctk_instance (который является CTkFrame в нашей композиции)
instance = window_obj.ctk_instance
if instance is not None:
return instance.winfo_toplevel()
return None
except Exception:
# Резервный откат, если приложение еще не запущено
return None
# =====================================================================
# 🚀 ГЛОБАЛЬНЫЕ ФУНКЦИИ-ШОРТКАТЫ (Свобода от вызова через класс)
# =====================================================================
[документация]
def popup_info(title: str, message: str, /, *, timeout: float | None = None) -> None:
"""Выводит быстрое информационное модальное окно уведомления."""
master = _get_active_master()
InfoPopup(title, message, master=master, timeout=timeout)
[документация]
def popup_warning(title: str, message: str, /, *, timeout: float | None = None) -> None:
"""Выводит модальное окно предупреждения или критической ошибки."""
master = _get_active_master()
WarningPopup(title, message, master=master, timeout=timeout)
[документация]
def popup_confirm(title: str, message: str, on_callback: Callable[[bool], None], /, *, timeout: float | None = None) -> None:
"""
Выводит модальный диалог подтверждения с кнопками Да/Нет.
Результат выбора (True/False) передается аргументом в on_callback.
"""
master = _get_active_master()
ConfirmPopup(title, message, on_callback, master=master, timeout=timeout)
[документация]
def popup_image(title: str, source: str | Path | bytes, /, *, timeout: float | None = None) -> None:
"""
Открывает независимое неблокирующее окно просмотра изображения.
Позволяет пользователю параллельно работать с главным интерфейсом приложения.
"""
master = _get_active_master()
ImagePopup(title, source, master=master, timeout=timeout)
[документация]
def popup_color(title: str, initial_color: str | Color = "#FFFFFF") -> Color | None:
"""Открывает палитру и синхронно возвращает объект Color или None."""
master = _get_active_master()
dialog = ColorPickerPopup(title, initial_color=initial_color, master=master)
dialog.ctk_instance.wait_window(dialog.ctk_instance)
return dialog.chosen_color if dialog.is_confirmed else None
[документация]
def popup_date(title: str, initial_date: datetime.date | None = None) -> datetime.date | None:
"""Открывает календарь и синхронно возвращает дату или None."""
master = _get_active_master()
dialog = DatePickerPopup(title, initial_date=initial_date, master=master)
dialog.ctk_instance.wait_window(dialog.ctk_instance)
return dialog.selected_date if dialog.is_confirmed else None
[документация]
def popup_directory(title: str = _("ctk_extender.builtins.popup.select_folder")) -> str | None:
"""
Синхронный диалог выбора папки.
Возвращает абсолютный путь в виде строки или None.
"""
master = _get_active_master()
# Нативный Win32/POSIX диалог
path = filedialog.askdirectory(title=title, master=master)
return path if path else None
[документация]
def popup_file(
title: str = _("ctk_extender.builtins.popup.select_file"),
file_types: list[tuple[str, str]] | None = None,
multiple: bool = False,
single_only: bool = True
) -> str | list[str] | None:
"""
Синхронный диалог выбора файлов с фильтром расширений и контролем количества.
:param file_types: Список кортежей ограничений, например: [("Изображения", "*.png;*.jpg"), ("Все файлы", "*.*")]
:param multiple: Если True, разрешает множественный выбор файлов (Ctrl+Клик)
:param single_only: Если True, то даже при multiple=True вернет ошибку или жестко обрежет массив до 1 элемента
"""
master = _get_active_master()
types = file_types or [(_("ctk_extender.builtins.popup.all_files"), "*.*")]
if multiple:
# askopenfilenames (с буквой 's' на конце) возвращает кортеж путей
paths = filedialog.askopenfilenames(title=title, filetypes=types, master=master)
if not paths:
return None
paths_list = list(paths)
# Если включен жесткий лимит на 1 элемент
if single_only:
return paths_list[0]
return paths_list
else:
path = filedialog.askopenfilename(title=title, filetypes=types, master=master)
return path if path else None