Skip to main content

Qt integration for Blender

Project description

blender_qt

Qt integration for Blender with support for:

  • custom node-editor spaces backed by QML or QWidget
  • viewport gizmos backed by QML or QWidget
  • managed top-level Qt windows

Developer-oriented architecture and workaround notes live in docs/developer-guide.md.

Stable public API is exposed from the top-level blender_qt package. Modules under blender_qt.core, blender_qt.qml, blender_qt.widgets, and blender_qt._internal should be treated as implementation details unless explicitly documented otherwise.

Installation

pip install blender_qt

The wheel is published for add-on authors who want to depend on Blender Qt from normal Python packaging workflows. The package can be imported outside Blender for metadata and API discovery, but runtime features still require Blender's Python environment and modules such as bpy.

Sample Extension

See blender_qt_sample/README.md for a sample Blender extension project that uses this wheel and packages it with beb.

That sample uses beb because it is a convenient way to build a Blender extension archive around the local blender_qt wheel. You do not need to use beb for your own Blender Qt-based add-ons unless that workflow fits your packaging needs.

Lifecycle

Call blender_qt.init() before registering spaces, gizmos, or windows. Call blender_qt.shutdown() when your add-on unloads.

import blender_qt


def register() -> None:
    blender_qt.init()


def unregister() -> None:
    blender_qt.shutdown()

Register a custom QML space

Use register_qml_space() to add a custom node-editor space rendered from a QML root item.

from pathlib import Path

import blender_qt

TREE_TYPE = "MY_ADDON_QML_NT"
QML_PATH = Path(__file__).with_name("main.qml")

blender_qt.register_qml_space(
    tree_type=TREE_TYPE,
    label="My QML Space",
    qml_path=QML_PATH,
    icon="NODETREE",
    sidebar_category="My Tools",
)

Unregister a custom space

Use unregister_space() with the same tree_type.

blender_qt.unregister_space("MY_ADDON_QML_NT")

Register a custom widget space

Use register_widget_space() when your UI is a QWidget tree.

from PySide6 import QtWidgets
import blender_qt


class MyWidgetSurface(QtWidgets.QWidget):
    def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
        super().__init__(parent)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(QtWidgets.QLabel("Hello from a widget space"))


blender_qt.register_widget_space(
    tree_type="MY_ADDON_WIDGET_NT",
    label="My Widget Space",
    widget_factory=MyWidgetSurface,
    icon="NODETREE",
    sidebar_category="My Tools",
)

Unregister it the same way:

blender_qt.unregister_space("MY_ADDON_WIDGET_NT")

List active custom spaces:

tree_types = blender_qt.registered_space_types()

Register a QML viewport gizmo

Use register_qml_viewport_gizmo() to render a floating viewport panel from QML.

from pathlib import Path

import blender_qt

blender_qt.register_qml_viewport_gizmo(
    gizmo_id="MY_ADDON_QML_GIZMO",
    qml_path=Path(__file__).with_name("gizmo.qml"),
    space_type="VIEW_3D",
    width=320,
    height=220,
    is_2d=True,
    initial_position=(80, 80),
)

3D QML gizmo

Set is_2d=False to anchor the panel in world space.

blender_qt.register_qml_viewport_gizmo(
    gizmo_id="MY_ADDON_QML_GIZMO_3D",
    qml_path=Path(__file__).with_name("gizmo.qml"),
    space_type="VIEW_3D",
    width=320,
    height=220,
    is_2d=False,
    initial_3d_position=(0.0, 0.0, 1.0),
    billboard=True,
    use_depth_test=True,
)

Register a widget viewport gizmo

Use register_widget_viewport_gizmo() to render a floating viewport panel from a QWidget.

from PySide6 import QtWidgets
import blender_qt


class MyViewportPanel(QtWidgets.QWidget):
    def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
        super().__init__(parent)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(QtWidgets.QPushButton("Action"))


blender_qt.register_widget_viewport_gizmo(
    gizmo_id="MY_ADDON_WIDGET_GIZMO",
    widget_factory=MyViewportPanel,
    label="My Widget Gizmo",
    space_type="VIEW_3D",
    width=280,
    height=180,
    is_2d=True,
    initial_position=(120, 120),
)

Unregister a viewport gizmo

Use unregister_viewport_gizmo() with the same gizmo_id.

blender_qt.unregister_viewport_gizmo("MY_ADDON_WIDGET_GIZMO")

List active gizmos:

gizmo_ids = blender_qt.registered_viewport_gizmo_ids()

Managed windows

Use register_window() for standalone top-level windows.

from PySide6 import QtWidgets
import blender_qt

window = QtWidgets.QDialog()
window.setWindowTitle("My Tool")
blender_qt.register_window(window, unique=True)

Optional close callback:

def _on_window_closed(widget: QtWidgets.QWidget) -> None:
    print("Closed", widget.objectName())


blender_qt.register_window(window, unique=True, on_close=_on_window_closed)

Get active managed windows:

windows = blender_qt.managed_windows()

Close them all:

blender_qt.close_managed_windows()

Recommended add-on structure

import blender_qt


def register() -> None:
    blender_qt.init()
    blender_qt.register_qml_space(
        tree_type="MY_ADDON_QML_NT",
        label="My Space",
        qml_path="/path/to/main.qml",
    )
    blender_qt.register_qml_viewport_gizmo(
        gizmo_id="MY_ADDON_GIZMO",
        qml_path="/path/to/gizmo.qml",
        is_2d=True,
    )


def unregister() -> None:
    blender_qt.unregister_viewport_gizmo("MY_ADDON_GIZMO")
    blender_qt.unregister_space("MY_ADDON_QML_NT")
    blender_qt.shutdown()

Notes

  • tree_type and gizmo_id must be unique.
  • Registering a duplicate space or gizmo raises ValueError.
  • Missing QML files raise FileNotFoundError during registration.
  • QML viewport gizmos use is_2d instead of mode.
  • QML viewport gizmos do not use a label argument.
  • Widget viewport gizmos still accept label, which is used as the host window title.
  • Use VIEW_3D, IMAGE_EDITOR, NODE_EDITOR, CLIP_EDITOR, or SEQUENCE_EDITOR for space_type where supported by Blender.

Project Layout

  • blender_qt: importable package
  • docs: contributor documentation
  • tests: standalone smoke tests for package metadata and public API imports

Releases

GitHub Actions runs tests and build validation on pushes and pull requests, and publishes tagged releases to PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

blender_qt-0.4.0.tar.gz (76.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

blender_qt-0.4.0-py3-none-any.whl (94.1 kB view details)

Uploaded Python 3

File details

Details for the file blender_qt-0.4.0.tar.gz.

File metadata

  • Download URL: blender_qt-0.4.0.tar.gz
  • Upload date:
  • Size: 76.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for blender_qt-0.4.0.tar.gz
Algorithm Hash digest
SHA256 6d296c43a632599ebd70eea5223abc7d7e46c5777ea1eb38fad9fc9f0e2a7fa1
MD5 682bb6c150019887b7bf815d9997996d
BLAKE2b-256 697e3b0247497fc139ddc318f6686bfd97f8a95238d842d6b2d2de28b35f8950

See more details on using hashes here.

File details

Details for the file blender_qt-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: blender_qt-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 94.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for blender_qt-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 46f601a6e49ec77937737227a181f9a5a5ee04a13563d094f348d635a1e6b3d0
MD5 30d73378dfa5529c562d50c08e21e3a5
BLAKE2b-256 5b35b9744d9df3b4e3d01157f4f551dd6729e84ba806b02c660f9d5ae2e003ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page