Sandbox — LSD is in active testing. All systems operational·payments are test-mode (no real charges).
Overview Use Cases News Docs Tool Store Support Community Get LSD
All docs
Docs/ Views/ Everything you show is a View
Views

Everything you show is a View

The show side of LSD is one declarative View class: with a kind it represents data, with no kind it collects input, and show() presents either. None import GTK.

TL;DRLSD has two kinds of thing: entities you run and views you show. The show side is one declarative class, View. With a kind it represents data (a table, chart, or flowchart) via to_text()/to_image(); with no kind it collects input — param() fields plus @action buttons (a sidebar form, informally a "panel"). There is no separate Panel type. show() presents either — a window, or console text when headless — and none import GTK. The inheritance chain is Configurable → View.

LSD splits everything an Engine holds into two kinds of thing: entities you run (pipelines) and views you show (UI). This page is the show side. See How an LSD package fits together for the whole picture and Naming: which is which for the exact words.

The show side is one declarative class, View. A View does one of two things — it represents data (a table, chart, or flowchart) or collects input (params + actions) — and you present it with show(). There is no separate "panel" type; the difference is simply whether the View has a kind. None of it imports GTK — the desktop registers a renderer and LSD stays front-end-agnostic, so the same View works in a window or as console text.

One View, two shapes#

The View Is You author it with Headless rendering
Represents data a View with a kind — a table, chart, flowchart, or custom @tool.view → a View to_text() / to_image()
Collects input a View with no kindparam() fields + @action (the base View; informally a panel when docked in the sidebar) @tool.view → a View to_text() (params + actions)

Where a View lands is set by location= (editor / sidebar / overlay), and any View is presented with a_view.show() or engine.add(a_view) + engine.show().

Data view — represent data#

A data view is a View with a kind. It takes data through feed(data) and renders it. The three built-in kinds come from the views façade — views.table, views.chart, views.flowchart — and you can subclass View for a custom one. Every data view answers to_text() and to_image(), so an agent or the CLI sees it with no display:

Python
from lsdtools import views

w = views.chart(kind="line", title="Loss")
w.feed({"series": [{"name": "train", "x": [0, 1, 2, 3], "y": [0.9, 0.6, 0.4, 0.25]}]})
print(w.to_text())
# → [line] Loss
#     train: 4 pts, min=0.25 max=0.9 last=0.25

feed(data) is for literal data. To make a data view follow a pipeline, hand it an entity with one verb — watch — instead of feeding a one-shot snapshot:

Python
grid = views.table(title="Grades").watch(entity)   # holds the entity's output; refreshes on re-run

watch reads the entity's live output and refreshes on every re-run (views.table(entity.output) would read once and never update). watch returns the view, so it chains as one phrase, and the framework wires the event bus for you. It is the same verb a deliver step's ViewerPayload is routed through.

Collect input — a View with no kind#

A View with no kind is param() fields plus @action buttons — the base View, for collecting input and running actions. Where it shows is location= (the desktop docks a sidebar one at the side — what people informally call a panel — but that's just placement). An action handler reads the fields as typed attributes; there is no values dict to keep in sync. See A View of params and actions for the full story.

Python
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}"

ClipPanel(cutoff=0.7).invoke("apply")   # → 'keeping grades ≥ 0.7'

Presenting a View#

Every View has the base verb show(): a window when a front-end is loaded, a console rendering otherwise. Attaching a view to the engine mirrors attaching an entity — engine.add(a_view) then engine.show() is the exact partner of engine.add(entity) then engine.run():

Python
from lsdtools import Engine, views, Table

t = Table({"name": ["ann", "bob"], "score": [12, 30]})

engine = Engine()
engine.add(views.table(t, title="Grades"))
engine.show()          # a window, or console text when headless

a_view.show(mode="text") always prints the text rendering, which is what makes a view testable without a display.

The inheritance chain#

One short chain underlies it all, which is why param() behaves the same everywhere:

  • Configurable — anything with param() fields (steps, entities, the engine).
  • View — a Configurable that also has @action buttons and optional data (feed + to_text / to_image). With a kind it represents data; with none it is an input form.

Steps are Configurables, not Views — they have parameters but no actions. See Naming: which is which for the overloaded words (View the class vs @tool.view the decorator, views.table vs the Table class).

LearnParams & actions · Data views · Custom views

StartHow an LSD package fits together · Naming: which is which

ExamplesA dockable panel · A complete package

Frequently asked questions

Are panels and views two different things?

No — there is one View class. A View with a kind (table/chart/flowchart/custom) represents data via feed() + to_text()/to_image(); a View with no kind collects input — param() fields plus @action buttons. "Panel" is just informal shorthand for that no-kind View when it is docked in the sidebar; there is no separate Panel class or @tool.panel decorator. show() presents either; neither imports GTK.

Do I need a display to build or test a surface?

No. A View introspects headless (get_parameters / get_actions) and, when it has a kind, renders with to_text() / to_image(). View.show(mode="text") prints the console rendering, so the same code runs in the CLI, in CI, or from an agent. GTK only enters when the desktop registers the renderer.

By LSD Team · Last updated Jul 18, 2026 Ask on Discord View as Markdown