NodeBuilder¶
Base class for creating custom node types using Pydantic models.
NodeBuilder(id=None, data=None, selected=None, **initial_values)
¶
Bases: AnyWidget
Base class for custom node widgets implementing the NodeFactory protocol.
This class can be used in two ways: 1. As a standalone widget for configuration UI 2. As a base class for custom node types registered with NodeFlowWidget
To create a custom node, inherit from this class and define: - parameters: A Pydantic BaseModel class defining configuration fields - label: Display name for the node - Optional: icon, category, description, inputs, outputs
Example
from pydantic import BaseModel, Field
class ProcessingConfig(BaseModel): ... threshold: float = Field(default=0.5, ge=0, le=1) ... mode: str = "auto"
class ProcessingNode(NodeBuilder): ... parameters = ProcessingConfig ... label = "Image Processor" ... icon = "🖼️" ... category = "processing" ... inputs = [{"id": "input", "label": "Image"}] ... outputs = [{"id": "output", "label": "Processed"}]
Initialize the node builder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
Widget ID (default: "json-schema-node") |
None
|
|
data
|
Initial data dict (for standalone widget mode) |
None
|
|
selected
|
Selection state |
None
|
|
**initial_values
|
Initial parameter values (passed to Pydantic model) |
{}
|
Source code in pynodewidget/json_schema_node.py
get_values()
¶
Get current configuration values.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing all current parameter values |
Source code in pynodewidget/json_schema_node.py
set_values(values)
¶
Update configuration values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Dict[str, Any]
|
Dictionary of parameter values to update |
required |
Source code in pynodewidget/json_schema_node.py
set_value(key, value)
¶
Update a single configuration value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Parameter name |
required |
value
|
Any
|
New value |
required |
validate()
¶
Validate current configuration.
Returns:
| Type | Description |
|---|---|
bool
|
True if configuration is valid, False otherwise |
Source code in pynodewidget/json_schema_node.py
execute(inputs)
¶
Execute node logic (to be overridden in subclasses).
This is a placeholder for future execution engine support. Subclasses can override this to implement custom node logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Dict[str, Any]
|
Dictionary of input values from connected nodes |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary of output values |
Source code in pynodewidget/json_schema_node.py
from_pydantic(model_class, label=None, icon='', category='general', description='', grid_layout=None, initial_values=None, header=None, footer=None, style=None, validation=None, fieldConfigs=None, **kwargs)
classmethod
¶
Create a node from a Pydantic model (factory method for convenience).
This is a convenience method for creating a node widget without defining a full subclass. For better code organization, prefer creating a proper subclass instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
Type[BaseModel]
|
Pydantic BaseModel class |
required |
label
|
Optional[str]
|
Display name |
None
|
icon
|
str
|
Unicode emoji or icon |
''
|
category
|
str
|
Node category |
'general'
|
description
|
str
|
Help text |
''
|
grid_layout
|
Optional[Dict[str, Any]]
|
Grid layout configuration dict |
None
|
initial_values
|
Optional[Dict[str, Any]]
|
Initial parameter values |
None
|
header
|
Optional[Dict[str, Any]]
|
Header configuration dict |
None
|
footer
|
Optional[Dict[str, Any]]
|
Footer configuration dict |
None
|
style
|
Optional[Dict[str, Any]]
|
Style configuration dict |
None
|
validation
|
Optional[Dict[str, Any]]
|
Validation configuration dict |
None
|
fieldConfigs
|
Optional[Dict[str, Dict[str, Any]]]
|
Per-field configuration dict |
None
|
**kwargs
|
Any
|
Additional configuration options |
{}
|
Returns:
| Type | Description |
|---|---|
NodeBuilder
|
New JsonSchemaNodeWidget instance |
Source code in pynodewidget/json_schema_node.py
227 228 229 230 231 232 233 234 235 236 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 | |
from_schema(schema, label, icon='', category='general', description='', grid_layout=None, initial_values=None)
classmethod
¶
Create a node from a JSON schema (legacy support for rapid prototyping).
This method provides backward compatibility for code using raw JSON schemas. For new code, use Pydantic models with the parameters attribute instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
Dict[str, Any]
|
JSON Schema definition |
required |
label
|
str
|
Display name |
required |
icon
|
str
|
Unicode emoji or icon |
''
|
category
|
str
|
Node category |
'general'
|
description
|
str
|
Help text |
''
|
grid_layout
|
Optional[Dict[str, Any]]
|
Grid layout configuration dict |
None
|
initial_values
|
Optional[Dict[str, Any]]
|
Initial parameter values |
None
|
Returns:
| Type | Description |
|---|---|
NodeBuilder
|
New JsonSchemaNodeWidget instance |
Source code in pynodewidget/json_schema_node.py
Overview¶
JsonSchemaNodeWidget implements the NodeFactory protocol and provides a convenient base class for defining custom nodes. It automatically generates UI from Pydantic models and handles value synchronization.
Basic Usage¶
Creating a Node Class¶
from pydantic import BaseModel, Field
from pynodewidget import JsonSchemaNodeWidget
# 1. Define parameters with Pydantic
class ProcessorParams(BaseModel):
threshold: float = Field(default=0.5, ge=0, le=1)
mode: str = "auto"
enabled: bool = True
# 2. Create node class
class ProcessorNode(JsonSchemaNodeWidget):
label = "Processor"
parameters = ProcessorParams
icon = "⚙️"
category = "processing"
description = "Process data with threshold"
inputs = [{"id": "in", "label": "Input"}]
outputs = [{"id": "out", "label": "Output"}]
layout_type = "horizontal"
handle_type = "button"
Required Attributes¶
label: Display name (string)parameters: Pydantic BaseModel class
Optional Attributes¶
icon: Emoji or Unicode symbol (default: "")category: Grouping category (default: "general")description: Help text (default: "")inputs: List of input handle configs (default: [])outputs: List of output handle configs (default: [])layout_type: Layout style - "horizontal", "vertical", "compact" (default: "horizontal")handle_type: Handle type - "base", "button", "labeled" (default: "base")
Handle Configuration¶
Input and Output Handles¶
class MyNode(JsonSchemaNodeWidget):
label = "My Node"
parameters = MyParams
inputs = [
{"id": "data_in", "label": "Data"},
{"id": "config_in", "label": "Config"}
]
outputs = [
{"id": "result", "label": "Result"},
{"id": "metadata", "label": "Metadata"}
]
Using Pydantic for Handles¶
from pydantic import BaseModel
class InputHandles(BaseModel):
data_in: str
config_in: str
class OutputHandles(BaseModel):
result: str
metadata: str
class MyNode(JsonSchemaNodeWidget):
label = "My Node"
parameters = MyParams
inputs = InputHandles
outputs = OutputHandles
Working with Values¶
Getting Values¶
# Inside node class
def execute(self, inputs):
config = self.get_values()
threshold = config["threshold"]
mode = config["mode"]
# ... use values
# From widget instance
node = ProcessorNode(threshold=0.8, enabled=True)
values = node.get_values()
# {"threshold": 0.8, "mode": "auto", "enabled": True}
Setting Values¶
# Set multiple values
node.set_values({"threshold": 0.9, "mode": "manual"})
# Set single value
node.set_value("threshold", 0.9)
# During initialization
node = ProcessorNode(threshold=0.7, mode="advanced")
Validation¶
# Check if current values are valid
if node.validate():
print("Configuration is valid")
else:
print("Invalid configuration")
Execution Logic¶
Implementing execute()¶
class ProcessorNode(JsonSchemaNodeWidget):
label = "Processor"
parameters = ProcessorParams
inputs = [{"id": "data_in", "label": "Data"}]
outputs = [{"id": "data_out", "label": "Processed"}]
def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
"""Process input data."""
# Get configuration
config = self.get_values()
threshold = config["threshold"]
# Get input data
data = inputs.get("data_in", [])
# Process
filtered = [x for x in data if x >= threshold]
# Return outputs
return {"data_out": filtered}
execute() Signature¶
def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
"""Execute node logic.
Args:
inputs: Dictionary mapping input handle IDs to values
e.g., {"data_in": [1, 2, 3], "config_in": {...}}
Returns:
Dictionary mapping output handle IDs to values
e.g., {"data_out": [1, 2], "metadata": {...}}
"""
pass
Execution is Optional
PyNodeWidget provides the graph structure but doesn't enforce an execution model. Implement execute() if you plan to run workflows programmatically.
Factory Methods¶
from_pydantic()¶
Create a node without defining a full subclass:
from pynodewidget import JsonSchemaNodeWidget
from pynodeflow.node_builder import with_style
# Quick node creation
node = JsonSchemaNodeWidget.from_pydantic(
model_class=ProcessorParams,
label="Quick Processor",
icon="⚡",
category="processing",
inputs=[{"id": "in", "label": "Input"}],
outputs=[{"id": "out", "label": "Output"}],
initial_values={"threshold": 0.7}
)
# With styling
node = JsonSchemaNodeWidget.from_pydantic(
model_class=ProcessorParams,
label="Styled Processor",
header={
"show": True,
"icon": "✨",
"className": "bg-blue-600 text-white"
},
style={
"minWidth": "300px",
"shadow": "lg"
}
)
from_schema()¶
Create a node from raw JSON schema (legacy):
schema = {
"type": "object",
"properties": {
"threshold": {
"type": "number",
"default": 0.5,
"minimum": 0,
"maximum": 1
}
}
}
node = JsonSchemaNodeWidget.from_schema(
schema=schema,
label="Schema Node",
icon="📋",
inputs=[{"id": "in", "label": "Input"}],
outputs=[{"id": "out", "label": "Output"}]
)
Layout and Styling¶
Layout Types¶
# Horizontal: inputs left, outputs right
class HorizontalNode(JsonSchemaNodeWidget):
layout_type = "horizontal"
# Vertical: inputs top, outputs bottom
class VerticalNode(JsonSchemaNodeWidget):
layout_type = "vertical"
# Compact: minimal spacing
class CompactNode(JsonSchemaNodeWidget):
layout_type = "compact"
Handle Types¶
# Base: small dot handles (default)
class BaseNode(JsonSchemaNodeWidget):
handle_type = "base"
# Button: larger, interactive buttons
class ButtonNode(JsonSchemaNodeWidget):
handle_type = "button"
# Labeled: handles with visible labels
class LabeledNode(JsonSchemaNodeWidget):
handle_type = "labeled"
Mixed Handle Types¶
# Different types for inputs and outputs
node = JsonSchemaNodeWidget.from_pydantic(
MyParams,
label="Mixed Handles",
input_handle_type="labeled",
output_handle_type="button"
)
Advanced Configuration¶
Enhanced Styling¶
from pynodeflow.node_builder import create_processing_node, with_style
# Using node builder utilities
config = create_processing_node("Advanced Node", icon="🚀")
config = with_style(config,
min_width="350px",
max_width="600px",
border_radius="12px",
shadow="xl",
class_name="border-2 border-blue-500"
)
node = JsonSchemaNodeWidget.from_pydantic(
MyParams,
**config
)
Custom Header and Footer¶
node = JsonSchemaNodeWidget.from_pydantic(
MyParams,
label="Custom Node",
header={
"show": True,
"icon": "✨",
"bgColor": "#3b82f6",
"textColor": "#ffffff",
"className": "font-bold"
},
footer={
"show": True,
"text": "v1.0.0",
"className": "text-xs text-gray-500"
}
)
Conditional Fields¶
from pynodeflow.node_builder import make_fields_conditional
fieldConfigs = make_fields_conditional(
trigger_field="mode",
trigger_value="advanced",
dependent_fields=["threshold", "iterations"]
)
node = JsonSchemaNodeWidget.from_pydantic(
MyParams,
label="Conditional Node",
fieldConfigs=fieldConfigs
)
Validation Configuration¶
node = JsonSchemaNodeWidget.from_pydantic(
MyParams,
label="Validated Node",
validation={
"showErrors": True,
"errorPosition": "inline", # or "tooltip", "footer"
"validateOnChange": True
}
)
Complete Example¶
from pydantic import BaseModel, Field
from typing import Literal
from pynodewidget import JsonSchemaNodeWidget, NodeFlowWidget
# Define parameters
class ImageProcessorParams(BaseModel):
algorithm: Literal["blur", "sharpen", "edge"] = "blur"
strength: float = Field(default=0.5, ge=0, le=1, description="Effect strength")
preserve_alpha: bool = Field(default=True, description="Preserve transparency")
iterations: int = Field(default=1, ge=1, le=10, description="Number of passes")
# Create node class
class ImageProcessorNode(JsonSchemaNodeWidget):
"""Image processing node with multiple algorithms."""
label = "Image Processor"
parameters = ImageProcessorParams
icon = "🖼️"
category = "image"
description = "Apply various image processing algorithms"
inputs = [
{"id": "image", "label": "Image"},
{"id": "mask", "label": "Mask (optional)"}
]
outputs = [
{"id": "result", "label": "Processed Image"},
{"id": "metadata", "label": "Processing Info"}
]
layout_type = "vertical"
handle_type = "labeled"
def execute(self, inputs):
"""Execute image processing."""
config = self.get_values()
image = inputs.get("image")
mask = inputs.get("mask")
# Processing logic here
result = self._process_image(
image,
algorithm=config["algorithm"],
strength=config["strength"],
iterations=config["iterations"]
)
metadata = {
"algorithm": config["algorithm"],
"iterations": config["iterations"]
}
return {
"result": result,
"metadata": metadata
}
def _process_image(self, image, algorithm, strength, iterations):
"""Internal processing method."""
# Your image processing code
pass
# Use in workflow
flow = NodeFlowWidget(nodes=[ImageProcessorNode])
flow
See Also¶
- NodeFlowWidget: Main widget for workflows
- Protocols: NodeFactory protocol
- Custom Nodes Guide: Practical examples