Plugin API Reference

Plugin API Reference

Complete API reference for LichtFeld Studio plugins.


Registration

import lichtfeld as lf

lf.register_class(cls)           # Register a Panel, Operator, or Menu class
lf.unregister_class(cls)         # Unregister a Panel, Operator, or Menu class

Scrub Controls

from lfs_plugins import ScrubFieldController, ScrubFieldSpec

Retained panels can use these helpers to turn a range slider row (input.setting-slider) into a scrub field:

  • Drag horizontally on the scrub field to scrub values.
  • Click the numeric text area to type a value directly.
  • The controller keeps the displayed value in sync and applies clamping, snapping, and fill width updates.
SCRUB_FIELD_SPECS = {
    "quality": ScrubFieldSpec(min_value=0.0, max_value=1.0, step=0.01, fmt="%.2f"),
}

class MyPanel(lf.ui.Panel):
    # ...
    def on_bind_model(self, ctx):
        model = ctx.create_data_model("my_panel")
        if model is None:
            return
        model.bind("quality", lambda: f"{self._quality:.2f}", self._set_quality)

    def __init__(self):
        self._scrub_fields = ScrubFieldController(
            SCRUB_FIELD_SPECS,
            self._get_scrub_value,
            self._set_scrub_value,
        )

    def on_mount(self, doc):
        self._scrub_fields.mount(doc)

    def on_unmount(self, doc):
        self._scrub_fields.unmount()

    def on_update(self, doc):
        return self._scrub_fields.sync_all()

Each scrubbed data-value still needs a normal model.bind(...) entry. The controller upgrades the range input UI, but it does not create data-model variables for you.

ScrubFieldSpec fields are min_value, max_value, step, fmt, data_type (default float), and pixels_per_step (unused in the current controller implementation).

Panel

import lichtfeld as lf
# lf.ui.Panel is the base class for all panels
AttributeTypeDefaultDescription
idstrmodule.qualnameUnique panel identifier
labelstr""Display name (id fallback when empty)
spacelf.ui.PanelSpacelf.ui.PanelSpace.MAIN_PANEL_TABPanel space (see below)
parentstr""Parent panel id. Embeds as a collapsible section; embedded panels must not override space
orderint100Sort order (lower = higher)
optionsset[lf.ui.PanelOption]set()DEFAULT_CLOSED, HIDE_HEADER
poll_dependenciesset[lf.ui.PollDependency]{SCENE, SELECTION, TRAINING}Which state changes trigger poll()
sizetuple[float, float] | NoneNoneInitial width/height hint, mainly for floating panels
templatestr | os.PathLike[str]""Retained RML template. Use an absolute path for plugin-local files
stylestr""Inline RCSS appended to the retained document
height_modelf.ui.PanelHeightModelf.ui.PanelHeightMode.FILLFILL or CONTENT for retained panels
update_interval_msint100Cadence for retained/hybrid on_update() work
MethodReturnsDescription
poll(cls, context)boolClassmethod. Show/hide condition
draw(self, ui)NoneImmediate-mode content
on_bind_model(self, ctx)NoneBind retained data models before document load
on_mount(self, doc)NoneCalled once after the retained document mounts
on_unmount(self, doc)NoneCalled before the retained document is destroyed
on_update(self, doc)None | boolPeriodic retained update. Return True to mark content dirty
on_scene_changed(self, doc)NoneCalled when the active scene generation changes

Registering a panel with the same id as an existing panel replaces it (see Panel replacement).

lf.ui.Panel is unified: a panel can start as draw(ui) only and later add template, style, height_mode, or retained hooks without switching base classes or rewriting the panel body.

Panel definitions are validated during lf.register_class(). Invalid enum values, removed legacy field names, unsupported retained features on VIEWPORT_OVERLAY, or conflicting embedded-panel fields raise ValueError, TypeError, or AttributeError.

The panel API is strict in v1: use the enum values above, not string literals.

Panel spaces

MAIN_PANEL_TAB, SIDE_PANEL, VIEWPORT_OVERLAY, SCENE_HEADER, FLOATING, STATUS_BAR

Retained shell behavior

If a panel uses retained features and template is empty, LichtFeld selects a shell automatically:

  • FLOATING -> rmlui/floating_window.rml
  • STATUS_BAR -> rmlui/status_bar_panel.rml
  • Other retained panel spaces -> rmlui/docked_panel.rml

Built-in template aliases:

  • builtin:docked-panel
  • builtin:floating-window
  • builtin:status-bar

Panel styling guide

GoalUseNotes
Minimal paneldraw(self, ui)No extra files needed
Light retained stylingstyleInline RCSS text, not a path
Full custom retained UItemplateUse an absolute path for plugin-local .rml
Hybrid paneltemplate plus draw(ui)Render immediate content into <div id="im-root"></div>

When a plugin-local template file such as main_panel.rml is present, LichtFeld automatically loads a sibling main_panel.rcss stylesheet if it exists.


Operator

from lfs_plugins.types import Operator, Event

Operator extends PropertyGroup, so it supports typed properties as class attributes.

AttributeTypeDescription
labelstrDisplay name
descriptionstrTooltip text
optionsSet[str]{'UNDO', 'BLOCKING'}
MethodReturnsDescription
poll(cls, context)boolClassmethod. Can the op run?
invoke(self, context, event)setCalled on trigger, can start modal
execute(self, context)setSynchronous execution
modal(self, context, event)setHandle events in modal mode
cancel(self, context)NoneCalled on cancellation

Return sets

{"FINISHED"}, {"CANCELLED"}, {"RUNNING_MODAL"}, {"PASS_THROUGH"}

Or dict form: {"status": "FINISHED", "key": value, ...}


Event

from lfs_plugins.types import Event
AttributeTypeDescription
typestr'MOUSEMOVE', 'LEFTMOUSE', 'RIGHTMOUSE', 'MIDDLEMOUSE', 'KEY_A'-'KEY_Z', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'ESC', 'RET', 'SPACE'
valuestr'PRESS', 'RELEASE', 'NOTHING'
mouse_xfloatMouse X (viewport coords)
mouse_yfloatMouse Y (viewport coords)
mouse_region_xfloatMouse X relative to region
mouse_region_yfloatMouse Y relative to region
delta_xfloatMouse delta X
delta_yfloatMouse delta Y
scroll_xfloatScroll X offset
scroll_yfloatScroll Y offset
shiftboolShift modifier
ctrlboolCtrl modifier
altboolAlt modifier
pressurefloatTablet pressure (1.0 for mouse)
over_guiboolMouse is over GUI element
key_codeintKey code (see key_codes.hpp)

Properties

from lfs_plugins.props import (
    Property, FloatProperty, IntProperty, BoolProperty,
    StringProperty, EnumProperty, FloatVectorProperty,
    IntVectorProperty, TensorProperty, CollectionProperty,
    PointerProperty, PropertyGroup, PropSubtype,
)

FloatProperty

FloatProperty(
    default: float = 0.0,
    min: float = -inf,
    max: float = inf,
    step: float = 0.1,
    precision: int = 3,
    subtype: str = "",       # FACTOR, PERCENTAGE, ANGLE, TIME, DISTANCE, POWER
    name: str = "",
    description: str = "",
    update: Callable = None,
)

IntProperty

IntProperty(
    default: int = 0,
    min: int = -2**31,
    max: int = 2**31 - 1,
    step: int = 1,
    name: str = "",
    description: str = "",
    update: Callable = None,
)

BoolProperty

BoolProperty(
    default: bool = False,
    name: str = "",
    description: str = "",
    update: Callable = None,
)

StringProperty

StringProperty(
    default: str = "",
    maxlen: int = 0,         # 0 = unlimited
    subtype: str = "",       # FILE_PATH, DIR_PATH, FILE_NAME
    name: str = "",
    description: str = "",
    update: Callable = None,
)

EnumProperty

EnumProperty(
    items: list[tuple[str, str, str]] = [],  # (identifier, label, description)
    default: str = None,     # First item if None
    name: str = "",
    description: str = "",
    update: Callable = None,
)

FloatVectorProperty

FloatVectorProperty(
    default: tuple = (0.0, 0.0, 0.0),
    size: int = 3,
    min: float = -inf,
    max: float = inf,
    subtype: str = "",       # COLOR, COLOR_GAMMA, TRANSLATION, DIRECTION,
                             # VELOCITY, ACCELERATION, XYZ, EULER, QUATERNION
    name: str = "",
    description: str = "",
    update: Callable = None,
)

IntVectorProperty

IntVectorProperty(
    default: tuple = (0, 0, 0),
    size: int = 3,
    min: int = -2**31,
    max: int = 2**31 - 1,
    name: str = "",
    description: str = "",
    update: Callable = None,
)

TensorProperty

TensorProperty(
    shape: tuple = (),       # Use -1 for variable dims, e.g. (-1, 3)
    dtype: str = "float32",
    device: str = "cuda",
    name: str = "",
    description: str = "",
    update: Callable = None,
)

CollectionProperty

CollectionProperty(
    type: Type[PropertyGroup],  # Item type
    name: str = "",
    description: str = "",
)
MethodReturnsDescription
add()PropertyGroupAdd new item
remove(index)NoneRemove by index
clear()NoneRemove all items
move(from, to)NoneReorder items
__len__()intItem count
__getitem__(index)PropertyGroupAccess by index
__iter__()IteratorIterate items

PointerProperty

PointerProperty(
    type: Type[PropertyGroup],  # Referenced type
    name: str = "",
    description: str = "",
)
MethodReturnsDescription
get_instance()PropertyGroupGet or create referenced object

PropertyGroup

from lfs_plugins.props import PropertyGroup
MethodReturnsDescription
get_instance()clsClassmethod. Singleton access
add_property(name, prop)NoneAdd property at runtime
remove_property(name)NoneRemove runtime property
get_all_properties()dict[str, Property]All properties (class + runtime)
get(prop_id)AnyGet property value by name
set(prop_id, value)NoneSet property value by name

PropSubtype constants

from lfs_plugins.props import PropSubtype

PropSubtype.NONE             # ""
PropSubtype.FILE_PATH        # "FILE_PATH"
PropSubtype.DIR_PATH         # "DIR_PATH"
PropSubtype.FILE_NAME        # "FILE_NAME"
PropSubtype.COLOR            # "COLOR"
PropSubtype.COLOR_GAMMA      # "COLOR_GAMMA"
PropSubtype.TRANSLATION      # "TRANSLATION"
PropSubtype.DIRECTION        # "DIRECTION"
PropSubtype.VELOCITY         # "VELOCITY"
PropSubtype.ACCELERATION     # "ACCELERATION"
PropSubtype.XYZ              # "XYZ"
PropSubtype.EULER            # "EULER"
PropSubtype.QUATERNION       # "QUATERNION"
PropSubtype.AXISANGLE        # "AXISANGLE"
PropSubtype.ANGLE            # "ANGLE"
PropSubtype.FACTOR           # "FACTOR"
PropSubtype.PERCENTAGE       # "PERCENTAGE"
PropSubtype.TIME             # "TIME"
PropSubtype.DISTANCE         # "DISTANCE"
PropSubtype.POWER            # "POWER"
PropSubtype.TEMPERATURE      # "TEMPERATURE"
PropSubtype.PIXEL            # "PIXEL"
PropSubtype.UNSIGNED         # "UNSIGNED"
PropSubtype.LAYER            # "LAYER"
PropSubtype.LAYER_MEMBER     # "LAYER_MEMBER"

ToolDef / ToolRegistry

ToolDef

from lfs_plugins.tool_defs.definition import ToolDef, SubmodeDef, PivotModeDef
@dataclass(frozen=True)
class ToolDef:
    id: str                                      # Unique tool ID
    label: str                                   # Display label
    icon: str                                    # Icon name
    group: str = "default"                       # "select", "transform", "paint", "utility"
    order: int = 100                             # Sort order within group
    description: str = ""                        # Tooltip
    shortcut: str = ""                           # Keyboard shortcut
    gizmo: str = ""                              # "translate", "rotate", "scale", ""
    operator: str = ""                           # Operator to invoke on activation
    submodes: tuple[SubmodeDef, ...] = ()
    pivot_modes: tuple[PivotModeDef, ...] = ()
    poll: Callable[[Any], bool] | None = None    # Availability check
    plugin_name: str = ""                        # For custom icon loading
    plugin_path: str = ""                        # For custom icon loading
MethodReturnsDescription
can_activate(context)boolCheck if tool can be activated
to_dict()dictConvert to dict for C++ interop

SubmodeDef

@dataclass(frozen=True)
class SubmodeDef:
    id: str           # Unique submode ID
    label: str        # Display label
    icon: str         # Icon name
    shortcut: str = ""

PivotModeDef

@dataclass(frozen=True)
class PivotModeDef:
    id: str           # Unique pivot mode ID
    label: str        # Display label
    icon: str         # Icon name

ToolRegistry

from lfs_plugins.tools import ToolRegistry
MethodReturnsDescription
register_tool(tool)NoneRegister a custom tool
unregister_tool(tool_id)NoneUnregister by ID
get(tool_id)Optional[ToolDef]Get tool by ID (builtins first)
get_all()list[ToolDef]All tools (builtins + custom, sorted)
set_active(tool_id)boolActivate a tool
get_active()Optional[ToolDef]Get active tool
get_active_id()strGet active tool ID

Signals

from lfs_plugins.ui.signals import Signal, ComputedSignal, ThrottledSignal, Batch, batch

Signal[T]

Signal(initial_value: T, name: str = "")
Property/MethodReturnsDescription
.valueTGet/set current value
.peek()TGet without tracking
.subscribe(callback)() -> NoneSubscribe; returns unsubscribe fn
.subscribe_as(owner, callback)() -> NoneOwner-tracked subscription

ComputedSignal[T]

ComputedSignal(compute: Callable[[], T], dependencies: list[Signal])
Property/MethodReturnsDescription
.valueTGet computed value (lazy)
.subscribe(callback)() -> NoneSubscribe to changes
.subscribe_as(owner, callback)() -> NoneOwner-tracked subscription

ThrottledSignal[T]

ThrottledSignal(initial_value: T, max_rate_hz: float = 60.0, name: str = "")
Property/MethodReturnsDescription
.valueTGet/set current value
.flush()NoneForce pending notification
.subscribe(callback)() -> NoneSubscribe to changes
.subscribe_as(owner, callback)() -> NoneOwner-tracked subscription

Batch / batch()

with Batch():       # Class form
    ...

with batch():       # Function form
    ...

Defers all signal notifications until the block exits.

SubscriptionRegistry

from lfs_plugins.ui.subscription_registry import SubscriptionRegistry

registry = SubscriptionRegistry.instance()
unsub = registry.register(owner="my_plugin", unsubscribe_fn=fn)
registry.unregister_all("my_plugin")   # Cleanup on unload

Capabilities

from lfs_plugins.capabilities import CapabilityRegistry, CapabilitySchema, Capability
from lfs_plugins.context import PluginContext, SceneContext, ViewContext, CapabilityBroker

CapabilityRegistry

registry = CapabilityRegistry.instance()
MethodReturnsDescription
register(name, handler, ...)NoneRegister a capability
unregister(name)boolUnregister by name
unregister_all_for_plugin(plugin_name)intUnregister all for plugin
invoke(name, args)dictInvoke capability
get(name)Optional[Capability]Get by name
list_all()list[Capability]List all capabilities
has(name)boolCheck existence

register() parameters

registry.register(
    name: str,                    # Unique name, e.g. "my_plugin.feature"
    handler: Callable,            # fn(args: dict, ctx: PluginContext) -> dict
    description: str = "",
    schema: CapabilitySchema = None,
    plugin_name: str = None,
    requires_gui: bool = True,
)

CapabilitySchema

@dataclass
class CapabilitySchema:
    properties: dict[str, dict[str, Any]]   # JSON Schema-like property defs
    required: list[str]                      # Required property names

Capability

@dataclass
class Capability:
    name: str
    description: str
    handler: Callable
    schema: CapabilitySchema
    plugin_name: Optional[str]
    requires_gui: bool

PluginContext

@dataclass
class PluginContext:
    scene: Optional[SceneContext]
    view: Optional[ViewContext]
    capabilities: CapabilityBroker
MethodReturnsDescription
build(registry, include_view=True)PluginContextClassmethod. Build from state

SceneContext

@dataclass
class SceneContext:
    scene: Any                          # PyScene object
MethodReturnsDescription
set_selection_mask(mask)NoneApply selection mask

ViewContext

@dataclass
class ViewContext:
    image: Any                          # [H, W, 3] tensor
    screen_positions: Optional[Any]     # [N, 2] tensor or None
    width: int
    height: int
    fov: float
    rotation: Any                       # [3, 3] tensor
    translation: Any                    # [3] tensor

CapabilityBroker

class CapabilityBroker:
    def invoke(self, name: str, args: dict = None) -> dict
    def has(self, name: str) -> bool
    def list_all(self) -> list[str]

PluginManager

from lfs_plugins.manager import PluginManager
mgr = PluginManager.instance()
MethodReturnsDescription
plugins_dirPathProperty. ~/.lichtfeld/plugins/
discover()list[PluginInfo]Scan for plugins
load(name, on_progress=None)boolLoad a plugin
unload(name)boolUnload a plugin
reload(name)boolHot-reload a plugin
load_all()dict[str, bool]Load all user-enabled plugins
install(url, on_progress=None, auto_load=True)strInstall from Git URL
uninstall(name)boolRemove a plugin
update(name, on_progress=None)boolUpdate a plugin
search(query, compatible_only=True)list[RegistryPluginInfo]Search registry
check_updates()dict[str, tuple]Check installed plugin updates
get_state(name)Optional[PluginState]Get plugin state
get_error(name)Optional[str]Get error message
get_traceback(name)Optional[str]Get error traceback

PluginInfo

@dataclass
class PluginInfo:
    name: str
    version: str
    path: Path
    description: str = ""
    author: str = ""
    entry_point: str = "__init__"
    dependencies: list[str] = []
    auto_start: bool = False
    hot_reload: bool = True
    plugin_api: str = ""
    lichtfeld_version: str = ""
    required_features: list[str] = []

PluginState

class PluginState(Enum):
    UNLOADED = "unloaded"
    INSTALLING = "installing"
    LOADING = "loading"
    ACTIVE = "active"
    ERROR = "error"
    DISABLED = "disabled"

lichtfeld.plugins convenience API

import lichtfeld as lf
FunctionReturnsDescription
lf.plugins.discover()list[PluginInfo]Discover plugins in ~/.lichtfeld/plugins/
lf.plugins.load(name)boolLoad a plugin
lf.plugins.unload(name)boolUnload a plugin
lf.plugins.reload(name)boolReload a plugin
lf.plugins.load_all()dict[str, bool]Load all user-enabled plugins
lf.plugins.start_watcher()NoneStart the hot-reload watcher
lf.plugins.stop_watcher()NoneStop the hot-reload watcher
lf.plugins.get_state(name)PluginState | NoneRead plugin state
lf.plugins.get_error(name)str | NoneRead the last plugin error
lf.plugins.get_traceback(name)str | NoneRead the full traceback
lf.plugins.create(name)strCreate the v1 source scaffold in ~/.lichtfeld/plugins/<name>
lf.plugins.settings(plugin_name)PluginSettingsPersistent per-plugin settings object
lf.plugins.register_capability(name, handler, ...)NoneRegister a capability (see parameters below)
lf.plugins.unregister_capability(name)boolUnregister a capability by name
lf.plugins.invoke(name, args)objectInvoke a registered capability
lf.plugins.has_capability(name)boolCheck if a capability exists
lf.plugins.list_capabilities()listList all registered capabilities

lf.plugins.settings() returns a persistent key-value store scoped to the given plugin. Values are saved across sessions.

lf.plugins.register_capability() accepts the same parameters as CapabilityRegistry.register(): name, handler, description, schema, plugin_name, and requires_gui. This is the preferred approach for most plugins; use CapabilityRegistry.instance() only for advanced use cases.

lf.plugins.create() writes the source package, including panels/main_panel.py, panels/main_panel.rml, and panels/main_panel.rcss. If you want a scaffold that also adds .venv, .vscode, and pyrightconfig.json, use the CLI command LichtFeld-Studio plugin create <name>.

Runtime compatibility constants:

ConstantTypeDescription
lf.PLUGIN_API_VERSIONstrHost plugin API version
lf.plugins.API_VERSIONstrSame plugin API version through the plugin namespace
lf.plugins.FEATURESlist[str]Supported optional plugin features on this host

Layout API

The ui object passed to Panel.draw() provides the immediate widget API used by both simple and hybrid panels. Depending on the panel space and shell, it may be rendered through the direct viewport path or the immediate-mode RML bridge, but the Python widget surface stays the same.

Text

MethodReturnsDescription
label(text)NonePlain text
label_centered(text)NoneCentered text
heading(text)NoneLarge heading
text_colored(text, color)NoneColored text (RGBA tuple)
text_colored_centered(text, color)NoneCentered colored text
text_selectable(text, height=0)NoneSelectable text
text_wrapped(text)NoneWord-wrapped text
text_disabled(text)NoneGrayed-out text
bullet_text(text)NoneBulleted text

Buttons

MethodReturnsDescription
button(label, size=(0,0))boolStandard button
button_styled(label, style, size=(0,0))boolStyled: “success”, “error”, “warning”, “primary”, “secondary”
button_callback(label, callback=None, size=(0,0))boolButton with callback
small_button(label)boolCompact button
invisible_button(id, size)boolInvisible clickable area

Input

MethodReturnsDescription
checkbox(label, value)(bool, bool)(changed, new_value)
radio_button(label, current, value)(bool, int)Radio button
input_text(label, value)(bool, str)Text input
input_text_with_hint(label, hint, value)(bool, str)Text with placeholder
input_text_enter(label, value)(bool, str)Confirm on Enter
input_float(label, value, step=0, step_fast=0, format='%.3f')(bool, float)Float input
input_int(label, value, step=1, step_fast=100)(bool, int)Integer input
input_int_formatted(label, value, step=0, step_fast=0)(bool, int)Formatted int input

Sliders & Drags

MethodReturnsDescription
slider_float(label, value, min, max)(bool, float)Float slider
slider_int(label, value, min, max)(bool, int)Integer slider
slider_float2(label, value, min, max)(bool, tuple)2-component slider
slider_float3(label, value, min, max)(bool, tuple)3-component slider
drag_float(label, value, speed=1, min=0, max=0)(bool, float)Float drag
drag_int(label, value, speed=1, min=0, max=0)(bool, int)Integer drag
stepper_float(label, value, steps=[1.0, 0.1, 0.01])(bool, float)Float stepper with configurable step sizes

Selection

MethodReturnsDescription
combo(label, current_idx, items)(bool, int)Dropdown selector
listbox(label, current_idx, items, height_items=-1)(bool, int)List selector
selectable(label, selected=False, height=0)boolSelectable item
prop_search(data, prop_id, search_data, search_prop, text='')(bool, int)Searchable dropdown

Color

MethodReturnsDescription
color_edit3(label, color)(bool, tuple)RGB color picker
color_edit4(label, color)(bool, tuple)RGBA color picker
color_picker3(label, color)(bool, tuple[float, float, float])Inline RGB color picker
color_button(label, color, size=(0,0))boolColor swatch button

File/Path

MethodReturnsDescription
path_input(label, value, folder_mode=True, dialog_title='')(bool, str)File/folder picker

Property Binding

MethodReturnsDescription
prop(data, prop_id, text=None)(bool, Any)Auto-widget based on property type

Layout Structure

MethodReturnsDescription
separator()NoneHorizontal line
spacing()NoneVertical space
same_line(offset=0, spacing=-1)NoneNext widget on same line
new_line()NoneForce new line
indent(width=0)NoneIncrease indent
unindent(width=0)NoneDecrease indent
begin_group() / end_group()NoneLogical widget group
set_next_item_width(width)NoneWidth for next widget
dummy(size)NoneEmpty space placeholder

Collapsible / Tree

MethodReturnsDescription
collapsing_header(label, default_open=False)boolCollapsible section
tree_node(label)boolTree node (call tree_pop())
tree_node_ex(label, flags='')boolExtended tree node
tree_pop()NoneClose tree node

Tables

MethodReturnsDescription
begin_table(id, columns)boolStart table
table_setup_column(label, width=0)NoneDefine column
table_headers_row()NoneDraw header row
table_next_row()NoneNext row
table_next_column()NoneNext column
table_set_column_index(column)boolJump to column
table_set_bg_color(target, color)NoneSet row/cell background
end_table()NoneEnd table

Images

MethodReturnsDescription
image(texture_id, size, tint=(1,1,1,1))NoneDisplay image
image_uv(texture_id, size, uv0, uv1, tint=(1,1,1,1))NoneImage with UV coords
image_button(id, texture_id, size, tint=(1,1,1,1))boolClickable image
toolbar_button(id, tex, size, selected=F, disabled=F, tooltip='')boolToolbar icon button
image_tensor(label, tensor, size, tint=None)NoneDisplay a tensor as an image (cached by label)
image_texture(texture, size, tint=None)NoneDisplay a DynamicTexture

image_tensor is the simplest way to display a GPU tensor — it internally manages a DynamicTexture cached by label. The tensor must be [H, W, 3] or [H, W, 4] (RGB/RGBA). CPU tensors and non-float32 dtypes are converted automatically.

ui.image_tensor("preview", my_tensor, (256, 256))

For full control (e.g. reusing one texture across multiple draw calls), use DynamicTexture directly:

tex = lf.ui.DynamicTexture(tensor)   # or DynamicTexture() + tex.update(tensor)
ui.image_texture(tex, (256, 256))

DynamicTexture

GPU tensor to OpenGL texture bridge via CUDA-GL interop.

tex = lf.ui.DynamicTexture()          # Empty
tex = lf.ui.DynamicTexture(tensor)    # From tensor
Method / PropertyReturnsDescription
update(tensor)NoneUpload [H, W, 3|4] tensor (auto-converts CPU→CUDA, uint8→float32)
destroy()NoneRelease GL resources
idintOpenGL texture ID
widthintCurrent width in pixels
heightintCurrent height in pixels
validboolTrue if texture is initialized
uv1tuple[float, float]UV scale factors for power-of-2 padding

Calling update() with a different resolution automatically recreates the GL texture. Textures are freed on plugin unload via lf.ui.free_plugin_textures(name).

Drag & Drop

MethodReturnsDescription
begin_drag_drop_source()boolStart drag source
set_drag_drop_payload(type, data)NoneSet drag payload
end_drag_drop_source()NoneEnd drag source
begin_drag_drop_target()boolStart drag target
accept_drag_drop_payload(type)str or NoneAccept payload
end_drag_drop_target()NoneEnd drag target

Popups & Menus

MethodReturnsDescription
begin_popup(id)boolStart popup
begin_context_menu(id='')boolStyled context menu
begin_popup_modal(title)boolModal popup
open_popup(id)NoneTrigger popup open
end_popup() / end_popup_modal()NoneEnd popup/modal
end_context_menu()NoneEnd context menu
close_current_popup()NoneClose current popup
begin_menu(label)boolStart menu
end_menu()NoneEnd menu
begin_menu_bar() / end_menu_bar()boolMenu bar
menu_item(label, enabled=True)boolMenu item
menu_item_toggle(label, shortcut, selected)boolToggle menu item
menu_item_shortcut(label, shortcut, enabled=True)boolMenu item with shortcut
menu(menu_id, text='', icon='')NoneInline menu reference
popover(panel_id, text='', icon='')NonePanel popover

Windows & Children

MethodReturnsDescription
begin_window(title, flags=0)boolStart window
begin_window_closable(title, flags=0)(bool, bool)Closable window
end_window()NoneEnd window
begin_child(id, size=(0,0), border=False)boolStart child region
end_child()NoneEnd child region
set_next_window_pos(pos, first_use=False)NoneSet window position
set_next_window_size(size, first_use=False)NoneSet window size
set_next_window_pos_center()NoneCenter window
set_next_window_pos_centered(first_use=False)NoneCenter next window (main viewport)
set_next_window_pos_viewport_center()NoneViewport center
set_next_window_focus()NoneFocus next window
set_next_window_bg_alpha(alpha)NoneSet next window BG alpha
push_window_style() / pop_window_style()NoneWindow style stack
push_modal_style() / pop_modal_style()NoneModal style stack

Drawing (Viewport)

MethodReturnsDescription
draw_line(x0, y0, x1, y1, color, thickness=1)NoneLine
draw_rect(x0, y0, x1, y1, color, thickness=1)NoneRectangle outline
draw_rect_filled(x0, y0, x1, y1, color, bg=False)NoneFilled rectangle
draw_rect_rounded(x0, y0, x1, y1, color, r, thick=1, bg=F)NoneRounded rect outline
draw_rect_rounded_filled(x0, y0, x1, y1, color, r, bg=F)NoneFilled rounded rect
draw_circle(x, y, radius, color, segments=32, thickness=1)NoneCircle outline
draw_circle_filled(x, y, radius, color, segments=32)NoneFilled circle
draw_triangle_filled(x0, y0, x1, y1, x2, y2, color, bg=F)NoneFilled triangle
draw_text(x, y, text, color, bg=False)NoneText at position
draw_polyline(points, color, closed=False, thickness=1)NonePolyline
draw_poly_filled(points, color)NoneFilled polygon
plot_lines(label, values, scale_min, scale_max, size)NoneLine plot

Drawing (Window)

MethodReturnsDescription
draw_window_rect_filled(x0, y0, x1, y1, color)NoneFilled rect to window
draw_window_rect(x0, y0, x1, y1, color, thickness=1)NoneRect outline to window
draw_window_line(x0, y0, x1, y1, color, thickness=1)NoneLine to window
draw_window_text(x, y, text, color)NoneText to window
draw_window_triangle_filled(x0, y0, x1, y1, x2, y2, color)NoneTriangle to window

Progress & Status

MethodReturnsDescription
progress_bar(fraction, overlay='', width=0)NoneProgress bar
set_tooltip(text)NoneTooltip for last item

State Queries

MethodReturnsDescription
is_item_hovered()boolLast item hovered
is_item_clicked(button=0)boolLast item clicked
is_item_active()boolLast item active
is_window_focused()boolWindow has focus
is_window_hovered()boolWindow is hovered
is_mouse_double_clicked(button=0)boolDouble click detected
is_mouse_dragging(button=0)boolMouse dragging
get_mouse_wheel()floatScroll wheel delta
get_mouse_delta()tupleMouse delta (dx, dy)

Position / Size

MethodReturnsDescription
get_cursor_pos()tupleCursor position
get_cursor_screen_pos()tupleCursor screen position
get_window_pos()tupleWindow position
get_window_width()floatWindow width
get_text_line_height()floatText line height
get_content_region_avail()tupleAvailable content area
get_viewport_pos()tupleViewport position
get_viewport_size()tupleViewport size
get_dpi_scale()floatDPI scale factor
calc_text_size(text)tupleText dimensions

Styling

MethodReturnsDescription
push_style_var(var, value)NonePush float style var
push_style_var_vec2(var, value)NonePush vec2 style var
pop_style_var(count=1)NonePop style vars
push_style_color(col, color)NonePush color override
pop_style_color(count=1)NonePop color overrides
push_item_width(width) / pop_item_width()NoneItem width stack
begin_disabled(disabled=True) / end_disabled()NoneDisable widget region. For composable disabled regions, prefer SubLayout.enabled (see Layout Composition below).

Keyboard / Mouse Capture

MethodReturnsDescription
set_keyboard_focus_here()NoneFocus next widget
capture_keyboard_from_app(capture=True)NoneCapture keyboard input
capture_mouse_from_app(capture=True)NoneCapture mouse input
set_mouse_cursor_hand()NoneSet hand cursor

Cursor Control

MethodReturnsDescription
set_cursor_pos(pos)NoneSet cursor position
set_cursor_pos_x(x)NoneSet cursor X
set_scroll_here_y(ratio=0.5)NoneScroll to current Y

Specialized Widgets

MethodReturnsDescription
crf_curve_preview(label, gamma, toe, shoulder, gamma_r=0, gamma_g=0, gamma_b=0)NoneTone curve preview
chromaticity_diagram(label, rx, ry, gx, gy, bx, by, nx, ny, range=0.5)(bool, list)Chromaticity diagram
template_list(list_type_id, list_id, data, prop_id, active_data, active_prop, rows=5)(int, int)Custom list template

Layout Composition

Create composable sub-layouts with automatic widget positioning and state cascading.

MethodReturnsDescription
row()SubLayoutHorizontal layout
column()SubLayoutVertical layout
split(factor=0.5)SubLayoutTwo-column split
box()SubLayoutBordered container
grid_flow(columns=0, even_columns=True, even_rows=True)SubLayoutResponsive grid
prop_enum(data, prop_id, value, text='')boolEnum toggle button

SubLayout is a context manager. Use with ui.row() as row: to enter the layout, then call widget methods on row instead of ui. Sub-layouts nest arbitrarily.

SubLayout state properties

PropertyTypeDescription
enabledboolDisabled state (cascades to children)
activeboolActive state (cascades to children)
alertboolOne-shot alert styling (red text/bg)

Example

def draw(self, ui):
    with ui.row() as row:
        row.prop_enum(self, "mode", "fast", "Fast")
        row.prop_enum(self, "mode", "quality", "Quality")

    with ui.box() as box:
        box.heading("Settings")
        box.prop(self, "opacity")

    with ui.column() as col:
        col.enabled = self.is_active
        col.prop(self, "value")
        with col.row() as row:
            row.button("Apply")
            row.button("Cancel")

    with ui.grid_flow(columns=3) as grid:
        for item in items:
            with grid.box() as cell:
                cell.label(item.name)
                cell.button("Select")

Scene API (lf module)

import lichtfeld as lf

Scene Management

FunctionReturnsDescription
get_scene()Scene or NoneGet scene object
get_render_scene()Scene or NoneGet render scene (PyScene)
has_scene()boolWhether scene is loaded
clear_scene()NoneClear all scene content
load_file(path, is_dataset=False)NoneLoad PLY or dataset
load_config_file(path)NoneLoad JSON config
get_scene_generation()intScene generation counter
list_scene()NonePrint scene tree

Node Operations (on Scene object)

MethodReturnsDescription
add_group(name, parent=-1)intAdd group node
add_splat(name, means, sh0, shN, scaling, rotation, opacity, ...)intAdd splat node
add_point_cloud(name, points, colors, parent=-1)intAdd point cloud
add_mesh(name, vertices, indices, colors=None, normals=None, parent=-1)intAdd mesh node
add_camera_group(name, parent, camera_count)intAdd camera group node
add_camera(name, parent, R, T, fx, fy, w, h, ...)intAdd camera node
remove_node(name, keep_children=False)NoneRemove node
rename_node(old, new)boolRename node
reparent(node_id, new_parent_id)NoneChange parent
duplicate_node(name)strDuplicate, returns new name
merge_group(group_name)strMerge group children
get_node(name)SceneNodeGet node by name
get_node_by_id(id)SceneNodeGet node by ID
get_nodes()list[SceneNode]All nodes
get_visible_nodes()list[SceneNode]Visible nodes only
root_nodes()list[int]Root node IDs
is_node_effectively_visible(id)boolConsiders parent visibility
total_gaussian_countintProperty. Total gaussians
invalidate_cache()NoneClear internal cache (no redraw)
notify_changed()NoneInvalidate cache + trigger viewport redraw

SceneNode Properties

Property/MethodReturnsDescription
idintNode ID
namestrNode name
typeNodeTypeNode type enum (SPLAT, POINTCLOUD, GROUP, etc.)
parent_idintParent node ID (-1 for root)
childrenlist[int]Child node IDs
visibleboolVisibility flag
lockedboolLock flag
gaussian_countintNumber of gaussians (splat nodes)
centroidtuple[float, float, float]Node centroid
world_transformtupleWorld-space 4x4 transform
splat_data()SplatData or NoneSplat data for this node (None if not a splat)
point_cloud()PointCloud or NonePoint cloud data (None if not a point cloud)
cropbox()CropBox or NoneCrop box data
ellipsoid()Ellipsoid or NoneEllipsoid data

Selection

FunctionReturnsDescription
select_node(name)NoneSelect node by name
select_nodes(names)NoneSelect multiple nodes by name
add_to_selection(name)NoneAdd a node to the current selection
deselect_all()NoneClear selection
has_selection()boolAny selection active
get_selected_node_name()strFirst selected node name
get_selected_node_names()list[str]All selected node names
can_transform_selection()boolSelection is transformable
get_selected_node_transform()list[float]16 floats, column-major
set_selected_node_transform(matrix)NoneSet transform
get_selection_center()list[float]Local space center
get_selection_world_center()list[float]World space center
capture_selection_transforms()dictSnapshot for undo

Scene Shortcuts

Module-level shortcuts for common scene operations (equivalent to Scene object methods):

FunctionReturnsDescription
set_node_visibility(name, visible)NoneToggle node visibility
remove_node(name, keep_children=False)NoneRemove node
reparent_node(name, new_parent)NoneReparent node
rename_node(old_name, new_name)NoneRename node
add_group(name, parent="")NoneAdd group node
get_num_gaussians()intTotal gaussian count

Gaussian-Level Selection (on Scene object)

MethodReturnsDescription
set_selection_mask(mask)NoneApply bool tensor mask
clear_selection()NoneClear gaussian selection
has_selection()boolAny gaussians selected
selection_maskTensorProperty. Current mask
set_selection(indices)NoneSelect by index list

Transforms

FunctionReturnsDescription
get_node_transform(name)list[float]16 floats, column-major
set_node_transform(name, matrix)NoneSet 4x4 transform
decompose_transform(matrix)dictSee keys below
compose_transform(translation, euler_deg, scale)list[float]Build 4x4 from components (Euler in degrees)

decompose_transform returns a dict with these keys:

KeyTypeDescription
translation[x, y, z]Position
rotation_quat[x, y, z, w]Quaternion
rotation_euler[rx, ry, rz]Euler angles (radians)
rotation_euler_deg[rx, ry, rz]Euler angles (degrees)
scale[sx, sy, sz]Scale

Splat Data (combined_model() / node.splat_data())

Accessible via scene.combined_model() (all nodes merged) or node.splat_data() (per-node).

Property/MethodReturnsDescription
means_rawTensor[N, 3] positions (view)
sh0_rawTensor[N, 1, 3] base SH (view)
shN_rawTensor[N, K, 3] higher SH (view)
scaling_rawTensor[N, 3] log-space (view)
rotation_rawTensor[N, 4] quaternions (view)
opacity_rawTensor[N, 1] logit-space (view)
get_means()TensorPositions
get_opacity()Tensor[N] sigmoid applied
get_scaling()TensorExp applied
get_rotation()TensorNormalized quaternions
get_shs()TensorSH0 + SHN concatenated
num_pointsintGaussian count
active_sh_degreeintCurrent SH degree
max_sh_degreeintMaximum SH degree
scene_scalefloatScene scale factor
soft_delete(mask)TensorMark for deletion, returns prev state
undelete(mask)NoneRestore deleted gaussians
apply_deleted()intPermanently remove, returns count
clear_deleted()NoneClear deletion mask
deletedTensorProperty. [N] bool deletion mask
has_deleted_mask()boolWhether deletion mask exists
visible_count()intNumber of non-deleted gaussians

After calling soft_delete(), undelete(), or clear_deleted(), call scene.notify_changed() to update the viewport.

Selection Groups (on Scene object)

MethodReturnsDescription
add_selection_group(name, color)intCreate a named selection group with an RGBA color tuple
remove_selection_group(id)NoneRemove a selection group by ID
rename_selection_group(id, name)NoneRename a selection group
set_selection_group_color(id, color)NoneChange group color
set_selection_group_locked(id, locked)NoneLock/unlock a group
is_selection_group_locked(id)boolCheck lock state
active_selection_groupintProperty (get/set). Active group ID
selection_groups()list[SelectionGroup]All selection groups

Training Control

FunctionReturnsDescription
prepare_training_from_scene()NoneInitialize trainer from scene cameras and point cloud
start_training()NoneStart training
pause_training()NonePause
resume_training()NoneResume
stop_training()NoneStop
reset_training()NoneReset to iteration 0
save_checkpoint()NoneSave checkpoint
switch_to_edit_mode()NoneEnter edit mode
has_trainer()boolTrainer loaded
trainer_state()strState string
finish_reason()str or NoneWhy training ended
trainer_error()str or NoneError message
context()ContextTraining context snapshot
optimization_params()OptimizationParamsTraining parameters
dataset_params()DatasetParamsDataset parameters
loss_buffer()list[float]Loss history
load_checkpoint_for_training(checkpoint_path, dataset_path, output_path)NoneLoad checkpoint for training

Training Status

FunctionReturnsDescription
trainer_elapsed_seconds()floatElapsed training time
trainer_eta_seconds()floatEstimated remaining time (-1 if unavailable)
trainer_strategy_type()strStrategy type (mcmc, default, etc.)
trainer_is_gut_enabled()boolGUT enabled
trainer_max_gaussians()intMax gaussians
trainer_num_splats()intCurrent splat count
trainer_current_iteration()intCurrent iteration
trainer_total_iterations()intTotal iterations
trainer_current_loss()floatCurrent loss

Training Hooks

DecoratorDescription
@lf.on_training_startCalled when training starts
@lf.on_iteration_startCalled at start of each iteration
@lf.on_pre_optimizer_stepCalled before optimizer step
@lf.on_post_stepCalled after each step
@lf.on_training_endCalled when training ends

Rendering

FunctionReturnsDescription
get_current_view()ViewInfoCurrent camera view
get_viewport_render()ViewportRenderCurrent viewport image
capture_viewport()ViewportRenderCapture for async use
render_view(rotation, translation, w, h, fov=60, bg=None)TensorRender from camera
compute_screen_positions(rotation, translation, w, h, fov=60)Tensor[N, 2] screen positions
get_render_settings()RenderSettingsCurrent render settings
get_render_mode() / set_render_mode(mode)RenderModeRender mode

Viewport Control

FunctionReturnsDescription
reset_camera()NoneReset camera
toggle_fullscreen()NoneToggle fullscreen
is_fullscreen()boolFullscreen state
toggle_ui()NoneToggle UI visibility
set_orthographic(ortho)NoneSet projection mode
is_orthographic()boolOrthographic state

Export

lf.export_scene(
    format: int,             # 0=PLY, 1=SOG, 2=SPZ, 3=HTML
    path: str,
    node_names: list[str],
    sh_degree: int,
)
lf.save_config_file(path: str)

Logging

FunctionDescription
lf.log.info(msg)Info level
lf.log.warn(msg)Warning level
lf.log.error(msg)Error level
lf.log.debug(msg)Debug level

Undo

lf.undo.push(
    name: str,
    undo: object,
    redo: object,
    validate: bool = False,
    id: str = '',
    source: str = 'python',
    scope: str = 'custom',
    estimated_bytes: int = 0,
    dirty_flags: int = 0,
    merge_window_ms: int = 0,
)
lf.undo.transaction(name: str = "Grouped Changes") -> Transaction
lf.undo.stack() -> dict
FunctionReturnsDescription
push(name, undo, redo, ...)NonePush an undo step
undo()NoneUndo last step
redo()NoneRedo last undone step
can_undo()boolWhether undo is available
can_redo()boolWhether redo is available
clear()NoneClear undo/redo history
get_undo_name()strName of the next undo step
get_redo_name()strName of the next redo step
undo_names()list[str]All undo step names
redo_names()list[str]All redo step names
undo_bytes()intMemory used by undo stack
redo_bytes()intMemory used by redo stack
total_bytes()intTotal memory used
total_cpu_bytes()intCPU memory used
total_gpu_bytes()intGPU memory used
max_bytes()intMemory budget
set_max_bytes(n)NoneSet memory budget
has_active_transaction()boolWhether a transaction is open
transaction_depth()intNesting depth of transactions
transaction_age_ms()intAge of the active transaction
active_transaction_name()strName of the active transaction
generation()intChange generation counter
subscribe(callback)NoneObserve undo/redo changes
unsubscribe(callback)NoneRemove observer
shrink_to_fit()NoneRelease excess memory
jump(stack, count)NoneNavigate history by count
transaction(name)TransactionContext manager for grouped changes
stack()dictStructured undo/redo items

Transaction supports with and exposes add(undo, redo) to append additional undo/redo pairs within the group.

  • lf.undo.transaction(...) groups multiple undoable mutations into one history step.
  • lf.undo.stack() returns structured undo/redo items with id, label, source, scope, and estimated_bytes.

UI Functions

FunctionReturnsDescription
lf.ui.tr(key)strTranslate string
lf.ui.theme()ThemeCurrent theme
lf.ui.context()AppContextApp context
lf.ui.request_redraw()NoneRequest UI redraw
lf.ui.set_language(lang_code)NoneSet UI language
lf.ui.get_current_language()strActive language code
lf.ui.get_languages()list[tuple[str, str]]Available languages
lf.ui.set_theme(name)NoneTheme switch (dark/light)
lf.ui.get_theme()strActive theme name
lf.ui.set_panel_enabled(panel_id, enabled)NoneToggle panel by id
lf.ui.is_panel_enabled(panel_id)boolPanel enabled state
lf.ui.get_panel_names(space=lf.ui.PanelSpace.FLOATING)list[str]Panel ids for a space
lf.ui.get_panel(panel_id)lf.ui.PanelInfo | NoneTyped panel info
lf.ui.get_main_panel_tabs()list[lf.ui.PanelSummary]Typed summaries for main-panel tabs
lf.ui.set_panel_label(panel_id, label)boolChange panel display name
lf.ui.set_panel_order(panel_id, order)boolChange panel sort order
lf.ui.set_panel_space(panel_id, space)boolMove panel to a different space (lf.ui.PanelSpace)
lf.ui.set_panel_parent(panel_id, parent)boolEmbed panel inside a tab as collapsible section
lf.ui.ops.invoke(op_id, **kwargs)OperatorReturnValueInvoke operator
lf.ui.ops.poll(op_id)boolOperator poll
lf.ui.ops.cancel_modal()NoneCancel modal operator
lf.ui.get_active_tool()strActive tool ID
lf.ui.get_active_submode()strActive submode
lf.ui.set_selection_mode(mode)NoneSet selection submode
lf.ui.get_transform_space()intTransform space enum index
lf.ui.set_transform_space(space)NoneSet transform space index
lf.ui.get_pivot_mode() / set_pivot_mode(mode)intPivot mode enum index
lf.ui.get_fps()floatCurrent FPS
lf.ui.get_git_commit()strGit commit hash

Note: GPU memory info is available via the Python utility from lfs_plugins.utils import get_gpu_memory, which returns current GPU memory usage in bytes as int.

File Dialogs

FunctionReturns
lf.ui.open_image_dialog(start_dir='')str
lf.ui.open_folder_dialog(title='Select Folder', start_dir='')str
lf.ui.open_dataset_folder_dialog()str
lf.ui.open_ply_file_dialog(start_dir='')str
lf.ui.open_mesh_file_dialog(start_dir='')str
lf.ui.open_checkpoint_file_dialog()str
lf.ui.open_json_file_dialog()str
lf.ui.open_video_file_dialog()str
lf.ui.save_json_file_dialog(default_name='config.json')str
lf.ui.save_ply_file_dialog(default_name='export')str
lf.ui.save_sog_file_dialog(default_name='export')str
lf.ui.save_spz_file_dialog(default_name='export')str
lf.ui.save_html_file_dialog(default_name='viewer')str

UI Hooks

Inject UI into existing panels at predefined hook points. Callbacks receive a layout object.

FunctionDescription
lf.ui.add_hook(panel, section, callback, position="append")Register a hook. position: "prepend" or "append"
lf.ui.remove_hook(panel, section, callback)Remove a specific hook callback
lf.ui.clear_hooks(panel, section="")Clear hooks for panel/section (or all sections if empty)
lf.ui.clear_all_hooks()Clear all registered hooks
lf.ui.get_hook_points()List all registered hook point keys
lf.ui.invoke_hooks(panel, section, prepend=False)Invoke hooks (prepend=True for prepend, False for append)
@lf.ui.hook(panel, section, position="append")Decorator form of add_hook

Hook points are runtime-defined. Query them with lf.ui.get_hook_points() instead of hard-coding.

Tensor API

import lichtfeld as lf
t = lf.Tensor

The tables below list the most-used tensor APIs. For the full bound surface, see src/python/stubs/lichtfeld/__init__.pyi.

Creation:

FunctionReturnsDescription
t.zeros(shape, device='cuda', dtype='float32')TensorZero-filled tensor
t.ones(shape, device, dtype)TensorOnes tensor
t.full(shape, value, device, dtype)TensorConstant-filled tensor
t.eye(n, device, dtype)TensorIdentity matrix
t.arange(start, end, step, device, dtype)TensorRange tensor
t.linspace(start, end, steps, device, dtype)TensorLinear space
t.rand(shape, device, dtype)TensorUniform random [0, 1)
t.randn(shape, device, dtype)TensorNormal random
t.empty(shape, device, dtype)TensorUninitialized tensor
t.randint(low, high, shape, device)TensorRandom integers
t.from_numpy(arr, copy=True)TensorFrom NumPy array
t.cat(tensors, dim=0)TensorConcatenate
t.stack(tensors, dim=0)TensorStack
t.where(condition, x, y)TensorConditional select

Properties:

PropertyTypeDescription
.shapetupleTensor dimensions
.ndimintNumber of dimensions
.numelintTotal elements
.devicestr'cpu' or 'cuda'
.dtypestrData type string
.is_contiguousboolMemory contiguous
.is_cudaboolOn GPU

Methods:

MethodReturnsDescription
.clone()TensorDeep copy
.cpu() / .cuda()TensorMove device
.contiguous()TensorMake contiguous
.sync()NoneCUDA synchronize
.numpy(copy=True)ndarrayConvert to NumPy
.to(dtype)TensorConvert dtype
.size(dim)intSize at dimension
.item()scalarExtract scalar
.sum(dim=None, keepdim=False)TensorReduce sum
.mean(dim=None, keepdim=False)TensorReduce mean
.max(dim=None, keepdim=False)TensorReduce max
.min(dim=None, keepdim=False)TensorReduce min
.reshape(shape)TensorReshape
.view(shape)TensorView reshape
.squeeze(dim=None)TensorRemove size-1 dims
.unsqueeze(dim)TensorAdd size-1 dim
.transpose(dim0, dim1)TensorSwap dimensions
.permute(dims)TensorReorder dimensions
.flatten(start=0, end=-1)TensorFlatten range
.expand(sizes)TensorBroadcast view
.repeat(repeats)TensorTile tensor
.prod(), .std(), .var()TensorAdditional reductions
.argmax(), .argmin()TensorIndex reductions
.all(), .any()TensorLogical reductions
.matmul(), .mm(), .bmm()TensorMatrix products
.masked_select(), .masked_fill()TensorMasked operations
.zeros_like(), .ones_like() etc.TensorLike-constructors
.from_dlpack() / .__dlpack__()TensorDLPack interop

Operators: +, -, *, /, **, ==, !=, <, >, <=, >=, [] (indexing/slicing)

Application

FunctionDescription
lf.request_exit()Exit with confirmation
lf.force_exit()Immediate exit
lf.run(path)Execute Python script
lf.on_frame(cb)Per-frame callback
lf.stop_animation()Clear frame callback
lf.mat4(rows)Create 4x4 matrix
lf.help()Show help

pyproject.toml Schema

[project]
name = ""                    # string, required - Unique plugin identifier
version = ""                 # string, required - Semantic version
description = ""             # string, required
authors = []                 # list[{name, email}], optional - PEP 621 authors
dependencies = []            # list[string], optional - Python packages (PEP 508)

[tool.lichtfeld]
hot_reload = true            # bool, required
entry_point = "__init__"     # string, optional - Module to load (default: __init__)
plugin_api = ">=1,<2"        # string, required - Supported plugin API range (PEP 440)
lichtfeld_version = ">=0.4.2"  # string, required - Supported host app/runtime range (PEP 440)
required_features = []       # list[string], required - Optional host features this plugin needs
author = ""                  # string, optional - Author fallback (if no [project].authors)

v1 is strict. Legacy min_lichtfeld_version / max_lichtfeld_version fields are removed and rejected.


Icon System

from lfs_plugins.icon_manager import get_icon, get_ui_icon, get_scene_icon, get_plugin_icon
FunctionReturnsDescription
get_icon(name)intLoad assets/icon/{name}.png
get_ui_icon(name)intLoad assets/icon/{name} (include ext)
get_scene_icon(name)intLoad assets/icon/scene/{name}.png
get_plugin_icon(name, plugin_path, plugin_name)intLoad {plugin_path}/icons/{name}.png with fallback

All return OpenGL texture ID (0 on failure). Icons are cached by C++.

Direct loading:

import lichtfeld as lf
texture_id = lf.load_icon(name)
lf.free_icon(texture_id)

Errors

Plugin errors are captured and accessible via the plugin manager:

import lichtfeld as lf

state = lf.plugins.get_state("my_plugin")   # PluginState enum
error = lf.plugins.get_error("my_plugin")   # Error message string
tb = lf.plugins.get_traceback("my_plugin")  # Full traceback string

PluginState values

StateDescription
UNLOADEDPlugin is not loaded
INSTALLINGPlugin is being installed
LOADINGPlugin is loading
ACTIVEPlugin is running
ERRORPlugin failed to load/run
DISABLEDPlugin is manually disabled