Skip to content

Blender Integration

AuroraView integrates with Blender using Desktop Mode or Native Mode (HWND), as Blender uses its own UI framework (not Qt).

Integration Modes for Blender

ModeDescriptionUse Case
Desktop ModeIndependent window with own event loopSimple tools, quick prototypes
Native Mode (HWND)Floating window attached to BlenderProduction tools, always-on-top panels

Installation

bash
pip install auroraview

Quick Start

Desktop Mode

python
from auroraview import run_desktop

run_desktop(
    title="Blender Tool",
    url="http://localhost:3000",
    width=800,
    height=600
)

Native Mode (HWND) with Window Effects

python
from auroraview import WebView

# Create floating panel with effects
webview = WebView(
    title="Blender Tool",
    url="http://localhost:3000",
    width=400,
    height=600,
    transparent=True,
    decorations=False,
    always_on_top=True,
    tool_window=True,
)

# Apply window effects (Windows only)
webview.apply_acrylic((30, 30, 30, 180))
webview.enable_click_through()

webview.show()

API Binding Example

python
from auroraview import AuroraView
import bpy

class BlenderAPI:
    def get_selected_objects(self) -> dict:
        """Get selected objects"""
        selected = bpy.context.selected_objects
        return {
            "objects": [obj.name for obj in selected],
            "count": len(selected)
        }

    def select_object(self, name: str = "") -> dict:
        """Select object by name"""
        bpy.ops.object.select_all(action='DESELECT')
        obj = bpy.data.objects.get(name)
        if obj:
            obj.select_set(True)
            bpy.context.view_layer.objects.active = obj
            return {"ok": True, "name": name}
        return {"ok": False, "error": "Object not found"}

    def create_cube(self, name: str = "Cube", size: float = 2.0) -> dict:
        """Create a cube"""
        bpy.ops.mesh.primitive_cube_add(size=size)
        obj = bpy.context.active_object
        obj.name = name
        return {"ok": True, "name": obj.name}

    def get_transform(self, name: str = "") -> dict:
        """Get object transform"""
        obj = bpy.data.objects.get(name)
        if obj:
            return {
                "ok": True,
                "location": list(obj.location),
                "rotation": list(obj.rotation_euler),
                "scale": list(obj.scale)
            }
        return {"ok": False, "error": "Object not found"}

    def set_location(self, name: str = "", x: float = 0, y: float = 0, z: float = 0) -> dict:
        """Set object location"""
        obj = bpy.data.objects.get(name)
        if obj:
            obj.location = (x, y, z)
            return {"ok": True}
        return {"ok": False, "error": "Object not found"}

    def render_image(self, filepath: str = "/tmp/render.png") -> dict:
        """Render current scene"""
        bpy.context.scene.render.filepath = filepath
        bpy.ops.render.render(write_still=True)
        return {"ok": True, "filepath": filepath}

# Create WebView with API
webview = AuroraView(
    url="http://localhost:3000",
    api=BlenderAPI()
)
webview.show()
javascript
// JavaScript side
const sel = await auroraview.api.get_selected_objects();
console.log('Selected:', sel.objects);

await auroraview.api.create_cube({ name: 'MyCube', size: 3.0 });
await auroraview.api.set_location({ name: 'MyCube', x: 2, y: 0, z: 1 });

// Render
await auroraview.api.render_image({ filepath: '/tmp/my_render.png' });

Blender Operator

Create a Blender operator to launch the tool:

python
import bpy
from auroraview import AuroraView

class AURORAVIEW_OT_launch_tool(bpy.types.Operator):
    bl_idname = "auroraview.launch_tool"
    bl_label = "Launch AuroraView Tool"
    bl_description = "Launch the AuroraView web tool"

    _webview = None

    def execute(self, context):
        if AURORAVIEW_OT_launch_tool._webview is None:
            AURORAVIEW_OT_launch_tool._webview = AuroraView(
                url="http://localhost:3000",
                api=BlenderAPI()
            )
        AURORAVIEW_OT_launch_tool._webview.show()
        return {'FINISHED'}

def menu_func(self, context):
    self.layout.operator(AURORAVIEW_OT_launch_tool.bl_idname)

def register():
    bpy.utils.register_class(AURORAVIEW_OT_launch_tool)
    bpy.types.VIEW3D_MT_view.append(menu_func)

def unregister():
    bpy.types.VIEW3D_MT_view.remove(menu_func)
    bpy.utils.unregister_class(AURORAVIEW_OT_launch_tool)

if __name__ == "__main__":
    register()

Add-on Structure

Create a proper Blender add-on:

my_addon/
├── __init__.py
├── api.py
└── operators.py

__init__.py:

python
bl_info = {
    "name": "AuroraView Tool",
    "author": "Your Name",
    "version": (1, 0, 0),
    "blender": (3, 0, 0),
    "location": "View3D > View > AuroraView Tool",
    "description": "Web-based tool using AuroraView",
    "category": "3D View",
}

from . import operators

def register():
    operators.register()

def unregister():
    operators.unregister()

Thread Safety

AuroraView provides automatic thread safety for Blender integration. Blender requires all bpy operations to run on the main thread, which AuroraView handles via bpy.app.timers.

Zero Configuration

Since dcc_mode="auto" is the default, AuroraView automatically detects Blender and enables thread safety. No configuration needed!

Automatic Thread Safety (Default)

Just use AuroraView normally - thread safety is automatic:

python
from auroraview import WebView
import bpy

# Thread safety is automatically enabled when Blender is detected
webview = WebView(
    title="Blender Tool",
    url="http://localhost:3000",
    # dcc_mode="auto" is the default - no need to specify!
)

@webview.on("create_mesh")
def handle_create(data):
    # Automatically runs on Blender main thread!
    mesh_type = data.get("type", "cube")
    if mesh_type == "cube":
        bpy.ops.mesh.primitive_cube_add()
    elif mesh_type == "sphere":
        bpy.ops.mesh.primitive_uv_sphere_add()
    return {"ok": True, "object": bpy.context.active_object.name}

@webview.on("get_selection")
def handle_selection(data):
    selected = [obj.name for obj in bpy.context.selected_objects]
    return {"selection": selected, "count": len(selected)}

Manual Thread Safety with Decorators

python
from auroraview import WebView
from auroraview.utils import dcc_thread_safe, dcc_thread_safe_async

webview = WebView(title="Blender Tool", url="http://localhost:3000")

@webview.on("render_frame")
@dcc_thread_safe  # Blocks until render complete
def handle_render(data):
    filepath = data.get("filepath", "/tmp/render.png")
    bpy.context.scene.render.filepath = filepath
    bpy.ops.render.render(write_still=True)
    return {"ok": True, "filepath": filepath}

@webview.on("refresh_view")
@dcc_thread_safe_async  # Fire-and-forget
def handle_refresh(data):
    for area in bpy.context.screen.areas:
        area.tag_redraw()

Using run_on_main_thread Directly

python
from auroraview.utils import run_on_main_thread, run_on_main_thread_sync

# Fire-and-forget
def deselect_all():
    bpy.ops.object.select_all(action='DESELECT')

run_on_main_thread(deselect_all)

# Blocking with return value
def get_scene_objects():
    return [obj.name for obj in bpy.data.objects]

objects = run_on_main_thread_sync(get_scene_objects)
print(f"Scene objects: {objects}")

Timer-based Updates

For real-time sync with Blender:

python
import bpy
from auroraview import AuroraView

class BlenderSyncTool:
    def __init__(self):
        self.webview = AuroraView(url="http://localhost:3000")
        self._last_selection = []
        bpy.app.timers.register(self._check_selection, persistent=True)

    def _check_selection(self):
        current = [obj.name for obj in bpy.context.selected_objects]
        if current != self._last_selection:
            self._last_selection = current
            self.webview.emit("selection_changed", {"objects": current})
        return 0.1  # Check every 100ms

    def show(self):
        self.webview.show()

# Usage
tool = BlenderSyncTool()
tool.show()

Released under the MIT License.