Extension System
File structure and manifest configuration for WiTwin extensions
Overview#
The Extension System allows you to extend WiTwin Studio with custom panels, components, nodes, console commands, file loaders, and geometry generators. Extensions are Python packages that integrate seamlessly with the editor's UI and backend.
Extension Structure#
my_extension/
├── manifest.json # Extension metadata (required)
├── __init__.py # Python entry point
├── panels.py # Custom panels
├── components.py # Custom components
├── nodes.py # Custom nodes
└── settings.py # Custom settingsManifest File#
The manifest.json file is required for every extension and defines its metadata:
{
"id": "my_extension",
"name": "My Extension",
"version": "1.0.0",
"description": "Description of my extension",
"author": "Your Name",
"dependencies": ["numpy", "scipy"],
"entry_point": "__init__.py"
}Manifest Fields#
| Field | Required | Description |
|---|---|---|
id | Yes | Unique identifier (lowercase, no spaces) |
name | Yes | Display name shown in UI |
version | Yes | Semantic version (e.g., "1.0.0") |
description | No | Short description of the extension |
author | No | Author name or organization |
dependencies | No | Python packages to auto-install via pip |
entry_point | No | Python entry file (default: __init__.py) |
Example Manifest#
{
"id": "rf_analyzer",
"name": "RF Analyzer",
"version": "1.0.0",
"description": "RF signal analysis tools for wireless simulation",
"author": "WiTwin Team",
"dependencies": ["numpy", "scipy", "scikit-rf"]
}Extension Loading#
When WiTwin starts, extensions are loaded in this order:
- Server discovers extensions in
extensions/directory - Manifest is read and validated
- Dependencies are installed via pip (if specified)
- Entry point module is executed
- Decorated classes are auto-registered (components, panels, settings, nodes)
Entry Point#
The __init__.py file is the entry point for your extension. Import all your custom classes here:
# __init__.py
from .components import MyComponent, AnotherComponent
from .panels import MyPanel
from .nodes import MyNode
from .settings import MySettings
# All decorated classes are auto-registered on importExtension Panel UI#
The Extensions panel in the editor shows:
- List of installed extensions
- Enable/disable toggle per extension
- Extension status indicator (loaded, error)
- Reload button to refresh extensions
- Settings link (if extension has settings)
What You Can Customize#
| Feature | Documentation |
|---|---|
| Components | Customize Components |
| Nodes | Customize Nodes |
| Panels | Customize Panels |
| Console Commands | Customize Console Commands |
| File Loading | Customize File Loading |
| Geometry | Customize Geometry |
Complete Extension Example#
File Structure#
rf_analyzer/
├── manifest.json
├── __init__.py
├── components.py
└── panels.pymanifest.json#
{
"id": "rf_analyzer",
"name": "RF Analyzer",
"version": "1.0.0",
"description": "RF signal analysis tools",
"dependencies": ["numpy", "scipy"]
}init.py#
from .components import RFAnalyzerComponent
from .panels import RFToolsPanelcomponents.py#
from witwin.components import Component, component, button, float_field
from witwin import Results, Console
@component(name="RFAnalyzer")
class RFAnalyzerComponent(Component):
frequency = float_field(2.4e9, min=1e6, max=100e9)
bandwidth = float_field(20e6, min=1e3, max=1e9)
@button(display_name="Analyze Spectrum")
def analyze(self):
Console.log(f"Analyzing at {self.frequency/1e9:.2f} GHz")
# Perform analysis...
Results.plot(freqs, power, title="Spectrum")
Results.commit(tag="spectrum")
return "Analysis complete"panels.py#
from witwin.panels import Panel, panel
from witwin import Console
@panel(name="RF Tools", icon="radio")
class RFToolsPanel(Panel):
def get_ui(self):
return {
"type": "container",
"children": [
{"type": "label", "text": "RF Analysis Tools"},
{"type": "button", "label": "Scan All", "action": "scan_all"}
]
}
def scan_all(self):
Console.log("Scanning all RF components...")
return {"status": "scanning"}