Node plugins#
The Nodes canvas can be extended with nodes that apply a user-defined transformation.
To this end, a Python file is required that turns input signals into an output signal.
Once installed, your node kind works like a builtin node with a toolbar button, param widgets, evaluation, scripting (node_*), and save/restore.
Plugins are optional, when no plugin is installed, the app runs unchanged.
For how to use nodes in the GUI see Nodes (transforms → derived signals).
Plugins location#
uz_dataviewer scans these locations in order, and only if they exist:
UZ_DATAVIEWER_PLUGINS: one or more directories (os.pathsep-separated::on Linux/macOS,;on Windows). Point this at your plugin folder.~/.uz_dataviewer/nodes/: the default user plugin folder.
export UZ_DATAVIEWER_PLUGINS=/path/to/my/plugins
python run.py
You can also load a folder at runtime from the console (no restart):
load_plugins("/path/to/my/plugins") # or load_plugins() for the configured dirs
A missing folder or file is silently skipped. A plugin that throws an error on import is logged to the console and skipped.
Plugin structure#
A plugin file registers one or more transforms with the @transform decorator.
The function takes inputs (a list of (time, y) NumPy arrays (one per connected input) as well as the node’s params (a dict[str, str]) and returns (out_time, out_y, info).
import numpy as np
from uz_dataviewer.plugins import transform, ParamSpec
@transform(
"movavg", # the node kind (unique; the toolbar button label)
params={"width": "51"}, # default params (values are strings)
inputs=(1, 1), # (min, max) number of inputs
ui=[ParamSpec.int("width", "window (samples)")], # widgets the canvas draws
)
def moving_average(inputs, params):
time, y = inputs[0]
width = max(1, int(float(params.get("width", 51))))
out = np.convolve(np.asarray(y, float), np.ones(width) / width, mode="same")
return time, out, f"moving avg, {width} samples" # info shows under the node
A working example is located at ultrazohm_sw/uz_dataviewer/examples/plugins/moving_average.py.
@transform(...) arguments#
arg |
meaning |
|---|---|
|
unique node-kind id; must not be |
|
default parameters as a |
|
|
|
list of |
|
optional unit string for the derived signal (default |
ParamSpec widgets#
ParamSpec.float(key, label), .int(key, label), .bool(key, label), .enum(key, options, label), .str(key, label).
Each renders a widget that writes back to params[key]; read it in your function with params.get(key, default).
params values are always strings — coerce with float() / int().
Rules & tips#
Output an
(out_time, out_y, info)tuple.out_timeis the x-axis of the derived signal (for a spectrum-like node it can be frequency). Raise a normal exception with a clear message on bad input — it’s caught, shown on the node, and the rest of the graph keeps working.The result is materialized as a derived run named after the node, so it appears in Navigation and is draggable into plots / FFT / Histogram.
Keep transforms pure NumPy if you want them to also work in the web build. Native plugins can import anything that’s installed.
Scripting & sessions: a graph stores only the node kind and its params, never your code, so
.uzscript/JSON round-trip unchanged. Opening a session whose plugin isn’t installed keeps the node as a greyed “missing plugin” placeholder (params + links preserved); install the plugin and re-evaluate to restore it.