Observant Integration¶
QtPie's reactive features are powered by Observant (PyPI), a reactive state management library for Python.
You don't need to understand Observant to use QtPie - the state() function and Widget[T] handle everything automatically. This page is for users who want to understand the internals or use Observant directly.
What is Observant?¶
Observant provides:
- Observable - A value that notifies listeners when it changes
- ObservableProxy - Wraps any object (dataclass, etc.) making all its fields observable
- Validation - Field-level validation with reactive error lists
- Dirty Tracking - Know which fields changed since last save
- Undo/Redo - Per-field history with configurable limits
How QtPie Uses Observant¶
state() Fields¶
When you use state():
Under the hood, QtPie creates an ObservableProxy that wraps your value. When you assign to self.count, the proxy notifies all bound widgets to update.
Widget[T] Models¶
When you use Widget[T]:
QtPie automatically:
- Creates a
Person()instance asself.model - Wraps it in
ObservableProxyasself.model_observable_proxy - Auto-binds widget fields to model fields by name
The model_observable_proxy enables two-way binding, validation, dirty tracking, and undo/redo.
Using Observant Directly¶
Sometimes you need direct access to Observant features.
Subscribing to Changes¶
from qtpie.state import get_state_observable
@widget
class Counter(QWidget):
count: int = state(0)
def setup(self) -> None:
obs = get_state_observable(self, "count")
obs.on_change(lambda value: print(f"Count changed to {value}"))
Working with Widget[T] Proxy¶
@widget
class PersonEditor(QWidget, Widget[Person]):
name: QLineEdit = make(QLineEdit)
def setup(self) -> None:
# Get observable for a field
name_obs = self.model_observable_proxy.observable(str, "name")
# Subscribe to changes
name_obs.on_change(lambda v: print(f"Name: {v}"))
# Read/write
current = name_obs.get()
name_obs.set("Alice")
Nested Path Access¶
# Get observable for nested path
city_obs = self.model_observable_proxy.observable_for_path("address.city")
# With optional chaining
owner_name_obs = self.model_observable_proxy.observable_for_path("owner?.name")
When to Use Observant Directly¶
You usually don't need to. QtPie's bind= parameter handles most cases.
Use Observant directly when you need:
- Custom change handlers beyond what
bind=provides - Programmatic access to observables for complex logic
- Features not exposed through QtPie's API
Learn More¶
- Observant Documentation
- Observant on PyPI
- ObservableProxy Reference
- Nested Paths
- Validation
- Undo/Redo
See Also¶
- Reactive State - Using
state()for reactive fields - Record Widgets - Using
Widget[T]for forms - Validation - Field validation
- Dirty Tracking - Change detection
- Undo & Redo - History management