Customize File Loading
Register custom file handlers for importing assets
Overview#
The File Import system allows you to register handlers for custom file types. When users drag files into the viewport or use the import dialog, your handler processes the file data.
Register a Handler#
from witwin.handlers.file_import import FileImportHandler
def handle_custom_file(filename: str, content: bytes) -> dict:
# Process file content
return {
"success": True,
"object_id": "created_object_123"
}
FileImportHandler.register_handler(
extensions=[".custom", ".cst"],
handler=handle_custom_file,
description="Custom file format"
)API Reference#
FileImportHandler.register_handler(
extensions: List[str], # File extensions ['.obj', '.ply']
handler: Callable, # Function(filename, content) -> result
description: str = "", # Description for UI
extension_id: str = None # For extension cleanup
)
FileImportHandler.unregister_handler(extensions: List[str])
FileImportHandler.unregister_extension_handlers(extension_id: str)Handler Function#
The handler receives the filename and file content as bytes:
def my_handler(filename: str, content: bytes) -> dict:
"""
Args:
filename: Original filename (e.g., "model.obj")
content: Raw file content as bytes
Returns:
dict with import results
"""
return {
"success": True,
"object_id": "obj_123",
"message": "Import successful"
}Return Value#
| Field | Type | Description |
|---|---|---|
success | bool | Whether import succeeded |
object_id | str | ID of created scene object |
message | str | Status message for user |
error | str | Error message if failed |
Examples#
Text Data File#
from witwin.handlers.file_import import FileImportHandler
from witwin import Console
def handle_csv(filename: str, content: bytes) -> dict:
try:
text = content.decode('utf-8')
lines = text.strip().split('\n')
header = lines[0].split(',')
rows = [line.split(',') for line in lines[1:]]
Console.log(f"Loaded CSV: {len(rows)} rows, {len(header)} columns")
return {
"success": True,
"message": f"Loaded {filename}: {len(rows)} rows"
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
FileImportHandler.register_handler(
extensions=[".csv"],
handler=handle_csv,
description="CSV data files"
)Binary Mesh File#
import numpy as np
from witwin.handlers.file_import import FileImportHandler
from witwin import Scene
def handle_ply(filename: str, content: bytes) -> dict:
try:
# Parse PLY header and data
vertices, faces = parse_ply(content)
# Create scene object with mesh
scene = Scene.get_current()
obj = scene.create_object(filename.replace('.ply', ''))
mesh = obj.add_component("Mesh")
mesh.set_mesh_data(vertices, faces)
return {
"success": True,
"object_id": obj.id,
"message": f"Imported {len(vertices)} vertices, {len(faces)} faces"
}
except Exception as e:
return {
"success": False,
"error": f"Failed to parse PLY: {e}"
}
def parse_ply(content: bytes):
# PLY parsing logic
# Returns (vertices: np.ndarray, faces: np.ndarray)
pass
FileImportHandler.register_handler(
extensions=[".ply"],
handler=handle_ply,
description="PLY mesh files"
)Image File#
import numpy as np
from PIL import Image
import io
from witwin.handlers.file_import import FileImportHandler
from witwin import Console
def handle_image(filename: str, content: bytes) -> dict:
try:
img = Image.open(io.BytesIO(content))
arr = np.array(img)
Console.log(f"Loaded image: {img.width}x{img.height}, mode={img.mode}")
return {
"success": True,
"message": f"Loaded {filename}: {img.width}x{img.height}"
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
FileImportHandler.register_handler(
extensions=[".png", ".jpg", ".jpeg", ".bmp"],
handler=handle_image,
description="Image files"
)JSON Configuration#
import json
from witwin.handlers.file_import import FileImportHandler
from witwin import Console, Notifications
def handle_json_config(filename: str, content: bytes) -> dict:
try:
config = json.loads(content.decode('utf-8'))
# Validate config structure
if 'version' not in config:
return {
"success": False,
"error": "Missing 'version' field in config"
}
# Apply configuration
apply_config(config)
Notifications.success("Config Loaded", f"Applied {filename}")
return {
"success": True,
"message": f"Applied configuration from {filename}"
}
except json.JSONDecodeError as e:
return {
"success": False,
"error": f"Invalid JSON: {e}"
}
def apply_config(config: dict):
Console.log(f"Applying config v{config['version']}")
# Apply config logic here
FileImportHandler.register_handler(
extensions=[".json", ".config"],
handler=handle_json_config,
description="Configuration files"
)Extension Integration#
When creating handlers in an extension, use the extension_id for cleanup:
EXTENSION_ID = "mesh_importer"
def handle_obj(filename: str, content: bytes) -> dict:
# Handle OBJ file
pass
def handle_stl(filename: str, content: bytes) -> dict:
# Handle STL file
pass
# Register handlers
FileImportHandler.register_handler(
extensions=[".obj"],
handler=handle_obj,
description="Wavefront OBJ",
extension_id=EXTENSION_ID
)
FileImportHandler.register_handler(
extensions=[".stl"],
handler=handle_stl,
description="STL mesh",
extension_id=EXTENSION_ID
)
# Later, to unregister all handlers from this extension:
# FileImportHandler.unregister_extension_handlers(EXTENSION_ID)Complete Example#
from witwin.handlers.file_import import FileImportHandler
from witwin import Console, Scene, Notifications
import numpy as np
EXTENSION_ID = "rf_importer"
def handle_s2p(filename: str, content: bytes) -> dict:
"""Handle Touchstone S2P files (RF S-parameters)."""
try:
text = content.decode('utf-8')
lines = [l.strip() for l in text.split('\n') if l.strip() and not l.startswith('!')]
# Parse header
header = None
data_lines = []
for line in lines:
if line.startswith('#'):
header = line
else:
data_lines.append(line)
# Parse frequency and S-parameters
frequencies = []
s_params = []
for line in data_lines:
parts = line.split()
if len(parts) >= 9:
freq = float(parts[0])
frequencies.append(freq)
# S11, S21, S12, S22 (real, imag pairs)
s_params.append([float(p) for p in parts[1:9]])
Console.log(f"Loaded S2P: {len(frequencies)} frequency points")
Console.log(f"Frequency range: {frequencies[0]/1e9:.2f} - {frequencies[-1]/1e9:.2f} GHz")
# Create visualization
from witwin import Results
Results.plot(
[f/1e9 for f in frequencies],
[s[0] for s in s_params], # S11 magnitude
title="S11 Parameter",
xlabel="Frequency (GHz)",
ylabel="S11 (dB)"
)
Results.commit(message=f"S-parameters from {filename}")
Notifications.success("S2P Import", f"Loaded {len(frequencies)} points")
return {
"success": True,
"message": f"Imported S-parameters: {len(frequencies)} frequency points"
}
except Exception as e:
Console.error(f"Failed to parse S2P: {e}")
return {
"success": False,
"error": str(e)
}
# Register the handler
FileImportHandler.register_handler(
extensions=[".s2p", ".s1p", ".snp"],
handler=handle_s2p,
description="Touchstone S-parameter files",
extension_id=EXTENSION_ID
)