mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-16 20:31:14 +00:00
doc: HLSD for preset plugin APIs
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
# Preset Plugin API — High Level Design
|
||||
|
||||
## Purpose and scope
|
||||
|
||||
The preset plugin API lets Python plugins update the presets OrcaSlicer is using,
|
||||
so users do not have to perform every routine setting change by hand. It covers
|
||||
process, printer, and filament presets. All three use one staged update lifecycle;
|
||||
the way a target is selected is specific to the preset type.
|
||||
|
||||
The API edits Orca's managed edited preset, not a detached copy or a saved preset
|
||||
behind the UI's back. It does not save to disk. Saving, discarding, and transferring
|
||||
unsaved edits remain under Orca's existing preset workflow.
|
||||
|
||||
The API does not create or delete presets or provide off-tab editing. For filament,
|
||||
choosing a different `preset_name` as the update target also selects that preset for
|
||||
the requested slot, following the normal UI flow. A standalone slot-assignment or
|
||||
preset-fork operation is outside this API's contract.
|
||||
|
||||
## Preset and slot model
|
||||
|
||||
Each preset collection maintains a managed edited copy for its selected preset.
|
||||
Settings tabs apply changes to that copy and own the normal dirty-state, validation,
|
||||
save/discard/transfer, and downstream refresh behavior. Plugin updates must use the
|
||||
same path so that the settings tab, dirty indicators, and effective print
|
||||
configuration observe a consistent state.
|
||||
|
||||
Filament selection adds a slot-to-preset mapping. `PresetBundle::filaments` is the
|
||||
catalog, while `PresetBundle::filament_presets` maps each slot to a preset name.
|
||||
Several slots may refer to the same preset. A filament update edits that shared
|
||||
preset, so all slots referring to it are affected. Slot numbers exposed by the API
|
||||
are 1-based, matching the UI.
|
||||
|
||||
## API shape
|
||||
|
||||
Each preset API exposes `begin_update()`, which selects and binds one target, and
|
||||
returns a transaction. The transaction stages setting changes using `set()` and
|
||||
applies them only on explicit `commit()`.
|
||||
|
||||
```python
|
||||
# Filament is targeted by its 1-based slot. Omitting preset_name keeps the
|
||||
# preset currently assigned to the slot; providing it selects that preset for
|
||||
# the slot before staging updates.
|
||||
tx = await orca.host.preset.filament.begin_update(
|
||||
slot_number=2,
|
||||
preset_name="Generic PLA"
|
||||
)
|
||||
tx.set("filament_flow_ratio", "0.98")
|
||||
tx.set("filament_retract_length", "0.8")
|
||||
result = tx.commit()
|
||||
|
||||
# Process and printer default to their active presets. A caller can request a
|
||||
# different preset that is selectable in the corresponding UI dropdown.
|
||||
tx = await orca.host.preset.process.begin_update(
|
||||
preset_name="0.20mm Standard"
|
||||
)
|
||||
tx.set("layer_height", "0.2")
|
||||
tx.commit()
|
||||
```
|
||||
|
||||
The example expresses the intended API contract. The current Python bridge does not
|
||||
support this awaitable transaction shape; supporting it requires new coroutine or
|
||||
continuation handling. Any wait for a UI decision must suspend the plugin operation
|
||||
without blocking Orca's UI thread.
|
||||
|
||||
### Target resolution
|
||||
|
||||
- **Filament:** `slot_number` identifies the slot. If `preset_name` is omitted,
|
||||
the transaction targets the preset currently assigned to that slot. If supplied,
|
||||
it requests that preset for the slot and selects it through the normal UI flow;
|
||||
the name must be among the presets offered by that slot's dropdown. Selecting a
|
||||
different preset changes the slot-to-preset mapping. In both cases the transaction
|
||||
edits the shared preset, not an isolated slot override.
|
||||
- **Process and printer:** omitting `preset_name` targets the currently active
|
||||
preset. Supplying `preset_name` requests the preset by the name shown in its
|
||||
dropdown. It must be one of the choices the dropdown currently offers, rather
|
||||
than merely any preset present in the collection. This is particularly important
|
||||
for process presets, whose selectable choices can depend on the current context.
|
||||
- A transaction targets exactly one preset. Multi-preset transactions are not
|
||||
supported: applying them would require switching among targets while the
|
||||
application has only one managed edited copy per collection.
|
||||
|
||||
Targets are resolved through Orca's normal preset-selection model. The API should
|
||||
not accept raw preset pointers or filesystem paths as target identities. A missing,
|
||||
unavailable, or ambiguous target fails explicitly instead of silently selecting a
|
||||
different preset.
|
||||
|
||||
## Selection and user interaction
|
||||
|
||||
`begin_update()` resolves its target, then delegates any required selection to the
|
||||
shared underlying `switch_preset` operation. It calls `switch_preset` only when the
|
||||
requested target differs from the active target; otherwise it returns a transaction
|
||||
without switching. `switch_preset` is responsible for using Orca's normal preset
|
||||
selection flow, including updating a filament slot assignment when requested. If
|
||||
the normal selection flow requires user input, it opens the relevant existing
|
||||
dialog(s), including unsaved-edit choices such as save, discard, or transfer. The
|
||||
operation is awaitable and remains pending until those interactions finish; it
|
||||
resolves with the switch result or cancellation, without applying staged settings.
|
||||
A canceled switch does not change the requested target or filament slot assignment,
|
||||
but it does not roll back decisions the user already accepted in an earlier dialog.
|
||||
`switch_preset` is a proposed implementation-level coordinator, not an existing
|
||||
function or a separate plugin API. It must use the normal type-specific selection
|
||||
paths and confirm that the requested preset actually became active; a successful
|
||||
selection request alone is not sufficient.
|
||||
|
||||
If the requested target is already active, `begin_update()` returns without a
|
||||
selection prompt, apart from any UI-thread dispatch needed by the bridge. If a
|
||||
different preset is requested, Orca selects it through the normal UI flow. For
|
||||
filament, this also updates the requested slot's assignment when `preset_name`
|
||||
differs from its current assignment. When the current preset has unsaved changes,
|
||||
the plugin operation remains pending until the user chooses among Orca's existing
|
||||
options, such as transfer, discard, or cancel.
|
||||
|
||||
The API never suppresses that warning, chooses an option for the user, or switches
|
||||
away from dirty edits silently. Cancel aborts the operation before any staged target
|
||||
changes are applied. Process and printer updates follow this rule too when the
|
||||
plugin requests a different `preset_name`; they normally return immediately when
|
||||
the active preset is the target.
|
||||
|
||||
## Staging and commit semantics
|
||||
|
||||
`set(key, value)` stages a serialized config value and does not mutate the preset.
|
||||
The transaction is the boundary for a related group of changes, avoiding
|
||||
intermediate partially-updated states. At commit, the API validates keys and values
|
||||
against the target preset type and applies the batch through the existing tab and
|
||||
settings-group update path. Existing setting-specific validation, correction, and
|
||||
callbacks remain authoritative; plugin updates must not create a parallel setter
|
||||
with different behavior.
|
||||
|
||||
A successful commit updates dirty state and downstream configuration/Plater state
|
||||
once for the batch where possible. The edited preset remains unsaved; persistence
|
||||
continues through Orca's normal save action. A validation or commit failure must
|
||||
not leave only some staged target settings applied. This all-or-nothing batch
|
||||
guarantee requires a batch-aware commit path; the current setter applies one option
|
||||
at a time and cannot provide it by simply looping over existing calls. A filament slot assignment
|
||||
made as part of target selection is not a staged setting: after the user accepts
|
||||
the selection flow, it may remain changed even if a later commit fails. The API
|
||||
does not automatically switch back or reverse the user's selection or any accepted
|
||||
transfer/discard decision for the previous preset.
|
||||
|
||||
## Outcomes and consistency
|
||||
|
||||
The API distinguishes target resolution failures, invalid setting/value, user
|
||||
cancellation, stale filament-slot mapping, and commit failure. A result identifies
|
||||
the target preset, whether preset selection changed, and (for filament) whether the
|
||||
slot assignment changed and which slots share the edited preset. A canceled
|
||||
selection applies neither the requested slot assignment nor staged target changes;
|
||||
a failed commit applies none of the staged target settings.
|
||||
|
||||
Filament slot assignments can change while a plugin operation is pending. The API
|
||||
must detect that and reject a stale operation instead of silently editing the new
|
||||
slot assignment. It must not hold raw preset pointers across reloads. Conflicting
|
||||
duplicate writes to one setting in a transaction are rejected rather than resolved
|
||||
by incidental call order.
|
||||
|
||||
The existing one-setting filament call,
|
||||
`orca.host.preset.filament.update(slot_number, key, value)`, can remain as a
|
||||
compatibility shorthand. It shares the staged API's target resolution, selection,
|
||||
validation, and commit path.
|
||||
|
||||
## Design principles
|
||||
|
||||
- **Reduce routine interaction, preserve agency.** Clean switches should be
|
||||
seamless; unsaved user work must still receive the normal warning and decision.
|
||||
- **One source of truth.** UI and plugins share the managed edited copy and setting
|
||||
update path, including validation and callbacks.
|
||||
- **The UI workflow is authoritative.** The API must enter through the same
|
||||
selection and setting-change workflows as the UI, not reproduce their logic or
|
||||
write config values directly. This keeps existing and future side effects active:
|
||||
`on_change` handling for mutually exclusive, invalid, or out-of-range settings;
|
||||
compatibility and dependent-preset updates; and printer-agent changes when the
|
||||
selected printer changes.
|
||||
- **Explicit target and explicit commit.** Plugins know which preset they edit and
|
||||
when staged changes become visible.
|
||||
- **No hidden persistence.** Plugin edits remain unsaved until the user saves them
|
||||
through Orca.
|
||||
- **Shared means shared.** Editing a filament preset through one slot affects every
|
||||
slot assigned to it. Choosing another preset for a slot is explicit in
|
||||
`preset_name`; slot-specific divergence would require a separate fork operation.
|
||||
- **Consistent lifecycle, type-specific targeting.** Process, printer, and
|
||||
filament share transaction and error semantics while respecting their existing
|
||||
selection models.
|
||||
|
||||
## Feasibility against the current codebase
|
||||
|
||||
The design builds on existing preset and tab behavior, but the transaction API and
|
||||
some target-selection behavior are new. The current plugin surface exposes
|
||||
per-setting `update()` calls. For filament, `update_filament_slot()` resolves the
|
||||
preset already assigned to a slot, selects it through `Tab::select_preset()`, and
|
||||
applies one setting through `Tab::apply_preset_config_value()`. It does not change
|
||||
the slot assignment. There is no current `begin_update()`, transaction type, or
|
||||
`switch_preset()` helper.
|
||||
|
||||
`Tab::select_preset()` is the existing basis for switching to process, printer, or
|
||||
filament presets and for invoking the normal unsaved-edit flow. The flow is
|
||||
type-specific: a process selection can trigger compatibility handling for
|
||||
filaments, and a printer selection can involve technology changes and dependent
|
||||
preset compatibility. It may therefore show more than one dialog. Cancellation of
|
||||
a later step does not necessarily undo decisions the user already accepted in an
|
||||
earlier dialog. The plugin operation must preserve those side effects and verify
|
||||
the resulting edited target.
|
||||
|
||||
The filament `preset_name` behavior is an additional integration, not a capability
|
||||
of `Tab::select_preset()`: selecting a different name for a slot must also update
|
||||
the slot-to-preset mapping through the Plater's normal selection behavior. That
|
||||
path updates more than the vector entry, including dependent UI/configuration state.
|
||||
The API should reuse or factor that path rather than call
|
||||
`PresetBundle::set_filament_preset()` alone. It must also define which UI-visible
|
||||
slots are valid, including whether mixed-color virtual slots are targetable.
|
||||
|
||||
Process and printer dropdown choices are constrained by visibility and
|
||||
compatibility, and compatibility may depend on the active context. `Tab::select_preset()`
|
||||
performs its own compatibility and technology checks; merely finding a name in the
|
||||
collection does not prove it is an allowed dropdown choice. Target validation must
|
||||
follow the same rules as the UI.
|
||||
|
||||
The current `PluginHostUi::run_on_ui_thread()` path marshals work synchronously and
|
||||
waits for completion; modal selection dialogs block that operation until answered.
|
||||
It is not an asyncio/coroutine bridge. Thus the user-facing wait exists today, but
|
||||
the proposed Python `await` behavior does not. An awaitable `switch_preset()` needs
|
||||
new plugin scheduling/continuation support and must avoid blocking the UI thread or
|
||||
deadlocking callers running on threads that the UI itself waits for.
|
||||
|
||||
Finally, `Tab::apply_preset_config_value()` validates and commits one setting at a
|
||||
time. It backs up one option and invokes callbacks that can adjust related settings.
|
||||
A transaction that promises all-or-nothing updates and one downstream refresh needs
|
||||
a batch-aware prepare/commit mechanism that accounts for those callback effects;
|
||||
repeating the current single-setting method would allow partial updates and repeated
|
||||
refreshes. Since `begin_update()` performs target selection before the caller stages
|
||||
values, invalid staged values can still follow a user-visible selection prompt. That
|
||||
is a consequence of this API shape and should remain explicit in its behavior.
|
||||
|
||||
## Integration boundary
|
||||
|
||||
The plugin API coordinates target resolution, preset selection, staged values, and
|
||||
the settings commit path. It does not own or duplicate preset dirty-state logic,
|
||||
selection prompts, setting validation, or persistence. Target changes must flow
|
||||
through the same UI selection path so dependent behavior—such as selecting the
|
||||
printer agent—still runs. Setting changes must flow through the same UI change path
|
||||
so `on_change` and related validation/correction behavior still runs. The
|
||||
integration must bridge potential UI prompts to the Python operation
|
||||
asynchronously, reuse the normal Plater slot-selection behavior for explicit
|
||||
filament targets, then commit a batch without losing existing per-setting behavior.
|
||||
Reference in New Issue
Block a user