Исходный код EJIO.ctk_extender.widget.form.fields.option_menu

## /ctk_extender/widget/form/fields/option_menu_field.py

import customtkinter as ctk
from typing import Any, Literal
from .base import InputField
from ...container import WidgetContainer
from ...components import Label
from ...geometry import GeometryStrategy, GridStrategy

__all__ = [
    'FormOptionMenuField',
]

FieldLayout = Literal['row', 'column']


[документация] class FormOptionMenuField(WidgetContainer, InputField): """ Высокоуровневое поле формы на базе выпадающего списка ctk.CTkOptionMenu. Интегрировано в подсистему валидации форм. """ __slots__ = ('_name', '_label_text', '_values', '_default_value', '_layout', '_required', '_menu_instance') def __init__( self, parent: Any, name: str, label_text: str, values: list[str], default_value: str | None = None, layout: FieldLayout = 'row', required: bool = False, geometry: GeometryStrategy | None = None, **kwargs: Any ) -> None: self._name: str = name self._label_text: str = label_text self._values: list[str] = values # Если дефолтное значение не задано, берем первый элемент списка self._default_value: str = default_value or (values[0] if values else "") self._layout: FieldLayout = layout self._required: bool = required self._menu_instance: ctk.CTkOptionMenu | None = None super().__init__(parent, geometry, **kwargs)
[документация] def build(self) -> ctk.CTkFrame: """Строит каркас поля формы с поддержкой сеточного позиционирования.""" frame_instance = super().build() frame_instance.configure(fg_color="transparent") # 1. Создаем текстовую подпись (Label) lbl = self.add_widget(Label, text=self._label_text) # 2. Создаем нативный выпадающий список ctk.CTkOptionMenu # Значение переменной ctk.StringVar будет отслеживать выбранный элемент self._menu_instance = ctk.CTkOptionMenu( frame_instance, values=self._values, **self._kwargs ) self._menu_instance.set(self._default_value) # 3. Выстраиваем сетку grid в зависимости от режима layout if self._layout == 'row': lbl.set_geometry(GridStrategy(row=0, column=0, sticky="w", padx=(0, 10), pady=2)) self._menu_instance.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))) self._menu_instance.grid(row=1, column=0, sticky="ew", pady=(0, 2)) frame_instance.grid_columnconfigure(0, weight=1) return frame_instance
@property def name(self) -> str: return self._name
[документация] def get_value(self) -> str: if self._menu_instance is None: return "" return str(self._menu_instance.get())
[документация] def set_value(self, value: Any) -> None: if self._menu_instance is not None: self._menu_instance.set(str(value))
[документация] def clear(self) -> None: if self._menu_instance is not None: self._menu_instance.set(self._default_value)
[документация] def validate(self) -> str | None: """Проверяет, выбрано ли значение, если поле обязательно.""" current_val = self.get_value() if self._required and (not current_val or current_val == self._default_value and len( self._values) > 1 and self._default_value == self._values[0]): # Кастомная логика: если поле обязательно, пользователь должен выбрать элемент, отличный от заглушки return f"Пожалуйста, выберите значение в поле '{self._label_text}'." return None
[документация] def destroy(self) -> None: self._menu_instance = None super().destroy()