Skip to content

make_later()

Declare a widget field that will be initialized later, typically in the setup() lifecycle hook.

Overview

make_later() is used when you need to initialize a field after the widget is constructed, usually because:

  1. The field depends on other fields that need to be initialized first
  2. You need to reference self during initialization
  3. The field requires complex initialization logic

Signature

def make_later() -> Any

Returns a dataclass field that is excluded from __init__ and must be set manually.

Basic Usage

Simple Deferred Field

from qtpie import widget, make_later

@widget()
class MyWidget(QWidget):
    value: int = make_later()

    def setup(self) -> None:
        self.value = 42

The field is uninitialized until you set it in setup().

Common Use Case: Secondary ObservableProxy

Note: For Widget[T] classes, QtPie automatically creates model and record_observable_proxy - you don't need make_later() for the default case.

Use make_later() when you need a secondary proxy for a different model:

from dataclasses import dataclass
from observant import ObservableProxy
from qtpie import Widget, make, make_later, widget
from qtpy.QtWidgets import QLineEdit, QWidget

@dataclass
class Dog:
    name: str = ""

@dataclass
class Owner:
    name: str = ""

@widget()
class DogEditor(QWidget, Widget[Dog]):
    # model and record_observable_proxy for Dog are auto-created
    name_edit: QLineEdit = make(QLineEdit, bind="name")  # binds to Dog

    # Secondary model needs make_later() for its proxy
    owner_model: Owner = make(Owner)
    owner_proxy: ObservableProxy[Owner] = make_later()
    owner_edit: QLineEdit = make(QLineEdit, bind="owner_proxy.name")

    def setup(self) -> None:
        self.owner_proxy = ObservableProxy(self.owner_model, sync=True)

Why use make_later() here? - The owner_proxy needs to wrap the owner_model object - But owner_model doesn't exist yet during field initialization - So we defer owner_proxy creation until setup() when owner_model is available

Widget[T] with make_later()

When using Widget[T], you can defer model initialization:

@widget()
class PersonEditor(QWidget, Widget[Person]):
    record: Person = make_later()  # Will be set in setup()
    name: QLineEdit = make(QLineEdit)

    def setup(self) -> None:
        # Custom model initialization
        self.record = Person(name="Charlie", age=25)

Error Handling

If you use make_later() for the model but forget to set it in setup(), QtPie will raise a helpful error:

@widget()
class PersonEditor(QWidget, Widget[Person]):
    record: Person = make_later()
    name: QLineEdit = make(QLineEdit)
    # Oops! No setup() method

# Raises: ValueError: Field 'model' marked with make_later() was not set in setup()
w = PersonEditor()

This prevents silent failures when you accidentally omit initialization.

Multiple Deferred Fields

You can use make_later() for multiple secondary proxies:

from dataclasses import dataclass
from observant import ObservableProxy
from qtpie import Widget, make, make_later, widget
from qtpy.QtWidgets import QLineEdit, QWidget

@dataclass
class Dog:
    name: str = ""

@dataclass
class Cat:
    name: str = ""

@widget()
class PetEditor(QWidget, Widget[Dog]):
    # Dog's model/record_observable_proxy are auto-created
    dog_name: QLineEdit = make(QLineEdit, bind="name")  # binds to Dog

    # Cat needs manual proxy setup
    cat_model: Cat = make(Cat)
    cat_proxy: ObservableProxy[Cat] = make_later()
    cat_name: QLineEdit = make(QLineEdit, bind="cat_proxy.name")

    def setup(self) -> None:
        self.cat_proxy = ObservableProxy(self.cat_model, sync=True)

When to Use make_later()

Use make_later() when: - Field initialization depends on other fields - You need to reference self during initialization - You want to defer complex setup logic to setup()

Don't use make_later() when: - The field can be initialized independently → Use make() instead - You're just creating a simple widget → Use make() instead

Comparison: make() vs make_later()

# ✓ Use make() - field is independent
label: QLabel = make(QLabel, "Hello")

# ✓ Use make() - simple default value
model: Dog = make(Dog)

# ✓ Use make_later() - depends on model field
proxy: ObservableProxy[Dog] = make_later()

# ✗ DON'T use make_later() unnecessarily
label: QLabel = make_later()  # Just use make()!

Implementation Details

Under the hood, make_later() creates a dataclass field with: - init=False - excluded from __init__ - Special metadata flag for QtPie tracking

# Equivalent to:
field(init=False, metadata={"qtpie_make_later": True})

Lifecycle Flow

@widget()
class Example(QWidget):
    regular: QLabel = make(QLabel, "I'm created first")
    deferred: int = make_later()

    def setup(self) -> None:
        # Called after regular fields are initialized
        self.deferred = 42

# Execution order:
# 1. __init__() creates 'regular' field
# 2. setup() runs → 'deferred' gets set
# 3. Widget is ready

See Also

Tips

  1. Always set deferred fields in setup()
  2. QtPie will error if you forget (for Widget[T] models)
  3. Your code will error at runtime for other fields

  4. Use for ObservableProxy pattern

  5. This is the #1 use case
  6. Model → make(), Proxy → make_later()

  7. Keep it simple

  8. If you can use make(), use it
  9. Only defer when truly necessary