NodeFlowWidget¶
The main widget for creating and managing interactive node-based workflows in Jupyter notebooks.
NodeFlowWidget(height='600px', **kwargs)
¶
Bases: AnyWidget
A Jupyter widget wrapping ReactFlow for interactive node graph visualization.
This widget can be initialized with a list of node classes that implement the NodeFactory protocol. Node types will be automatically registered and made available in the visual editor.
Examples:
>>> from pynodewidget import NodeFlowWidget
>>>
>>> class MyNode(JsonSchemaNodeWidget):
... label = "My Node"
... parameters = MyParams
>>>
>>> flow = NodeFlowWidget(
... nodes=[MyNode],
... height="800px"
... )
Initialize the NodeFlowWidget.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
height
|
str
|
Height of the widget canvas (default: "600px") |
'600px'
|
**kwargs
|
Any
|
Additional widget configuration options |
{}
|
Source code in pynodewidget/widget.py
export_json(filename='flow.json')
¶
Export the current flow to a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Output filename |
'flow.json'
|
Source code in pynodewidget/widget.py
clear()
¶
get_flow_data()
¶
Get the current flow data as a dictionary.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with nodes and edges |
add_node_type_from_schema(json_schema, type_name, label, description='', icon='', grid_layout=None, style=None, _default_values_override=None)
¶
Add a node type from a JSON schema with grid layout support.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_schema
|
Dict[str, Any]
|
JSON Schema definition (can be from Pydantic model_json_schema()) |
required |
type_name
|
str
|
Unique type identifier |
required |
label
|
str
|
Display label for the node |
required |
description
|
str
|
Description shown in the panel |
''
|
icon
|
str
|
Unicode emoji or symbol (e.g., "🔧", "⚙️", "📊") |
''
|
grid_layout
|
Optional[Dict[str, Any]]
|
Grid layout configuration (use helpers from grid_layouts module). Can be a dict or a NodeGrid Pydantic model. If not provided, defaults to vertical layout with JSON schema fields. |
None
|
style
|
Optional[Dict[str, Any]]
|
Style configuration dict with 'minWidth', 'maxWidth', 'shadow', etc. |
None
|
_default_values_override
|
Optional[Dict[str, Any]]
|
Internal parameter to override default values extraction |
None
|
Example
from pynodewidget.grid_layouts import create_three_column_grid from pynodewidget.models import BaseHandle, TextField
widget.add_node_type_from_schema( ... json_schema={"type": "object", "properties": {...}}, ... type_name="processor", ... label="Data Processor", ... icon="⚙️", ... grid_layout=create_three_column_grid( ... left_components=[BaseHandle(id="in1", label="Input", handle_type="input")], ... center_components=[TextField(id="name", label="Name")], ... right_components=[BaseHandle(id="out1", label="Output", handle_type="output")] ... ) ... )
Source code in pynodewidget/widget.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
Overview¶
NodeFlowWidget is the entry point for creating node-based UIs in Jupyter. It manages:
- Node types: Registry of available node classes
- Graph state: Nodes, edges, and their positions
- Values: Current parameter values for all nodes
- Viewport: Canvas position and zoom level
Basic Usage¶
Creating a Widget¶
from pynodewidget import NodeFlowWidget
# Empty widget
flow = NodeFlowWidget()
# With registered node types
flow = NodeFlowWidget(nodes=[MyNode1, MyNode2])
# Custom height
flow = NodeFlowWidget(height="800px")
Registering Node Types¶
# Register during initialization
flow = NodeFlowWidget(nodes=[ProcessorNode, SourceNode])
# Register after creation
flow.register_node_type(SinkNode)
# Register with custom type name
flow.register_node_type(MyNode, type_name="custom_processor")
Working with Values¶
Getting Values¶
# Get all values for a node
values = flow.get_node_values("node-1")
# Returns: {"threshold": 0.5, "enabled": True}
# Get single value with default
threshold = flow.get_node_value("node-1", "threshold", default=0.5)
Setting Values¶
# Update single value
flow.update_node_value("node-1", "threshold", 0.8)
# Update multiple values
flow.set_node_values("node-1", {
"threshold": 0.8,
"enabled": False,
"mode": "advanced"
})
Value Synchronization¶
The node_values trait uses ObservableDict for automatic synchronization:
# This triggers an update to the JavaScript UI
flow.node_values["node-1"]["threshold"] = 0.8
# Changes in the UI automatically update this dict
print(flow.node_values["node-1"]["threshold"])
Managing Graph Structure¶
Accessing Nodes and Edges¶
# Get all nodes
nodes = flow.nodes
# [{"id": "node-1", "type": "ProcessorNode", "position": {...}, ...}]
# Get all edges
edges = flow.edges
# [{"id": "e1-2", "source": "node-1", "target": "node-2", ...}]
# Get specific node data
node_data = flow.get_node_data("node-1")
Modifying Graph¶
# Add a node programmatically
flow.nodes = [
*flow.nodes,
{
"id": "new-node",
"type": "ProcessorNode",
"position": {"x": 100, "y": 100},
"data": {}
}
]
# Add an edge
flow.edges = [
*flow.edges,
{
"id": "e-new",
"source": "node-1",
"target": "new-node",
"sourceHandle": "out",
"targetHandle": "in"
}
]
# Clear everything
flow.clear()
Import and Export¶
Export to File¶
# Export complete workflow
flow.export_json("workflow.json")
# Export returns the filename
filename = flow.export_json("my_workflow.json")
print(f"Saved to {filename}")
The exported JSON contains:
{
"nodes": [...],
"edges": [...],
"viewport": {"x": 0, "y": 0, "zoom": 1},
"node_templates": [...]
}
Import from File¶
# Load workflow
flow.load_json("workflow.json")
# Method chaining
flow.clear().load_json("workflow.json")
Node Types Must Be Registered
Before loading a workflow, ensure all node types used in the workflow are registered, or they won't render correctly.
Export as Dictionary¶
# Get flow data as dict
flow_data = flow.get_flow_data()
# Returns: {"nodes": [...], "edges": [...]}
# Full export including templates and viewport
import json
full_export = {
"nodes": flow.nodes,
"edges": flow.edges,
"node_templates": flow.node_templates,
"viewport": flow.viewport,
"node_values": dict(flow.node_values)
}
Traits (Synchronized Attributes)¶
These attributes automatically sync between Python and JavaScript:
nodes: List[Dict]¶
List of node objects in the graph.
flow.nodes = [
{
"id": "node-1",
"type": "ProcessorNode",
"position": {"x": 100, "y": 50},
"data": {...}
}
]
edges: List[Dict]¶
List of edge objects connecting nodes.
flow.edges = [
{
"id": "e1-2",
"source": "node-1",
"target": "node-2",
"sourceHandle": "out",
"targetHandle": "in"
}
]
node_templates: List[Dict]¶
Registered node type definitions. Populated by register_node_type().
node_values: ObservableDict¶
Current parameter values for all nodes, keyed by node ID.
viewport: Dict¶
Current viewport position and zoom.
height: str¶
Widget height (CSS value).
Legacy Methods¶
These methods are provided for backward compatibility:
add_node_type_from_schema()¶
Register a node type from a raw JSON schema.
flow.add_node_type_from_schema(
json_schema={"type": "object", "properties": {...}},
type_name="processor",
label="Processor",
icon="⚙️",
inputs=[{"id": "in", "label": "Input"}],
outputs=[{"id": "out", "label": "Output"}]
)
Prefer register_node_type()
For new code, use register_node_type() with a JsonSchemaNodeWidget subclass instead.
add_node_type_from_pydantic()¶
Register a node type from a Pydantic model.
from pydantic import BaseModel
class ProcessorParams(BaseModel):
threshold: float = 0.5
flow.add_node_type_from_pydantic(
model_class=ProcessorParams,
type_name="processor",
label="Processor",
icon="⚙️"
)
Examples¶
Complete Workflow Example¶
from pydantic import BaseModel, Field
from pynodewidget import NodeFlowWidget, JsonSchemaNodeWidget
# Define parameters
class FilterParams(BaseModel):
threshold: float = Field(default=0.5, ge=0, le=1)
enabled: bool = True
# Define node
class FilterNode(JsonSchemaNodeWidget):
label = "Filter"
parameters = FilterParams
icon = "🔍"
inputs = [{"id": "in", "label": "Data"}]
outputs = [{"id": "out", "label": "Filtered"}]
def execute(self, inputs):
config = self.get_values()
if not config["enabled"]:
return {"out": inputs["in"]}
data = inputs["in"]
threshold = config["threshold"]
return {"out": [x for x in data if x >= threshold]}
# Create widget
flow = NodeFlowWidget(nodes=[FilterNode], height="600px")
# Later: Update values from Python
flow.update_node_value("filter-1", "threshold", 0.8)
# Execute workflow (custom logic)
def run_workflow(flow):
# Your execution logic here
pass
# Export for later use
flow.export_json("filter_workflow.json")
Multiple Node Types¶
class SourceNode(JsonSchemaNodeWidget):
label = "Data Source"
parameters = SourceParams
outputs = [{"id": "data", "label": "Data"}]
class ProcessorNode(JsonSchemaNodeWidget):
label = "Processor"
parameters = ProcessorParams
inputs = [{"id": "in", "label": "Input"}]
outputs = [{"id": "out", "label": "Output"}]
class SinkNode(JsonSchemaNodeWidget):
label = "Data Sink"
parameters = SinkParams
inputs = [{"id": "data", "label": "Data"}]
# Create comprehensive workflow
flow = NodeFlowWidget(
nodes=[SourceNode, ProcessorNode, SinkNode],
height="800px"
)
Programmatic Graph Construction¶
# Create widget
flow = NodeFlowWidget(nodes=[MyNode])
# Add nodes programmatically
flow.nodes = [
{
"id": "source",
"type": "my_node",
"position": {"x": 0, "y": 0},
"data": {}
},
{
"id": "processor",
"type": "my_node",
"position": {"x": 200, "y": 0},
"data": {}
}
]
# Connect them
flow.edges = [
{
"id": "e1",
"source": "source",
"target": "processor",
"sourceHandle": "out",
"targetHandle": "in"
}
]
# Set initial values
flow.set_node_values("processor", {"threshold": 0.7})
See Also¶
- JsonSchemaNodeWidget: Create custom nodes
- ObservableDict: Auto-syncing dictionary
- Protocols: Extension protocols