Parametric Widget Creation ========================== **Dataclass-based configuration for widget creation strategies.** *Module: pyqt_reactive.forms.widget_creation_config* The Widget Creation Problem --------------------------- pyqt-reactive configuration is deeply nested. A pipeline configuration contains plate configurations, which contain step configurations, which contain processing parameters—many of which are themselves nested dataclasses. Some of these nested types are optional (``Optional[DataclassType]``), requiring checkbox-gated visibility. Building forms for this structure requires answering many questions for each field: - Is this a simple value (string, int, enum) or a nested structure? - Should it have a label? A reset button? - Does it need a container layout, and if so, horizontal or vertical? - Is it optional? Does it need a checkbox to toggle None/Instance? Before this system, these decisions were scattered across if/elif chains with duplicated logic. Adding a new widget type (like multi-select enums) required modifying multiple code paths that had grown organically and inconsistently. The Solution: Configuration Objects ----------------------------------- The parametric widget creation system consolidates all these decisions into configuration dataclasses. Each widget creation type (REGULAR, NESTED, OPTIONAL_NESTED) has a corresponding ``WidgetCreationConfig`` that declares: - What kind of container to create - What kind of main widget to create - Whether labels, reset buttons, or checkboxes are needed - Handler functions for optional features This follows the same pattern used across configuration registry systems: replace conditionals with configuration lookup. Widget Creation Types --------------------- There are three fundamentally different ways to render a configuration field, distinguished by nesting and optionality: .. code-block:: python from pyqt_reactive.forms.widget_creation_config import WidgetCreationType class WidgetCreationType(Enum): REGULAR = "regular" # Simple widgets (int, str, bool, enum) NESTED = "nested" # Nested dataclass forms OPTIONAL_NESTED = "optional_nested" # Optional[Dataclass] with checkbox Each type has a corresponding ``WidgetCreationConfig`` dataclass: .. code-block:: python @dataclass class WidgetCreationConfig: layout_type: str # "horizontal" or "vertical" is_nested: bool # Whether creates sub-form create_container: WidgetOperationHandler # Container widget factory setup_layout: Optional[WidgetOperationHandler] # Layout configuration create_main_widget: WidgetOperationHandler # Main widget factory needs_label: bool # Show field label needs_reset_button: bool # Show reset button needs_unwrap_type: bool # Unwrap Optional[T] to T is_optional: bool = False # Has None/Instance toggle needs_checkbox: bool = False # Show optional checkbox create_title_widget: Optional[OptionalTitleHandler] = None connect_checkbox_logic: Optional[CheckboxLogicHandler] = None ParameterFormManager ABC ------------------------ The widget creation system needs a consistent interface to the form manager. This ABC defines that interface, inspired by React's component model. Like React components, form managers maintain state (``parameters``, ``nested_managers``, ``widgets``) and provide methods to mutate that state (``update_parameter``, ``reset_parameter``). The React analogy is deliberate: parameter forms are essentially hierarchical UI components with controlled inputs. The ``create_widget`` method is analogous to React's ``render()``—it produces UI elements based on current state. The ``_apply_to_nested_managers`` method enables recursive traversal, similar to React's component tree: .. code-block:: python from pyqt_reactive.forms.widget_creation_types import ParameterFormManager class ParameterFormManager(ABC): """React-quality reactive form manager interface.""" # State (like React component state) parameters: Dict[str, Any] nested_managers: Dict[str, Any] widgets: Dict[str, Any] # State mutations (like setState) @abstractmethod def update_parameter(self, param_name: str, value: Any) -> None: ... @abstractmethod def reset_parameter(self, param_name: str) -> None: ... # Widget creation (like render) @abstractmethod def create_widget(self, param_name: str, param_type: Type, current_value: Any, widget_id: str) -> Any: ... # Component tree traversal @abstractmethod def _apply_to_nested_managers(self, callback: Callable) -> None: ... Type Definitions ---------------- The handler functions have complex signatures because they need access to everything the form manager knows about the field being rendered. TypedDicts and type aliases provide documentation and type checking for these signatures: Handler type aliases for type safety: .. code-block:: python # Main widget operation handler WidgetOperationHandler = Callable[ [ParameterFormManager, ParameterInfo, DisplayInfo, FieldIds, Any, Optional[Type], ...], Any ] # Optional title widget handler OptionalTitleHandler = Callable[ [ParameterFormManager, ParameterInfo, DisplayInfo, FieldIds, Any, Optional[Type]], Dict[str, Any] ] # Checkbox toggle logic handler CheckboxLogicHandler = Callable[ [ParameterFormManager, ParameterInfo, Any, Any, Any, Any, Any, Type], None ] Helper TypedDicts: .. code-block:: python class DisplayInfo(TypedDict, total=False): field_label: str checkbox_label: str description: str class FieldIds(TypedDict, total=False): widget_id: str optional_checkbox_id: str Configuration Registry ---------------------- The ``_WIDGET_CREATION_CONFIG`` dict maps types to configurations: .. code-block:: python _WIDGET_CREATION_CONFIG = { WidgetCreationType.REGULAR: WidgetCreationConfig( layout_type="horizontal", is_nested=False, create_container=_create_regular_container, setup_layout=None, create_main_widget=_create_regular_widget, needs_label=True, needs_reset_button=True, needs_unwrap_type=False, ), WidgetCreationType.NESTED: WidgetCreationConfig( layout_type="vertical", is_nested=True, create_container=_create_nested_container, setup_layout=_setup_nested_layout, create_main_widget=_create_nested_form, needs_label=False, needs_reset_button=False, needs_unwrap_type=True, ), WidgetCreationType.OPTIONAL_NESTED: WidgetCreationConfig( layout_type="vertical", is_nested=True, is_optional=True, needs_checkbox=True, create_container=_create_optional_container, setup_layout=_setup_nested_layout, create_main_widget=_create_nested_form, create_title_widget=_create_optional_title_widget, connect_checkbox_logic=_connect_optional_checkbox_logic, needs_label=False, needs_reset_button=False, needs_unwrap_type=True, ), } Handler Functions ----------------- Handlers encapsulate widget creation logic: .. code-block:: python def _create_nested_form(manager, param_info, display_info, field_ids, current_value, unwrapped_type, **kwargs) -> Any: """Create nested form and store in manager.nested_managers.""" nested_manager = manager._create_nested_form_inline( param_info.name, unwrapped_type, current_value ) manager.nested_managers[param_info.name] = nested_manager return nested_manager.build_form() def _create_optional_title_widget(manager, param_info, display_info, field_ids, current_value, unwrapped_type): """Create checkbox + title + reset button for optional dataclass.""" # Returns (title_widget, checkbox) tuple ... Usage Pattern ------------- The ``ParameterFormManager`` uses the config for type-dispatched widget creation: .. code-block:: python def _create_parameter_widget(self, param_info, param_type, current_value): # Determine widget type widget_type = self._classify_widget_type(param_type) # Get configuration config = _WIDGET_CREATION_CONFIG[widget_type] # Execute creation pipeline container = config.create_container(self, param_info, ...) if config.setup_layout: config.setup_layout(self, param_info, container, ...) widget = config.create_main_widget(self, param_info, ...) if config.needs_label: self._add_label(container, param_info) if config.needs_reset_button: self._add_reset_button(container, param_info) See Also -------- - :doc:`widget_protocol_system` - ABC contracts for widget operations - :doc:`field_change_dispatcher` - Dispatches changes from created widgets - :doc:`parameter_form_service_architecture` - Service architecture using these configs - :doc:`abstract_manager_widget` - ABC that orchestrates widget creation