BUI (Blender UI)
BUI is the user interface of Blender, extracted into a standalone, fast, C-first UI library. You get Blender's look and Blender's interactions, scrubbable number fields, drag-toggles, pie menus, the color wheel, Ctrl+Z on any edit and you can drive it three ways:
- tkinter syntax: run existing tkinter programs unchanged, now looking like Blender.
- Qt syntax: write
QWidget/QVBoxLayout/clicked.connectcode (a common subset today). - Blender components: a native component library (
Panel,NumberSlider,ColorSwatch,TreeView,PieMenu...).
All three share one C runtime (kit/). Python builds the widget tree and
runs your callbacks. Layout, input, drawing and presenting stay in C.
pip install blenderui # PyPI name (packaging is ready, first release pending)
Left: the component library (examples/components_demo.py).
Right: an unmodified tkinter program (examples/tk_unchanged_app.py) running on BUI.
Contents
- Why BUI
- Status
- Install and build
- Quick start
- How it works
- Performance
- Roadmap: more languages and platforms
- Packaging
- Documentation
- Repository layout
- Testing
- License
Why BUI
| Blender's real UI code | Widget drawing is a C port of Blender's interface_widgets.cc (round_box_edges, widgetbase_draw, widget_state, the shape tables). Color math, rects and text cursors are Blender's blenlib, linked. The HSV wheel uses Blender's own functions. |
| Blender's interactions | Drag number fields with a hidden, continuous cursor (Shift is precise, Ctrl snaps). Ctrl+Wheel steps values, Ctrl+C/V copies and pastes them, Backspace resets. Drag across checkboxes to set them all. Press-drag-release picks menu items, and typing in a menu searches it. Ctrl+Z / Ctrl+Shift+Z undoes UI edits. Tab moves between fields. Drag vertically over number fields to edit several at once. Panels collapse with an animation. |
| Familiar APIs | Use the tkinter or Qt code you already know, or the component library. |
| Fast | Rendering is damage-driven: an idle UI draws nothing, and a hover or scrub redraws only the widget it touches. A full window renders in under a millisecond (see Performance). |
| Small dependencies | C11 plus a CPython extension. No Qt or Tk widgets are used to draw anything. |
Status
| Part | State | Details |
|---|---|---|
C kit (kit/) |
✅ Working | Nodes, Tk-style geometry managers, input, Blender widget drawing, damage rendering, undo, animation, text area, canvas, scroll views, multi-select |
| Native window (Windows) | ✅ Working | Win32 window in C: DPI-aware, vsync'd animation, clipboard, cursors, window-manager features |
| Other platforms | ⚠️ Fallback | The window is hosted by the stdlib tkinter, which draws the frame buffer; the UI is identical. Linux and macOS are not regularly tested. |
tkinter syntax (bui.tksyntax) |
✅ Complete API | Every public name of every tkinter and ttk widget class exists (7846/7846). Nine real tkinter programs behave identically on the stdlib and on BUI. |
Qt syntax (bui.qtsyntax) |
🟡 Partial (~25%) | The common subset works: widgets, layouts, signals, QPainter, menus, timers. Model/view, docks, toolbars, splitters and stylesheets are missing. Details. |
Component library (bui.components) |
✅ Working | 45 Blender components, including editors (color ramp, curves) and templates |
| C++ / Qt render-sink backends | ✅ Working | For embedding BUI drawing into your own renderer (INTEGRATION.md) |
Packaging (pip install blenderui) |
🟡 Scaffolded | pyproject.toml (scikit-build-core), CI wheels for Windows, Linux and macOS, trusted publishing. A locally built wheel passes all suites. First PyPI release pending. (docs/publishing.md) |
| Rust, egui, WebAssembly / React, more | 🗺️ Planned | See the roadmap |
Install and build
From PyPI (once released)
pip install blenderui
import blenderui.tksyntax as tk # tkinter syntax
from blenderui.components import App # Blender components
from blenderui.qtsyntax import QWidget # Qt syntax
blenderui and bui are the same package (blenderui.X is bui.X);
the examples below use bui because that's how the source tree is
organised. Use blenderui in published code: the bui name on PyPI
belongs to an unrelated project. A console script comes with it:
blenderui-tk app.py runs a tkinter app on BUI.
From source
Requirements: CMake ≥ 3.20, a C11/C++17 compiler (GCC / MinGW or Clang; MSVC is untested), and Python 3.11+ with development headers.
git clone <this repository> BUI
cd BUI
cmake -S . -B build
cmake --build build
ctest --test-dir build # C + Python test suites
The Python package lives in bindings/python/bui. Its compiled extension
(buipy) stays in the build tree, and import bui finds it there
automatically. Put bindings/python on PYTHONPATH and you are done:
export PYTHONPATH=$PWD/bindings/python # Windows: set PYTHONPATH=%CD%\bindings\python
python examples/components_demo.py
More detail, including environment variables and troubleshooting: docs/getting-started.md.
Quick start
tkinter syntax
Run any tkinter program on BUI without changing it:
python -m bui.tksyntax examples/tk_unchanged_app.py
Or opt in from code, before anything imports tkinter:
import bui.tksyntax
bui.tksyntax.install() # `import tkinter` now returns BUI's tkinter
import tkinter as tk
from tkinter import ttk, messagebox
root = tk.Tk()
root.title("Hello")
name = tk.StringVar(value="Cube")
ttk.Entry(root, textvariable=name).pack(fill="x", padx=8, pady=8)
ttk.Button(root, text="Greet",
command=lambda: messagebox.showinfo("Hi", "Hello " + name.get())).pack()
root.mainloop()
Or import it directly, under its own name:
import bui.tksyntax as tk
from bui.tksyntax import ttk
Everything is supported, including ttk, messagebox, filedialog,
simpledialog, colorchooser, font and scrolledtext. That covers
pack/grid/place, scrollbars, Canvas (tags, create_window,
scroll regions), Text (indices, tags, marks, search, undo), Treeview,
bindings, variables and traces, after, and window-manager calls.
Full guide: docs/tkinter.md.
Qt syntax
from bui.qtsyntax import (QApplication, QWidget, QVBoxLayout, QFormLayout,
QLineEdit, QSlider, QPushButton, QLabel)
app = QApplication([])
window = QWidget()
window.setWindowTitle("Qt syntax on BUI")
layout = QVBoxLayout(window)
form = QFormLayout()
name = QLineEdit()
name.setPlaceholderText("type here…")
level = QSlider()
level.setRange(0, 100)
form.addRow("Name:", name)
form.addRow("Level:", level)
layout.addLayout(form)
status = QLabel("Press the button.")
button = QPushButton("Click me")
button.clicked.connect(lambda: status.setText("Hello " + (name.text() or "BUI")))
layout.addWidget(status)
layout.addWidget(button)
window.show()
app.exec()
Supported classes, signals, layouts and the known gaps: docs/qt.md.
Blender components
from bui.components import (App, Panel, NumberSlider, VectorField, EnumRow,
Checkbox, ColorSwatch, Button)
App("Object", (420, 520),
Panel("Transform",
VectorField("Location", (0.0, 1.25, 0.0)),
NumberSlider("Scale", 1.0, min=0, max=10, on_change=print)),
Panel("Shading",
EnumRow("Mode", ["Solid", "Material", "Rendered"]),
Checkbox("Shadows", True),
ColorSwatch("Color", (0.8, 0.3, 0.1, 1.0))),
Button("Apply", on_click=lambda: print("apply")),
).run()
Component reference and Blender interaction details: docs/components.md.
How it works
your code: tkinter syntax Qt syntax bui.components
│ │ │
└──────── Python: options, signals, variables, callbacks
│
C kit (kit/): node tree ─ geometry (pack/grid/place) ─ input & interaction
─ Blender widget drawing ─ damage-driven raster rendering
│
window host: native Win32 window (Windows) │ stdlib Tk window (elsewhere)
- One node tree. Every widget wraps one kit node:
button,number,slider,text,textarea,list,tree,canvas,scrollbarand so on. The C kit owns layout, hit testing, hover, focus, drag grabs, menus, tooltips, undo and animation. - Tk's geometry managers in C.
packandgridare ports of Tk's own packer and gridder. Qt's box, grid and form layouts are mapped onto them. - Damage rendering. A state change marks only its widget's rectangle. A frame redraws just those regions into a frame buffer (anti-aliased SDF shapes and a cached native text engine). Only the changed tiles are presented to the window.
- Python stays thin. Facades keep what tkinter and Qt keep in their own scripting layers: option values, variables, traces, signals and bindings.
More: ARCHITECTURE.md.
Performance
Measured on the full component demo (520×900) on a desktop CPU:
| Frame | Time |
|---|---|
| Complete frame (everything redrawn) | ~0.7 ms |
| Hover or number-scrub frame (damage only) | ~0.05 ms |
| Native window: input event → render → on screen | ~0.06 ms |
| Tk-hosted window, the same path | ~0.18 ms |
The test suite enforces a 120 Hz budget: full frames under 8.3 ms, and interaction frames under 1 ms.
Roadmap: more languages and platforms
BUI's core is plain C with a small, stable surface (kit/include/BUI_kit.h
for widgets, api/bui_c_api.h for render sinks). Its output is either a
frame buffer (BuiRaster, ARGB pixels plus damage rectangles) or a stream
of draw primitives. That makes every target below the same job: bind the C
API, feed in input events, and present the pixels or primitives. None of
them needs Python.
| Target | Plan | Status |
|---|---|---|
| Python: tkinter, Qt, components | bui.tksyntax, bui.qtsyntax, bui.components |
✅ Done (Qt partial) |
| C / C++ | BUI_kit.h, render sinks, Qt/QPainter backend |
✅ Done |
| Rust | blenderui-sys (bindgen over BUI_kit.h) and a safe blenderui crate: an owned node tree, closures for callbacks, winit windows |
🗺️ Planned |
| egui | Two ways in: draw a BUI panel inside egui (raster to an egui::TextureHandle, with input forwarded), and a Blender-styled egui::Visuals theme |
🗺️ Planned |
| WebAssembly | Compile the kit with Emscripten. The raster blits to a <canvas> (putImageData of the damage rectangles only), and DOM pointer and keyboard events go to BK_press / BK_key |
🗺️ Planned |
| React | @blenderui/react on top of the WASM build: a reconciler that maps JSX to kit nodes (<Panel>, <NumberSlider> ...) |
🗺️ Planned |
| JavaScript / TypeScript | @blenderui/web (the plain WASM API), and Node through N-API |
🗺️ Planned |
| Tauri / Electron | The WASM build in the webview, or the native kit through the host language | 🗺️ Planned |
| C# / .NET | P/Invoke over BUI_kit.h (WinForms / WPF / Avalonia hosts) |
🗺️ Planned |
| Go, Zig, Swift, Kotlin/JVM, Lua | cgo / @cImport / Swift C interop / Panama FFI / LuaJIT FFI over the same header |
🗺️ Planned |
| Dear ImGui | A backend that shows BUI panels in an ImGui window, plus a Blender-styled ImGui theme | 🗺️ Planned |
| Game engines: Godot, Unity, Bevy | Editor-style tool panels: GDExtension, a native Unity plugin, a Bevy plugin through the Rust crate | 🗺️ Planned |
| Flutter | dart:ffi bindings, with the frame buffer shown as a Texture |
🗺️ Planned |
| Native windows on Linux / macOS | Wayland / X11 and Cocoa hosts in C, like the Win32 one (today the stdlib Tk window hosts them) | 🗺️ Planned |
| GPU renderers | OpenGL / Vulkan / Metal / WebGPU render sinks for very large or high-DPI UIs (today a SIMD-friendly CPU raster, under 1 ms per frame) | 🗺️ Planned |
Intended API sketches (planned, not implemented yet)
Rust:
use blenderui::{App, Panel, NumberSlider, Checkbox, Button};
fn main() {
App::new("Tool", (360, 260))
.child(Panel::new("Object")
.child(NumberSlider::new("Size", 1.0).range(0.0, 10.0)
.on_change(|v| println!("size = {v}")))
.child(Checkbox::new("Smooth", true))
.child(Button::new("Apply").on_click(|| println!("apply"))))
.run();
}
egui:
egui::Window::new("Properties").show(ctx, |ui| {
blenderui_egui::panel(ui, &mut state.bui_panel); // a BUI subtree inside egui
});
React (WebAssembly):
import { BlenderUI, Panel, NumberSlider, Checkbox } from "@blenderui/react";
export default function Properties() {
const [size, setSize] = useState(1);
return (
<BlenderUI width={360} height={260}>
<Panel title="Object">
<NumberSlider label="Size" value={size} min={0} max={10} onChange={setSize} />
<Checkbox label="Smooth" defaultChecked />
</Panel>
</BlenderUI>
);
}
Work order: the Rust crate first (it also unlocks egui and Bevy), then the WebAssembly build (it unlocks React, JS/TS, Tauri and Electron), then native Linux / macOS windows.
Packaging
The project is packaged as blenderui for PyPI: pyproject.toml with
scikit-build-core, which drives the CMake build. One wheel contains the
buipy extension plus the bui and blenderui packages.
python -m pip install build
python -m build # dist/blenderui-<version>.tar.gz and a wheel
- Version: a single source,
bindings/python/bui/_version.py, read by CMake and by the packaging. - CI:
.github/workflows/wheels.ymlrunscibuildwheelfor CPython 3.11–3.14 on Windows (MinGW), Linux (manylinux) and macOS (arm64 and x86_64), plus an sdist and the full ctest run. Pushing av*tag publishes to PyPI with trusted publishing (no tokens stored). - Verified locally: a wheel built on Windows installs into a clean virtualenv and passes the component, syntax, render, tkinter-compat and input suites.
- Release steps and one-time PyPI setup: docs/publishing.md. Changes are recorded in CHANGELOG.md.
Documentation
| Guide | What's inside |
|---|---|
| docs/getting-started.md | Building, PYTHONPATH, environment variables, choosing the window host, troubleshooting |
| docs/tkinter.md | Running tkinter apps on BUI, module map, behaviour notes, the Blender look, limitations |
| docs/qt.md | Qt-syntax guide: classes, signals, layouts, painting, timers, coverage and gaps |
| docs/components.md | bui.components reference: layout, values, views, menus, color, editors, App |
| docs/c-api.md | The C kit (BUI_kit.h): nodes, properties, events, windows |
| docs/publishing.md | Packaging as blenderui, building wheels, CI, releasing to PyPI |
| CHANGELOG.md | Release notes |
| SYNTAX_COVERAGE.md | Member-by-member coverage tables for both syntax layers |
| INTEGRATION.md | Embedding BUI drawing in C, C++, Qt or PySide hosts through render sinks |
| ARCHITECTURE.md | Modules, dependency rules, what was taken from Blender and how |
Examples (examples/):
| File | Shows |
|---|---|
tk_unchanged_app.py |
A plain tkinter app: menus, notebook, form, table, text, canvas |
tk_syntax_demo.py |
tkinter syntax laid out like a Blender properties editor |
qt_syntax_demo.py |
Qt syntax: layouts, form widgets, signals |
components_demo.py |
Every Blender component, in a properties-editor layout |
kit_hello.c |
The C kit from C: a slider and a button in a native window |
python_demo.py, svg_demo.c |
Low-level drawing into an SVG render sink |
Repository layout
| Path | Contents |
|---|---|
kit/ |
The C runtime: kit_core.c (nodes, input, rendering, undo, views), kit_draw.c (theme and the interface_widgets.cc port), kit_widgets.c, kit_views.c (lists, trees), kit_menus.c, kit_color.c, kit_frame.c (geometry managers, canvas), kit_textarea.c, kit_window.c (Win32 host). Public API: kit/include/BUI_kit.h |
gpu/ |
Drawing: software raster (gpu_raster.c), render-sink dispatch |
core/, types/ |
Blender's blenlib subset (C11) and data types |
ui/, compat/ |
Blender's original interface sources and the seams replacing Blender's application APIs |
api/ |
Stable C ABI for render-sink hosts (bui_c_api.h) |
bindings/python/ |
buipy extension (src/) and the bui package: tksyntax/, qtsyntax.py, components/, host.py |
backends/qt/ |
C++ Qt/QPainter render-sink backend |
bindings/python/blenderui/ |
The blenderui import alias (the PyPI name) |
pyproject.toml, .github/workflows/ |
PyPI packaging (scikit-build-core) and the wheel and release CI |
examples/, tools/ |
Demos, the icon generator, the docs screenshot generator |
Testing
ctest --test-dir build --output-on-failure
| Suite | Covers |
|---|---|
core |
blenlib subset |
buipy, buipy_render |
Raster primitives, damage tracking, the native window against the frame buffer |
buipy_components |
Every component, Blender interactions, undo, multi-drag, animation, 120 Hz budgets |
buipy_syntax, buipy_advanced |
Qt and tkinter facades: layouts, painting, menus, dialogs, images |
buipy_tk_compat |
Nine tkinter programs run on stdlib tkinter and BUI; results must match |
buipy_tk_input |
tkinter widgets driven by real pointer and keyboard input through the kit |
License
Derived from Blender sources: GPL-2.0-or-later (see LICENSE and the SPDX
headers kept in every file).
Metadata
Release files for blenderui 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| blenderui-0.1.0.tar.gz | 1.7 MB | Without provenance | Details for blenderui-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| blenderui-0.1.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details for blenderui-0.1.0-cp312-cp312-win_amd64.whl |
Total release size: 2.1 MB
Release files / blenderui-0.1.0.tar.gz
| Download URL | blenderui-0.1.0.tar.gz |
|---|---|
| Size | 1.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d91b02a9ca3373a1f75ee3ef69ed56ed6095d33c4433a83ce6edb869036f7ea8
|
|
BLAKE2b-256 checksum How to use checksums |
9016de800d0feefd835a43abaaf72b4977f976236cec1e3a60cadc6ee80d46f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / blenderui-0.1.0-cp312-cp312-win_amd64.whl
| Download URL | blenderui-0.1.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 374.0 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
969116e3a0cebd9618e80402e945e17f42760a5920b9d03b1a4f74956895f225
|
|
BLAKE2b-256 checksum How to use checksums |
671a22144ece05380f13a7058d8fd2be02851b93d8230a60cfdf1d12302d5cf1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for blenderui 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.