State Management
This page documents how pyqt-reactive manages form state and integrates with ObjectState for hierarchical configuration management.
Form State Management
Purpose
pyqt-reactive manages form state through the ParameterFormManager and FieldChangeDispatcher.
Forms are automatically generated from dataclasses and maintain bidirectional binding with
the underlying data model.
Key Components
- ParameterFormManager
Generates PyQt6 forms from dataclass definitions
Manages widget creation and layout
Collects and validates user input
Integrates with ObjectState for lazy configuration
- FieldChangeDispatcher
Broadcasts field changes across the form
Enables reactive updates when one field changes
Supports conditional field visibility and enablement
Triggers validation and preview updates
- ValueCollectionService
Gathers current values from all widgets
Handles type conversion and validation
Returns typed dataclass instances
ObjectState Integration
Purpose
pyqt-reactive integrates with ObjectState to support lazy configuration and hierarchical inheritance. Forms can display placeholder text showing inherited values from parent contexts.
Key Features
Lazy Resolution: Fields with
Nonevalues inherit from context hierarchyProvenance Tracking: UI shows where each value comes from (global, pipeline, step)
Placeholder Text: Empty fields display inherited values as placeholders
Dirty Tracking: Automatic detection of unsaved changes
Undo/Redo: Git-style history with branching timelines
Integration Pattern
When a form is created from a dataclass with ObjectState context:
Form widgets are created for each field
Placeholder text shows inherited values from parent scopes
User edits update ObjectState’s live parameters
Dirty fields are tracked automatically
Save commits changes to ObjectState baseline
Widget Protocols
Purpose
pyqt-reactive uses ABC-based protocols to define type-safe widget contracts. This eliminates duck typing in favor of explicit, fail-loud inheritance-based architecture.
Core Protocols
- ValueGettable
Widgets that can return their current value
Method:
get_value() -> Any
- ValueSettable
Widgets that can accept a new value
Method:
set_value(value: Any) -> None
- PlaceholderCapable
Widgets that display placeholder text for inherited values
Method:
set_placeholder_text(text: str) -> None
- RangeConfigurable
Widgets with min/max constraints (spinboxes, sliders)
Methods:
set_minimum(),set_maximum()
- EnumSelectable
Widgets that display enum options (comboboxes)
Method:
set_enum_values(values: List[str]) -> None
- ChangeSignalEmitter
Widgets that emit signals when values change
Signal:
value_changed
Form Lifecycle
Purpose
Understanding the form lifecycle helps you integrate custom logic at the right points.
Stages
Creation:
ParameterFormManager(ConfigClass)- Analyzes dataclass fields - Creates widgets based on types - Sets up signal connectionsDisplay:
form.show()- Renders widgets in layout - Initializes placeholder text (if ObjectState context) - Connects change signalsUser Interaction - User edits fields - Widgets emit
value_changedsignals - FieldChangeDispatcher broadcasts changes - Dependent fields update reactivelyCollection:
form.collect_values()- Gathers current values from all widgets - Validates types and constraints - Returns typed dataclass instanceCleanup:
form.closeEvent()- Unregisters from cross-window updates - Cleans up signal connections - Removes flash overlays
ObjectStateRegistry
Purpose
The registry coordinates saved/live baselines across all ObjectStates so that application code can distinguish “proposed” vs “committed” values while showing immediate UI feedback.
Key Methods
- Baseline Management
get_baseline(scope_id): Get the saved baseline for a scopeset_baseline(scope_id, value): Update the saved baselineget_live(scope_id): Get current live (proposed) values
- History Navigation
time_travel_back(): Revert to previous statetime_travel_forward(): Move forward in historytime_travel_to_snapshot(id): Jump to specific snapshottime_travel_to_head(): Jump to latest statecreate_branch(name): Create experiment branchswitch_branch(name): Switch to different branch
- Dirty Tracking
get_dirty_states(): Get all modified scopesis_dirty(scope_id): Check if scope has unsaved changes
Flash Callbacks
ObjectState notifies pyqt-reactive of parameter changes through the on_resolved_changed
callback mechanism. This enables visual feedback (flash animations) when values change.
See Flash Callback System for details on how the flash callback system works.
Notes
Registry methods are classmethods; the registry is effectively a singleton.
History/undo is covered separately in Time Travel and Branching.
Forms automatically register with the registry when created in ObjectState context.