RFDT Solver and WiTwin Simulator are undergoing internal testing as we prepare for public release. The functions are limited. Sign up to receive updates.

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#

FieldTypeDescription
successboolWhether import succeeded
object_idstrID of created scene object
messagestrStatus message for user
errorstrError 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
)