All docs
Examples
Example: drag-drop import
A drag-drop import template — dropping a .csv builds a pre-wired entity via @tool.template and an @tool.entity builder function.
TL;DRA @tool.template registers a file type (extensions=[".csv"]); its build function (ctx, files, name=None) returns a ready-to-run Entity by calling an @tool.entity builder's .build(...). Drop a file on the desktop and a whole pipeline appears, already wired.
The nicest way to start a pipeline is to drop a file on the canvas and have a whole, wired entity
appear. A @tool.template maps a file extension to a build
function that returns a pre-wired entity.
The code#
Python
# tool.py
from lsdtools import Tool, Context, Source, Entity, Table
tool = Tool("cloud", label="Point Cloud")
@tool.load(source_kinds=("file",))
def read_points(src: Source) -> Table:
"""Read a points file (needs x, y, z columns)."""
return src.read()
@tool.shape
def near_origin(t: Table, radius: float = 100.0) -> Table:
"""Keep points within `radius` of the origin on the XY plane."""
r2 = t["x"] * t["x"] + t["y"] * t["y"]
return t.filter(r2 <= radius * radius)
@tool.entity(label="Point cloud", icon="lsd-folder-symbolic")
def point_cloud(path: str = "", radius: float = 100.0) -> Table:
"""An entity that reads a file, then trims it to points near the origin."""
src = read_points(Source.file(path))
return near_origin(src, radius=radius)
@tool.template("point-csv", label="Point cloud (CSV)", extensions=[".csv"])
def import_points(ctx: Context, files, name=None) -> Entity:
"""Dropped .csv → a pre-wired Point cloud entity."""
return point_cloud.build(name=name or files.main.stem, path=str(files.main))
What each line does#
@tool.entitypoint_cloud— a builder function: its signature (path,radius) becomes the entity's declarative parameters, and its body runs once at creation to wire the child steps. It returns the terminal step; LSD collects that step and everything upstream of it.@tool.template("point-csv", ..., extensions=[".csv"])— registers the drop target. The build function is(ctx, files, name=None) -> Entity.files.main— the dropped file as aPath(files["role"]addresses a specific role for multi-file templates).files.main.stemgives a sensible default entity name.point_cloud.build(name=..., path=...)— creates the entity of that type, passing the template's values as its parameters. Because the builder wiresread_points → near_origin, the returned entity is ready to run.
Run it in Python#
The graph the template builds — run the same wiring directly to see the result:
Python
from lsdtools import Engine, Source, Table
Table({"x": [0.0, 200.0], "y": [0.0, 0.0], "z": [1.0, 2.0]}).write_csv("cloud.csv")
pts = near_origin(read_points(Source.file("cloud.csv")), radius=100.0)
Engine().run(pts).output.print()
# → x y z
# 0 0 1 (the point 200 m out is dropped)
And the template/entity path itself — what a drop triggers:
Python
entity = point_cloud.build(name="cloud", path="cloud.csv") # what import_points returns
Engine().add(entity).run("cloud") # runs the pre-wired pipeline
Try changing it#
- Add
roles=[FileRole("collar"), FileRole("survey")](fromlsdtools.advanced) for a multi-file drop, and read them withfiles["collar"]/files["survey"]. - Give the template a
sniff=function so it only claims CSVs whose header hasx, y, z. - Set
priority=so your template wins over another that also handles.csv.
Related#
Learn — Entity types · Commands & actions · Load steps
Examples — File loader · A complete package
By LSD Team · Last updated Jul 17, 2026
Ask on Discord
View as Markdown