All docs
A View of params and actions
The base View — param() fields plus @action buttons, no kind — collects input and drives actions. Author it with @tool.view; location= only sets where it shows.
The base View has no kind: it is param() fields plus @action buttons —
the same declarative object the inspector uses to edit step settings. It collects input and runs
actions; give it a kind and it becomes a data view instead. An @action
handler reads the fields as typed attributes, so there is no values dict to keep in sync. (Where it
shows — sidebar, editor, or an overlay — is location=, separate from what it is. A no-kind View
docked in the sidebar is what people used to call a "panel".)
Author a View#
Subclass View, declare param() fields, and mark operations with @action:
from lsdtools import View, action, param
class ClipPanel(View):
cutoff = param(float, default=0.5, min=0.0, max=1.0, label="Cut-off")
@action(label="Apply", style="suggested")
def apply(self):
return f"keeping grades ≥ {self.cutoff}"
panel = ClipPanel(cutoff=0.7)
panel.invoke("apply") # → 'keeping grades ≥ 0.7'
invoke("apply") is the one dispatch entry for every front-end — a GUI button, the CLI, an
agent, or a test all go through it. param() gives you typing, coercion, min/max validation, and
the labelled field for free; @action's style is None, "suggested", or "destructive".
Dock it with @tool.view#
@tool.view decorates a function (ctx) -> View (return a View instance or class). With
location="sidebar" the desktop docks the result in the sidebar. When it mounts, the runtime injects
the host Context into the View's self.ctx, so an action body can reach the host —
self.ctx.ui.notify(...), self.ctx.ui.show(...) — with no manual wiring:
from lsdtools import Tool, Context, View, action, param
tool = Tool("grade", label="Grade tools")
class ClipPanel(View):
cutoff = param(float, default=0.5, min=0.0, max=1.0, label="Cut-off")
@action(label="Apply", style="suggested")
def apply(self):
self.ctx.ui.notify(f"cut-off set to {self.cutoff}")
@tool.view("grade.clip", title="Clip grades", location="sidebar")
def clip_panel(ctx: Context):
return ClipPanel()
# → a "Clip grades" panel docks in the sidebar
self.ctx.ui offers notify(message, level="info"), show(view) (open a View as a modal), and
confirm(message). On a standalone View you build yourself, self.ctx is None until you set it.
Show it standalone#
Any View presents itself with show() — a window when a front-end is loaded, a console rendering
when headless — so you can exercise the form without the desktop:
ClipPanel(cutoff=0.7).show("text")
# ── ClipPanel ──
# Cut-off: 0.7
# actions: Apply
show() is the View's own verb; attach it to the engine instead with engine.add(ClipPanel()) and
engine.show() to present it alongside your other views.
Overlays#
@tool.view(location="overlay") is the same idea docked over the 3D viewer as a HUD: its function
also returns a View (typically an action-only one), and the desktop reads the dock / view_kind
hints to place it. Everything on this page applies — an overlay is just a View shown in a different
spot.
Related#
Learn — Everything you show is a View · Data views · Commands & actions
Start — How an LSD package fits together · Naming: which is which
Examples — A dockable panel · A complete package </content>
Frequently asked questions
How does an action get at the host (notify, show a dialog)?
Through self.ctx. When a @tool.view is mounted, the runtime injects the host Context into self.ctx, so an @action body can call self.ctx.ui.notify(...) or self.ctx.ui.show(...) with no wiring. On a standalone View you construct yourself, self.ctx is None until you set it.
Is there still a Panel or Field class?
No. The old JSON ui()/Panel/Field builder was removed, and there is no Panel class or @tool.panel decorator. A View with no kind is just a View — param() for fields, @action for buttons — the same object the desktop inspector already edits. "Panel" is only the old informal word for one docked in the sidebar; where it shows is location= (sidebar / editor / overlay), separate from what it is.