跳到正文
Hacker News-首页· ppmpreetham·· 1 小时前精选AI 评分86

Blender UI and Runtime (BUI) 项目发布:速度远超 GPUI 的跨语言独立 UI 库

Blender's UI and Runtime: Much Faster Than GPUI

AI 导读

开源项目 BUI 将 Blender 的用户界面和运行时解耦并转化为独立的跨语言 UI 库,支持 Python、Rust、JavaScript、C 和 C++ 等多种语言及框架。该项目采用带有小 C 核心的高效运行时,支持 CPU 软件渲染与 GPU 渲染,提供类似 Blender 的组件与交互体验。

正文 · AI 翻译

译文尚不完整,完整内容请切换到原文。

BUI Logo

Blender 的用户界面,作为一个可从 Python、Rust、JavaScript/React、C、C++ 和 Dear ImGui 调用的库。

人们花了几十年时间构建和优化 Blender 的 GUI。它节能、省内存,而且速度超级快。我们将它解耦,让每个人都有机会用它构建自己的产品。它支持多种语言和框架,让你得心应手!

PyPI Python crates.io @blender-ui/react Wheels License: GPL-2.0-or-later Platforms

网站与完整文档:bui.preetham.top

BUI's Blender components: panels, number sliders, vector fields, checkboxes and a color swatch An unmodified tkinter program running on BUI

本 README 就像一本小册子。从头到尾阅读以了解完整内容,或者跳转到你需要的章节。当有更多内容要说时,每个章节都会链接到 docs/ 或 网站 上的更详细指南。

目录

第一部分:欢迎

  1. 什么是 BUI
  2. 为什么存在 BUI
  3. 它适合谁
  4. 多种接入方式
  5. 特性一览
  6. 图库

第二部分:快速入门

  1. 安装
  2. 第一个应用:tkinter
  3. 第一个应用:Blender 组件
  4. 第一个应用:Qt 语法
  5. 第一个应用:Rust(采用 rsx 和 gpui 构建器)
  6. 第一个应用:JavaScript 和 React
  7. 第一个应用:Dear ImGui
  8. 第一个应用:C

第三部分:核心概念

  1. 工作原理
  2. Blender 的控件与交互
  3. 三种外观:HTML、带样式的和 Blender 组件
  4. CPU 与 GPU 渲染
  5. 脏渲染 (Damage rendering)
  6. 窗口宿主
  7. 文本与字体
  8. 主题:深色与浅色

第四部分:按语言分类的指南

  1. Python
  2. Rust
  3. JavaScript 和 React
  4. Dear ImGui
  5. C 与嵌入
  6. 文档存放位置

第五部分:性能

  1. 在阅读数据之前
  2. 启动时间与电量
  3. 帧时间
  4. JavaScript 和 React:启动与动画
  5. 自行运行基准测试

第六部分:参考手册

  1. 平台
  2. 环境变量
  3. 从源码构建与 CMake 选项
  4. 覆盖率
  5. 仓库布局
  6. 示例

第七部分:项目

  1. 状态与路线图
  2. 常见问题解答
  3. 贡献与发布
  4. 许可与致谢

第一部分:欢迎

1. 什么是 BUI

BUI 采用了 Blender 的用户界面(其控件绘制、交互规则和外观),并将其转化为一个拥有小型 C 核心的独立 UI 库。你的代码负责构建控件树并处理回调。布局、输入、绘制和呈现均通过快速的原生代码在 CPU 或 GPU 上进行。

BUI 的核心是 kit:一个用 C 语言编写的小型运行时。kit 将所有控件保存在一棵树中,对它们进行布局,处理鼠标和键盘事件,并按照 Blender 的方式绘制它们。绘制代码是 Blender 自身控件代码的 C 语言移植版本,交互则遵循 Blender 的规则。在 kit 之上是 bindings(绑定):它们是轻量级层,允许 Python、Rust、JavaScript、C++ 和 Dear ImGui 与其进行通信。

BUI 不是什么:

  • 不是 Blender。它包含所需的 Blender UI 代码部分并独立构建。你不需要安装 Blender,也不需要懂 Blender 就能使用它。
  • 不是浏览器。Rust 和 JavaScript 端使用 HTML 和 CSS 作为 UI 语言,但这里没有浏览器引擎、没有 webview,也没有网络。
  • 不是重新构想。小部件绘制移植自 Blender 的 interface_widgets.cc,而对 Dear ImGui 的支持运行的是真正的 Dear ImGui。

2. 为什么会有 BUI

Blender 的界面以其手感而闻名。你按下一个数字输入框并拖动:光标消失,数值在你的手下平滑滑动,且永远不会触及屏幕边缘。你在输入框中输入 2*pi,它就能正常工作。你在菜单中输入 "ad cu",它就能找到 "Add Cube"。数百万艺术家双手都习惯了这些操作。

而且,这个界面既快速又节省资源。它只重绘发生变化的部分,在没有变化时休眠,并在毫秒级内启动。BUI 的存在是为了让任何语言编写的任何应用,都能同时拥有两者的优势:Blender 的手感和 Blender 的效率。

3. 适用人群

  • 你有一个 tkinter 应用,希望它看起来不那么像 1998 年的产物。在 BUI 上运行它,无需修改。
  • 你正在为艺术家构建工具,而他们已经熟悉 Blender。为他们提供双手期望的面版、滑块和快捷键。
  • 你编写 Rust 或 React,想要一个原生桌面 UI,而无需打包浏览器。
  • 你使用 Dear ImGui,希望它通过一个构建标志就能呈现出 Blender 的外观和手感。
  • 你很好奇 UI 工具包是如何工作的。其源码清晰易读,概念也用通俗易懂的语言进行了阐述。

4. 多种接入方式

你可以通过多种方式驱动 BUI,它们都共享相同的 C 运行时,因此它们都拥有相同的小部件和交互:

你编写 你得到 状态
tkinter 代码,甚至是现有程序,无需修改 Blender 风格的应用;每个 tkinter 和 ttk API 完整
Qt 风格的 Python(QWidget、布局、clicked.connect) Qt 小部件的共同子集 部分
Blender 组件(Panel、NumberSlider、PieMenu、节点编辑器……) 45 个来自 Python 的原生 Blender 组件和编辑器;其中十个也支持来自 Rust 和 JavaScript 完整
Rust:HTML + CSS + Tailwind、类 HTML 标记(rsx! / view!)、钩子(hooks),或 gpui / zopra 语法 一个类网页的框架:看起来像浏览器那样的 HTML、带有 styled 的 Blender 外观,以及 Blender 组件(<NumberSlider>、<Button>……) 运行中
基于 Node.js 或 Bun 的 JavaScript / React:类 DOM 的 API 或 JSX 通过 napi-rs 在原生窗口中运行相同的引擎和组件;由信号(signals)支持的 React 钩子 运行中
来自 C++、C、Python 或 Rust 的 Dear ImGui 在 Blender 主题的 BUI 窗口上运行真正的 Dear ImGui,或者 ImGui 的小部件*就是* Blender 小部件的构建版本 运行中
C(BUI_kit.h) 工具包本身:节点、小部件、窗口 运行中
MyGo MyGo 尚未支持(即将推出)

不知道该选哪个?如果你已有代码,请使用与之匹配的那一行。如果你准备用 Python 从头开始,Blender 组件是最友好的。

5. 特性一览

Blender 的小部件与交互

  • 你可以通过带有隐藏的连续光标的拖动来操作数字输入框:按住 Shift 可进行精确调节,按住 Ctrl 可进行对齐。
  • 数字输入框中的数学计算,带有 Blender 自己的表达式求值器:2*pi、min(4, 3)、round(1.7)、x if c else y。
  • Ctrl+滚轮可调整数值步长,Ctrl+C / Ctrl+V 可复制/粘贴数值,Backspace 可重置。
  • 跨复选框拖动可将它们全部设置。
  • 按住-拖动-释放菜单、饼图菜单和 Blender 的模糊菜单搜索:“ad cu”可找到“Add Cube”,容忍拼写错误,最近选择的项目排在最前面。
  • 每次 UI 编辑时均支持 Ctrl+Z / Ctrl+Shift+Z,在输入时的文本框内也同样支持;可通过 Tab 键在字段之间切换。
  • Blender 的浮动显示精度、多字段垂直拖动,以及随 Blender 动画折叠的面板。
  • HSV 色彩圆盘、色阶、曲线、树状图、列表、文本区域、画布、节点编辑器以及 Blender 的文件浏览器。
  • Blender 的深色和浅色主题,可在运行时切换。

Python,四种方式

  • tkinter:tkinter 和 ttk 的全部 7,846 个公共名称。九个真实的程序在标准库和 BUI 上表现完全相同。
  • Qt 语法:部件、布局、信号、QPainter、菜单和定时器。
  • 组件:45 个 Blender 部件、编辑器和模板。
  • Dear ImGui:from blenderui import imgui,pyimgui 风格。
  • 一个 pip install,没有任何 Qt 或 Tk 部件在绘制。

Rust:原生网络平台

  • 全部 113 个 HTML 元素,像浏览器一样进行绘制;styled 使任何子树都具有 Blender 的外观,而 Blender 组件(<NumberSlider>、<Button>、<ColorSwatch>……)则是真正的 Blender 部件。
  • 现代 CSS:flexbox、带命名区域的网格、表格、calc() 和所有数学函数、var()、oklch() 和 color-mix()、:has()、嵌套、@layer、@media 范围、@container、::before/::after、计数器。
  • 效果与动态:@keyframes 动画和过渡、滤镜、backdrop-filter、混合模式、clip-path、遮罩、2D 变换、border-image。
  • Tailwind CSS v4:class="md:hover:bg-sky-500/50" 或裸属性(<div p-4 bg-[#4772b3]>),带有 “您是指”编译错误。
  • 类似 HTML 的标记 rsx! / view!、#[component],以及 zopra、React 或 Leptos 风格的钩子(use_state、signal、Memo、Effect、context)。
  • 同样支持 gpui 兼容语法:div().flex().gap_2()、Render、cx.listener。
  • 在 crates.io 上:cargo add blenderui。

生来迅捷

  • 局部渲染(Damage rendering):闲置的 UI 消耗 0% 的 CPU,悬停时仅重绘其触及的部件。
  • 可选的 GPU 渲染器(Windows 上为 Direct3D 11,Linux 上为 OpenGL 3.3),仿照 Blender 的实例化部件绘制。它绘制与 CPU 光栅相同的像素。
  • 在基准测试中,每帧消耗的 CPU 比 gpui-fast 少 2–4 倍,内存占用更少,且首帧约为 5 毫秒。
  • Blender 文本:Inter(Blender 的 UI 字体)已嵌入并使用 FreeType 按照 Blender 的方式进行渲染,因此文本在每个平台上看起来都一样。在 GPU 上,字形使用 Slug 从其轮廓绘制,并回退到类似于 Blender 的逐字形图集。

图像

  • 每种语言一套解码器,用 C 语言编写:PNG、GIF、BMP、TGA、QOI(Wuffs,内存安全)、JPEG(libjpeg-turbo)、包含动画的 WebP(libwebp)、Radiance HDR、PNM 和 SVG。设计采用 Blender 的 imbuf:一种图像类型和一个格式表。
  • 按其绘制的大小进行解码,在 UI 线程之外进行:JPEG 缩略图使用 DCT 缩放(4K 照片在约 12 毫秒内变成 256 像素缩略图),可见图像优先,滚动移开的请求被取消,且图像到达时仅重绘其矩形区域。页面绝不会跳动:尺寸来自文件头。
  • 一个具有内存预算的缓存(默认 256 MiB)。滚动 1,000 张照片缩略图:p99 帧耗时 2.9 毫秒,内存 100 MB,而在 UI 线程上解码则为 96 毫秒和 2.5 GB;在稳定的帧中 0 次内存分配,每个块在退出时释放。图像。
  • 动态 GIF 和 WebP 可以在任何显示图片的地方播放,按绘制的尺寸逐帧解码(每帧动画保留两帧),每帧有各自的延迟,且仅重绘图片的矩形区域。当移出屏幕、滚动走远或在隐藏窗口中时,它们会暂停:同时播放 200 个动画时,渲染帧耗时仅为 0.76 毫秒 p50 / 1.5 毫秒 p99,移出屏幕后则完全不消耗性能。frame 保留单帧;减少动态效果(reduced motion)则会保留所有帧。
  • GPU 上的大图:超过 2047 像素的图片在 Direct3D 11 和 OpenGL 上会获得专属纹理(仅在超出设备限制时才会进行分块),并随图片释放。
  • <img>、CSS background-image、object-fit、Blender Image 组件、tkinter PhotoImage(现在还支持 JPEG、WebP、BMP、TGA、QOI、HDR、SVG)、画布图元、数据表格图片列、聊天列表媒体以及 Dear ImGui 纹理(也支持动态纹理)。

JavaScript 和 React

  • React 19.3 可渲染至 BUI:HTML 宿主组件、CSS 与 Tailwind、事件、Suspense、过渡、动作以及 Blender 组件(<NumberSlider>、<Button> 等)。BUI 在 CPU 或 GPU 上绘制它们;这里没有浏览器,也没有 webview。
  • React 的 Hook,采用 React 的名称,用于保存信号:将一个信号传给 JSX({count}),值的改变只会更新该节点而无需重新渲染;在渲染时读取它(count.value),组件则会像在 React 中一样重新渲染。有一套一致性测试套件会针对 React 原生的 Hook 对每个 Hook 进行检查。
  • @blender-ui/native:类似 DOM 的 API(createElement、appendChild、addEventListener),带有相同的信号,适用于没有 React 的应用。
  • Node.js 和 Bun,通过 napi-rs 插件提供支持,并带有用于测试的无头(headless)模式。

小型独立应用

  • bui build app.jsx 可生成一个约 7 MB 的可执行文件:包含内嵌 QuickJS 引擎的 BUI。
  • 首帧在约 70 毫秒内渲染完成,内存占用为 19 MB,无需安装任何东西。这里没有浏览器,也没有 webview。
  • --bun 则构建一个 Bun 可执行文件,包含 Node 的 API 和 JIT。
  • 构建过程只需不到一秒钟:应用的 bundle 会被附加到预构建的运行时中。

Dear ImGui

  • 真正的 Dear ImGui 1.93:imgui.h 的每个功能都可以正常工作,包括 ShowDemoWindow()。
  • 四种语言:C++(直接引入 imgui.h)、C(cimgui.h,全部 399 个函数)、Python 和 Rust(由 imgui.h 生成)。
  • Blender 的外观:采用 Inter 字体的 Blender 主题颜色和度量标准;或者通过 BUI_IMGUI_BLENDER_WIDGETS=ON 使用 Blender 真实的控件和交互,其像素级效果与 BUI 自身完全一致。
  • 闲置时零消耗:仅在接收到输入以及播放动画时才会运行帧。

原生支持所有平台

  • Windows(Win32)和 Linux(X11、通过 XWayland 的 Wayland)上的原生窗口,以及从 Blender 的 GHOST 移植而来的用于 macOS 的实验性 Cocoa 宿主。
  • HiDPI:Windows 上的按显示器 DPI、Linux 上的 Xft.dpi、macOS 上的 Retina。
  • 用于测试和截图的无头(headless)模式,带有能够精确步进动画的虚拟时钟。
  • 在 Linux 上,libX11 和 libGL 是在运行时动态加载的,因此 BUI 可以在没有它们的服务器上导入。

6. 图库

这里的每一张截图都是一个在后台离屏渲染的真实 BUI 窗口。如果一张图片 同时提供深色和浅色版本,GitHub 会显示与你的 主题相匹配的那一个。

A Geometry Nodes editor in BUI: seven nodes with typed sockets, colored links and header menus
来自 Python 的节点编辑器(examples/site/geometry_nodes.py)

A Shading pie menu open over a node editor The File menu with exp typed: Blender's fuzzy search through submenus
饼图菜单以及 Blender 的菜单搜索(在任意打开的菜单中输入即可)

Blender's color picker: HSV wheel, value strip, HSV sliders, hex field and eyedropper A tooltip on a Denoise dropdown
颜色选择器和工具提示

CSS effects: filters, clip-path shapes, transforms, blend modes and backdrop-filter on a gradient landscape
Rust 中的 CSS 效果:滤镜、clip-path、变换、混合模式、backdrop-filter(site_effects.rs)

A material library laid out with CSS grid: auto-fill columns, spans and dense packing
带有 repeat(auto-fill, minmax())、跨度(spans)和 grid-auto-flow: dense 的 CSS 网格(site_grid.rs)

Three moments of CSS keyframe animations: a spinner, a progress sweep, a bouncing ball and a fading toast
CSS @keyframes 在 0.25 s、0.6 s 和 1.0 s 时(site_animation.rs)

更多截图

The Rust showcase: a stylesheet, grid cards, Blender form widgets, a table and lists
examples/showcase.rs:Rust 中的 HTML + CSS

Dear ImGui's demo in Blender's theme, with ImGui's own widgets
Dear ImGui 示例及其自带组件,采用 Blender 主题

Qt syntax on BUI
Qt 语法

tkinter Text with tags tkinter Canvas tkinter messagebox
tkinter 程序:带标签的文本、画布、对话框


第二部分:快速入门

7. 安装

Python. 你需要 CPython 3.11 或更新版本。官方发布了适用于 Windows、Linux 和 macOS 的 Wheel 包,因此无需编译。一个包即可为你提供所有四个 Python API(tkinter 语法、Qt 语法、Blender 组件和 Dear ImGui):

pip install blenderui
python -c "import blenderui; print(blenderui.__version__)"

将其导入为 blenderui。你还会在较旧的示例中看到 import bui:它是原名称下的相同代码,但 PyPI 上的 bui 归属于一个无关的项目,因此请在你发布的任何内容中优先使用 blenderui。

Rust. 该 crate 为 crates.io 上的 blenderui:

cargo add blenderui

首次构建会为你编译 BUI 的 C 工具包,因此你需要 Rust 1.88 或更新版本、CMake 和 C 编译器(在 Windows 上,默认 MSVC 目标需要 Visual Studio Build Tools),在 Linux 上需要 X11 头文件(libx11-dev 或 libX11-devel;不需要 GL 头文件)。

JavaScript. 你需要 Node.js 18 或更新版本,或者 Bun:

npm install @blender-ui/react react     # or: bun add @blender-ui/react react
npm install @blender-ui/native          # the DOM-like API, without React

@blender-ui/react @blender-ui/native

npm 包是针对 Windows x64 预编译的。在 Linux 上,请通过克隆仓库构建插件(在 bindings/ 中先执行 npm install 然后执行 npm run build,需要 Rust、CMake 和 C 编译器);那里的窗口是原生的 X11 窗口。在 macOS 上,JavaScript 目前以无头模式运行(参见 平台)。

C 和 C++. 使用 CMake 3.20+ 和 C11 / C++20 编译器(MSVC、GCC、MinGW-w64 或 Clang)从源码构建;在 Linux 上还需要 X11 头文件:

git clone https://github.com/ppmpreetham/BUI && cd BUI
cmake -S . -B build
cmake --build build -j 2
ctest --test-dir build            # optional: run the test suites

在你自己的 CMake 项目中,将 BUI 添加为子目录并链接 bui::kit(或链接 Dear ImGui 的 bui::imgui):

add_subdirectory(BUI)
target_link_libraries(myapp PRIVATE bui::kit)

Dear ImGui. Python 通过 pip install blenderui(import blenderui.imgui)获取。C++ 链接 bui::imgui,C 链接 bui_cimgui。Rust 使用 blenderui-imgui crate:

cargo add blenderui-imgui

如果出现问题:

现象 解决方法
ImportError: buipy 你正在从源码检出目录中运行且未进行构建。请先构建(cmake --build build),或者设置 BUIPY_BUILD_DIR。扩展必须与你的 Python 版本匹配。
窗口从未出现 启动事件循环:mainloop()、app.exec() 或 App.run()。
UI 模糊或过小 设置 BUI_UI_SCALE,例如 BUI_UI_SCALE=1.25。
gpu=True 没有反应 使用 BUI_GPU_DEBUG=1 运行以查看 GPU 设备未启动的原因。如果没有 Direct3D 11 或 OpenGL 3.3,BUI 将停留在 CPU 渲染器上,它会绘制相同的像素。

更多信息:docs/getting-started.md 以及网站上的 安装 BUI。

8. 第一个应用:tkinter

在 BUI 上运行任何 tkinter 程序,一行代码都不用改:

blenderui-tk my_app.py          # same as: python -m bui.tksyntax my_app.py

blenderui-tk 会让 import tkinter 在该次运行中返回 BUI 的实现。或者在代码中显式启用:

import blenderui.tksyntax as tk
from blenderui.tksyntax import ttk, messagebox

root = tk.Tk()
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()

An unchanged tkinter program: a glTF export dialog with entries, checkbuttons, radio buttons, a spinbox, an option menu, a scale and a listbox An unchanged ttk program: a render-farm queue with a combobox, notebook, treeview and progress bar
未修改的 tkinter 和 ttk 程序(examples/site/tk_export.py、ttk_queue.py)

数字字段(Spinbox、Scale)支持像 Blender 那样的拖动调节,Ctrl+Z / Ctrl+Shift+Z 可用于撤销编辑,filedialog 打开 Blender 的文件浏览器。指南:docs/tkinter.md。

9. 第一个应用:Blender 组件

使用 Blender 自身的组件构建:

from blenderui.components import App, Panel, NumberSlider, VectorField, 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",
          Checkbox("Shadows", True),
          ColorSwatch("Color", (0.8, 0.3, 0.1, 1.0))),
    Button("Apply", on_click=lambda: print("apply")),
).run()

A Render Properties editor built from Blender components: menus, side tabs, panels with header checkboxes, number fields, sliders, dropdowns and a color swatch
来自组件的渲染属性编辑器(examples/site/render_props.py)

共有 45 个组件:布局、数值、按钮、视图、菜单与饼图菜单、颜色选择器、渐变色条、曲线和节点编辑器、区域与模板。参考文档:docs/components.md。

10. 第一个应用:Qt 语法

编写 Qt 风格的 Python 代码。导入语句是与 PySide 或 PyQt 唯一的区别,且两者皆不需要:

import sys
from blenderui.qtsyntax import (QApplication, QWidget, QVBoxLayout, QFormLayout,
                                QLineEdit, QComboBox, QSlider, QProgressBar,
                                QPushButton, QLabel, Qt)

app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("Export")
layout = QVBoxLayout(window)

form = QFormLayout()
kind = QComboBox()
kind.addItems(["glTF", "FBX", "OBJ"])
quality = QSlider(Qt.Horizontal)
quality.setRange(0, 100)
form.addRow("Format:", kind)
form.addRow("Quality:", quality)
layout.addLayout(form)

bar = QProgressBar()
quality.valueChanged.connect(bar.setValue)
layout.addWidget(bar)

status = QLabel("Ready.")
go = QPushButton("Export")
go.clicked.connect(lambda: status.setText("Exporting as " + kind.currentText()))
layout.addWidget(status)
layout.addWidget(go)

window.show()
sys.exit(app.exec())

A material editor in Qt syntax: a list, line edit, group box with a form layout, combo box, slider with spin box, checkbox and progress bar
Qt 语法(examples/site/qt_material.py)

Qt 语法是局部的(大约占主窗口类上 Qt 方法名的 24%);任何缺失的内容都会引发 AttributeError。指南: docs/qt.md。

11. 第一个应用:Rust,使用 rsx 和 gpui builders

BUI 的 Rust crate 有两种第一类语法,它们绘制相同的窗口:

  • rsx! 是宏内部类似 HTML 的标记,带有 #[component] 函数和用于状态的钩子。
  • gpui builders 是普通的方法链:div().flex().gap_3().child(...)。 状态存在于你的视图结构体中,并由 cx.listener 更新。gpui 代码也会针对 BUI 进行编译。

在 rsx! 中(这是 bindings/rust/blenderui/examples/hello.rs; 在 bindings/rust 中用 cargo run --release --example hello 运行它):

use blenderui::prelude::*;

#[component]
fn counter(label: &str) {
    let (count, set_count) = use_state(0);
    rsx! {
        <div flex items-center gap-3 p-4 rounded-xl bg-zinc-800>
            <span text-zinc-300>{label.to_string()} ": " {count().to_string()}</span>
            <div px-3 py-1 rounded-md bg-sky-700 hover:bg-sky-500 onClick={move || set_count(count() + 1)}>
                "Add one"
            </div>
        </div>
    }
}

struct Hello;

impl Render for Hello {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        rsx! {
            <body m-0 p-6 min-h-screen flex flex-col gap-4 bg-zinc-900 text-white>
                <h1 text-2xl font-bold>"Hello, BUI"</h1>
                <counter label="Clicks" />
                <NumberSlider min={0.0} max={10.0} value="5" />   // a real Blender slider
            </body>
        }
    }
}

fn main() {
    Application::new().run(|cx| {
        cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| Hello)).unwrap();
    });
}

在 gpui builders 中,相同的窗口:

use blenderui::prelude::*;

struct Hello { count: i32 }

impl Render for Hello {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let counter = div().flex().items_center().gap_3().p_4().rounded_xl().bg(rgb(0x27272a))
            .child(span().text_color(rgb(0xd4d4d8)).child(format!("Clicks: {}", self.count)))
            .child(div().px_3().py_1().rounded_md().bg(rgb(0x0369a1))
                .hover(|s| s.bg(rgb(0x0ea5e9))).cursor_pointer()
                .child("Add one")
                .on_click(cx.listener(|this, _, _, cx| {
                    this.count += 1;
                    cx.notify();
                })));
        body().m_0().p_6().css("min-height", "100vh").flex().flex_col().gap_4()
            .bg(rgb(0x18181b)).text_color(white())
            .child(h1().text_2xl().font_bold().child("Hello, BUI"))
            .child(counter)
            .child(NumberSlider().min(0.0).max(10.0).value("5"))   // a real Blender slider
    }
}

fn main() {
    Application::new().run(|cx| {
        cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| Hello { count: 0 })).unwrap();
    });
}

The Rust hello example: a heading, a counter component and a Blender slider

在 rsx! 中容易让人踩坑的两件小事:

  • 文本子级是文本(&'static str、String、SharedString)或元素。数字和借用的 &str 属性不是,所以请编写 {count().to_string()} 和 {label.to_string()}。
  • 组件的主体是以标记结尾的普通块,通常是一个 rsx!。

这是一个更大的例子,一个渲染农场仪表盘,分别用两种语法各写了一遍。 这两个程序绘制相同的像素:

A render-farm dashboard: a CSS grid of stat cards, job rows with Blender progress bars, a node list, a dropdown, button and checkbox
site_dashboard.rs(rsx!)和 site_dashboard_builder.rs(gpui builders)

指南:docs/rust.md,以及网站上的 Rust 教程。

12. 第一个应用:JavaScript 和 React

/** @jsxImportSource @blender-ui/react */
import { render, useState, NumberSlider } from "@blender-ui/react"

function Counter() {
  const [count, setCount] = useState(0) // React's hook, holding a signal
  return (
    <main styled className="p-6 flex flex-col gap-3">
      {" "}
      {/* `styled`: Blender's look */}
      <span>Clicks: {count}</span>
      <button onClick={() => setCount((c) => c + 1)}>Add one</button>
      <NumberSlider min={0} max={10} /> {/* a Blender component */}
    </main>
  )
}

render(<Counter />, { title: "Counter", width: 360, height: 200, gpu: true })
bun app.jsx                    # run it (Bun reads JSX directly)
npx bui build app.jsx          # or ship it: app.exe, one 7 MB file

没有 React,@blender-ui/native 会为你提供一个类似于 DOM 的 API:

const { App, signal } = require("@blender-ui/native")

const app = new App({ gpu: true })
const win = app.window({ title: "Hello", width: 360, height: 200 })
win.body.className = "m-0 p-6 flex flex-col gap-3 bg-[#2b2b2b] text-[#e6e6e6]"

const clicks = signal(0)
const label = win.createElement("span")
label.textContent = clicks // follows the signal
const button = win.createElement("button")
button.textContent = "Add one"
button.addEventListener("click", () => clicks.value++)
win.body.appendChild(label)
win.body.appendChild(button)

A task list in React on BUI: a Blender text field, button, checkboxes and a progress bar
React 中的任务列表(examples/site/js/todo_react.js);类似 DOM 的版本绘制出的内容是相同的

Node.js 18+ 或 Bun。预构建版本适用于 Windows x64;在 Linux 上,从源码构建插件(npm run build 在 bindings/node 中),窗口将是原生的。指南: docs/javascript.md。

13. 第一个应用:Dear ImGui

真正的 Dear ImGui 1.93 运行在 BUI 上:BUI 的窗口、输入、CPU/GPU 渲染器、Blender 的主题以及 Inter 字体。现有的 ImGui 代码无需修改即可编译。

C++:

#include "imgui.h"
#include "imgui_impl_bui.h"     // see examples/imgui_demo.cpp

static void frame(void*) {
    ImGui::ShowDemoWindow();
    ImGui::Begin("Hello");
    static float v = 0.5f;
    ImGui::SliderFloat("Value", &v, 0.0f, 1.0f);
    ImGui::End();
}

int main() {
    ImGui::CreateContext();
    ImGui_ImplBUI_App* app = ImGui_ImplBUI_CreateApp("Demo", 1280, 800, frame, nullptr);
    ImGui_ImplBUI_RunApp(app);
    ImGui_ImplBUI_DestroyApp(app);
    ImGui::DestroyContext();
}

Python:

import blenderui.imgui as imgui        # C: cimgui.h  ·  Rust: the blenderui-imgui crate

state = {"value": 0.5}

def frame():
    imgui.show_demo_window()
    imgui.begin("Hello")
    _, state["value"] = imgui.slider_float("Value", state["value"], 0.0, 1.0)
    imgui.end()

imgui.run(frame, title="Hello", size=(1280, 800))

C(cimgui.h,cimgui 惯例:ImGui_Button、ImGui_ButtonEx 以及 cimgui_impl_bui.h 中的后端)和 Rust(blenderui-imgui)的工作方式相同:

use blenderui_imgui as imgui;

fn main() {
    let mut value = 0.5f32;
    imgui::run("Hello", [1280, 800], move || {
        imgui::show_demo_window();
        imgui::begin("Hello");
        imgui::slider_float("Value", &mut value, 0.0, 1.0);
        imgui::end();
    });
}

使用 -DBUI_IMGUI_BLENDER_WIDGETS=ON(或 Rust 特性 blender-widgets)构建,ImGui 的构件就会变成 Blender 自己的构件:DragFloat 是 Blender 的拖动微调数字字段(按 Shift 精确调整,按 Ctrl 吸附),SliderFloat 是 Blender 的数字滑块,复选框可通过拖动切换,菜单和组合框会变成 Blender 的菜单。ImGui API 的其余部分保持不变。(PyPI wheel 在构建时未包含此功能,因此从 Python 使用时,除非你从源码构建,否则在 Blender 主题中获得的是 ImGui 自己的构件。)

Dear ImGui's demo window drawn with Blender's widgets
Dear ImGui 自己的演示,带有 BUI_IMGUI_BLENDER_WIDGETS=ON:数字字段、滑块、颜色行和面板标题均来自 Blender

A scene inspector in Dear ImGui with ImGui's own widgets and colors The same inspector with Blender widgets and Blender's style
相同的 ImGui 代码:ImGui 的构件(左)和 Blender 构件构建版本(右)(examples/site/imgui_inspector.cpp)

指南、覆盖率和原生外观:docs/imgui.md。

14. 第一个应用:C

该工具包的 API 只有一个头文件 kit/include/BUI_kit.h,它同时也是参考手册:每个函数都有一行注释。这是一个简短的 examples/kit_hello.c:原生窗口中的滑块和按钮。

#include <stdio.h>
#include "BUI_kit.h"

static void on_event(void *data, BKNode *node, int kind, void *ref, double a, double b)
{
  if (kind == BK_EV_CLICK) printf("clicked %s\n", BK_get_str(node, "text"));
  else if (kind == BK_EV_CHANGE) printf("%s = %g\n", BK_get_str(node, "label"), BK_get_num(node, "value"));
}

int main(void)
{
  BKNode *root = BK_node_new("frame");          /* everything is a node */
  BK_set_num(root, "padding", 8);               /* configured by property name */

  BKNode *size = BK_node_new("slider");
  BK_set_str(size, "label", "Size");
  BK_set_num(size, "max", 10);
  BK_set_num(size, "value", 2.5);
  BK_node_add(root, size, -1);
  BK_geom_set_manager(size, BK_GEOM_PACK);      /* Tk's packer, in C */
  BK_geom_set(size, "fill", 1);

  BKNode *apply = BK_node_new("button");
  BK_set_str(apply, "text", "Apply");
  BK_node_add(root, apply, -1);
  BK_geom_set_manager(apply, BK_GEOM_PACK);

  BKContext *ctx = BK_context_new(root);        /* input, focus, popups, undo */
  BKHost host = {0};
  host.emit = on_event;                         /* the window fills in the rest */
  BK_context_set_host(ctx, &host);

  if (BK_window_supported()) {                  /* Win32, X11 or Cocoa */
    BKWindow *win = BK_window_new(ctx, "BUI from C", 320, 120);
    BK_window_show(win);
    BK_window_run();
    BK_window_free(win);
  }
  BK_context_free(ctx);
  BK_node_free(root);
  return 0;
}
cmake -S . -B build -DBUI_BUILD_EXAMPLES=ON
cmake --build build --target bui_kit_hello

A modifier stack built with the C kit: panels, enum rows, number fields, dropdowns, a slider, checkboxes and buttons
一个更大的 C 程序:修改器堆栈(examples/site/kit_modifiers.c)

如果没有窗口系统,则渲染到位图光栅中(BK_render(ctx, raster, w, h, full)),这也是测试和截图的工作方式。指南:docs/c-api.md。


第三部分:概念

15. 工作原理

 your code:   tkinter   Qt   components   Dear ImGui (C++/C/Python/Rust)   Rust: rsx! / HTML / CSS / Tailwind / hooks   JS / React
                  │      │        │                │                                  │                                    │
                  └──── Python: options, signals    │ ImGui core + BUI backend          └── style cascade + taffy layout ◄────┘
                                  │                │                                  │
 C kit (kit/):   node tree · geometry · input & interaction · Blender widget drawing · undo · animation · themes · Blender search/expr
                                         │
 renderer:       damage-driven software raster (parallel)   or   GPU (Direct3D 11 / OpenGL 3.3, instanced, opt-in)
                                         │
 window:         native Win32 / X11 / Cocoa window   ·   stdlib Tk window   ·   headless (tests, screenshots)
  1. 你的代码(使用上述任意语言)创建构件,并指定它们发生更改时的行为。
  2. 工具包将它们保持在同一个树结构中,进行布局并处理输入,遵循 Blender 的规则。
  3. 渲染器将它们绘制出来,默认在 CPU 上,如果需要也可以在 GPU 上。
  4. 差分渲染(Damage rendering)使其成本低廉:更改只会重新绘制它接触到的矩形,闲置的窗口完全不消耗 CPU。

底层原理:

  • Blender 的真实 UI 代码。小部件绘制是 Blender interface_widgets.cc 的 C 语言移植版本(round_box_edges、widgetbase_draw、形状 表),交互遵循 interface_handlers.cc。颜色数学、矩形、 文本光标、字符串搜索和表达式求值都是直接复制的 Blender blenlib。一些界面文件(字符串搜索、文本编辑撤销、 浮点精度)被复制到 kit/intern/blender/ 中并针对该 工具包进行了编辑。X11 和 Cocoa 主机遵循 Blender 的 GHOST;Win32 主机则由 BUI 自行实现。
  • 一个节点树。每个小部件都是一个工具包节点。C 语言工具包负责布局、点击 测试、悬停、焦点、拖拽、菜单、工具提示、撤销和动画。
  • 局部刷新渲染。更改只会标记其小部件的矩形,并且只有 发生变化的图块才会到达屏幕。
  • 精简绑定。Python、Rust 和 JavaScript 仅保留其语法 层所需的内容。

更多信息:ARCHITECTURE.md 和 工作原理。

16. Blender 的小部件和交互

这些规则存在于 C 语言工具包中,因此它们在绘制 Blender 小部件的所有地方都以相同方式工作:Python 组件、tkinter 和 Qt 语法、Rust 和 JavaScript Blender 组件以及 styled 内部的表单控件、ImGui 的 Blender 小部件构建版本,以及每个 C 工具包小部件。

  • 拖拽微调。按住数值字段并向侧面拖拽。光标会隐藏 并循环,因此你永远不会触及屏幕边缘,并在拖拽开始的 位置重新出现。浮点数每像素移动一个点击步长;按住 Shift 以十分之一的 速度移动;按 Esc 取消并恢复该值。
  • Ctrl 吸附遵循 Blender 的规则:无界浮点数吸附到整数单位,角度 吸附到 10°,滑块根据其范围吸附到 0.1、1 或 10,整数吸附到 10 的倍数。
  • 点击输入,并键入数学公式:Blender 的表达式求值器 (expr_pylike_eval.cc)将 2*pi、90/4、min(4, 3) 或 x if c else y 转换为数字。这在 Blender 的数字字段和 滑块中有效;纯文本字段会将你键入的内容保留为文本。
  • 任意数值的快捷键:Backspace 重置为默认值,- 取反, Ctrl+Wheel 步进,Ctrl+C / Ctrl+V 复制和粘贴,右键点击打开复制、 粘贴和重置。
  • 同时修改多个字段。在数字字段列上向下拖拽以 选中它们,然后向侧面拖拽以同时更改它们。跨复选框 (或面板标题)拖拽可在一次操作中全部设置。
  • 面板随 Blender 的动画折叠;Ctrl+点击打开一个并 关闭其他面板;拖拽把手可重新排序。
  • 菜单支持按住-拖拽-释放或点击;悬停可切换菜单栏菜单; 饼菜单在指针周围打开。
  • 菜单搜索即 Blender 的字符串搜索:在任何打开的菜单中直接开始键入。 它匹配词首字母(“ad cu”可找到“Add Cube”),包容拼写错误,并 将你最近的选择排在最前面。
  • 随处撤销。Ctrl+Z 和 Ctrl+Shift+Z 适用于每个 UI 编辑操作,以及 键入时的文本框内部。连续输入或多字段拖拽算作 一步。
  • 浮点精度遵循 Blender 的规则:小数值会显示它们 所需的小数位数。
  • 工具提示在 Blender 的半秒延迟后出现。

即将推出:带单位的键入数值(“2 m”)、Blender 的即输即搜 弹出窗口,以及小部件拖拽。更多信息: Blender 的小部件。

17. 三种外观:HTML、样式化组件和 Blender 组件

在 Rust 和 JavaScript 中,你可以按元素选择外观:

你编写 你得到
纯 HTML:<button>、<input type="range">、<select> ... 浏览器绘制的内容:白底黑字以及 Chrome 的表单控件,使用 Inter UI 字体
任意元素上的 styled:<div styled>、div().styled() Blender 的外观和内部一切:Blender 的主题(深色或浅色)、UI 字体、Blender 部件
Blender 组件(大写):<NumberSlider>、<Button> ... Blender 的部件和随处可见的交互(拖动以擦除、Ctrl+Z、Blender 的颜色选择器)

这些组件包括 Button、Checkbox、Radio、NumberField、NumberSlider、 TextField、TextArea、ColorSwatch、Dropdown 和 ProgressBar。每一个 都是它所代表的 HTML 元素,因此每个属性、样式和事件都在它上面起作用:

rsx! {
    <main class="p-4 flex gap-2">                          // browser look
        <input type="range" />                             // Chrome's slider
        <NumberSlider min={0.0} max={10.0} value="2.5" />  // Blender's slider
        <div styled><button>"Apply"</button></div>         // Blender's look inside
    </main>
}
div().child(Checkbox().checked(true)).child(Button().child("Apply"))   // gpui syntax
import { render, NumberSlider, Button } from "@blender-ui/react"
// <NumberSlider min={0} max={10} onChange={...} />, <Button onClick={...}>Apply</Button>
// DOM API: win.createElement("NumberSlider")

Form controls as plain HTML, inside styled, and as Blender components
cargo run --example forms:纯 HTML,在 styled 内部,以及 Blender 组件

A form and a table in plain HTML, with a browser's default look The same markup inside styled: Blender's theme and widgets
相同的标记,纯文本(左)和在 styled 内部(右)。纯 HTML 在两个主题中看起来都一样。

Camera properties built from Blender components in Rust: number fields, sliders, a dropdown, checkboxes, a text field, a color swatch, a progress bar and buttons
来自 Rust 的 Blender 组件(site_components.rs)

Python 的 Blender 组件(Panel、PieMenu、节点编辑器等)是一个独立且更大的集合。

18. 在 CPU 和 GPU 上渲染

BUI 有两个渲染器,它们绘制相同的像素。

  • 软件光栅器(gpu/intern/gpu_raster.c)是默认设置。它使用带符号距离覆盖的抗锯齿形状(就像 Blender 的部件着色器)绘制到一个帧缓冲区中,在 64×32 的图块中将其与最后一帧进行比较,并且只报告发生更改的图块。大型画布在 Windows 上以并行色带绘制(BUI_RASTER_THREADS)。它不需要显卡的任何支持,并且占用内存最少。
  • GPU 渲染器是可选的:Windows 上的 Direct3D 11 (gpu/intern/gpu_d3d11.c)和 Linux 上的 OpenGL 3.3 core (gpu/intern/gpu_gl.c)。它以 Blender 的实例化部件绘制为模型:每个图元是单个超级着色器的一个 128 字节实例,因此一帧就是几个实例化的绘制调用。文本和图像缓存在图集中。帧渲染到持久目标中,因此只有损坏的区域会重新绘制;在 Windows 上,flip 模式交换链呈现脏矩形。在 Linux 上, libGL、libEGL 和 libX11 在运行时加载(用于窗口的 GLX,用于离屏的 EGL),因此不需要 GL 头文件,并且在缺少 OpenGL 的地方,BUI 会回退到 CPU。

着色器评估软件光栅器的公式:奇偶校验测试发现在两个后端上,172,800 个像素中有 10 个的差异超过 3/255。

开启 GPU:

位置 方式
Python 在创建窗口之前 bui.set_gpu(True);tkinter 语法也 tk.Tk(gpu=True)
Rust Application::new().gpu(true) 或 WindowOptions { gpu: Some(true), .. }
JavaScript new App({ gpu: true }) 或 React 的 render(..., { gpu: true })
C BUI_gpu_set_default(1) 或 BK_window_set_gpu(win, 1)
任何应用,无需修改代码 BUI_GPU=1

BUI_GPU=1 在代码中覆盖应用范围的设置;BUI_GPU=0 关闭应用范围的默认值,但请求 GPU 的窗口仍然会得到它。在无法启动设备的任何地方,BUI 会悄悄停留在 CPU 上(BUI_GPU_DEBUG=1 说明了原因)。

选哪一个? 工具窗口、表单或设置面板:CPU(对内存最轻量,空闲时为零)。大型或 HiDPI 窗口、大量动画、数千个项目:GPU(每帧的 CPU 占用大约减半或更少,多占用约 10 MB 内存外加 GPU 内存)。服务器和测试:CPU。macOS:暂时使用 CPU(那里的 GPU 渲染器即将推出)。

更多:渲染。

19. 损坏渲染

让 BUI 窗口保持打开并走开:它完全不消耗 CPU。将鼠标悬停在按钮上,BUI 会重新绘制该按钮,而不是整个窗口。

  1. 更改只会将为其部件的矩形标记为脏(弹出窗口为其阴影留出边距)。
  2. 脏矩形会合并(每个窗口最多 24 个)。
  3. 如果没有任何东西变脏,循环就不会渲染;它会休眠直到下一个输入或计时器。
  4. 只有损坏的区域会被重新绘制,并剪切到它们的矩形。
  5. 光栅器在 64×32 的图块中将新像素与最后一帧进行比较;未做任何更改的重绘不报告任何内容。
  6. 只有受损的图块才会到达屏幕(Win32 会复制它们,X11 会用 XPutImage 发送每个矩形,Cocoa 会使其失效,GPU 则会呈现脏矩形)。

在完整组件演示中,完整帧的大约耗费 0.7 ms,悬停或数字微调(number-scrub)帧大约耗费 0.05 ms。动画仅在有内容移动时运行,并与显示器同步。更多信息:Damage rendering。

20. 窗口宿主

窗口宿主将套件上下文放入顶层窗口中并为其提供输入。

宿主 平台 注意
Win32 (kit_window_win32.c) Windows BUI 自己的宿主。支持每监视器 DPI、垂直同步(vsync)同步动画、剪贴板、光标以及窗口管理器功能。
X11 (kit_window_x11.c) Linux;通过 XWayland 支持 Wayland 在 Blender 的 GHOST 之后进行输入映射。Xft.dpi 缩放。libX11 在第一个窗口打开时加载。
Cocoa (kit_window_cocoa.m) macOS 实验性。移植自 Blender 的 GHOST Cocoa 代码,支持 Retina 缩放。它已通过 CI 检查,目前已在一台 Apple Silicon Mac 上进行过手动测试;测试说明见 docs/macos-testing.md。
Tk Python,任何平台 一个标准的 tkinter 窗口,用于显示套件的帧缓冲区。BUI_HOST=tk 会强制启用它;当没有原生宿主可用时,它也是后备方案。
Headless 任何平台 无窗口:渲染到位图(raster)中用于测试和截图,使用虚拟时钟精确推进动画。

BUI_UI_SCALE=1.5 固定 UI 缩放而不跟随显示器(1 是 Blender 的默认值,20 像素小部件)。更多信息:Window hosts。

21. 文本与字体

你的应用的文本在 Windows、Linux 和 macOS 上看起来是一样的,并且看起来像 Blender 的:

  • Inter(Blender 的 UI 字体)已内嵌(可变版本,带有光学大小和字重轴)。UI 文本在缩放 1 时为 11 pt。
  • 渲染效果类似 Blender 的 BLF:BUI 内置了 FreeType 2.13.3,并使用轻量微调(light hinting)、Blender 的 gamma-1.43 覆盖表、Inter 的光学大小、真正的粗体实例、无字偶距(与 Blender 相同)以及 Blender 的文本阴影进行渲染。使用 BUI_TEXT_FREETYPE=OFF 时,则改用 stb_truetype 进行光栅化。
  • 在 GPU 上,默认情况下使用 Slug 从字形轮廓绘制字形,或者像 Blender 一样从每个字形的图集中绘制(BUI_SLUG=0,bui.set_slug(False),.slug(false),BUI_gpu_set_slug(0))。
  • 其他字体:BUI_FONT=path.ttf 替换 Inter,BUI_FONT_DIR 添加一个首先搜索的文件夹,blenderui::add_font(Rust)和 BUI_text_add_font(C)从内存注册字型,而 CSS @font-face 加载本地文件。
  • Windows 的 GDI 文本是可选功能(-DBUI_TEXT_GDI=ON,Rust 特性 gdi-text):采用 ClearType 风格的微调和系统字体,不使用 Slug。

值得了解的限制:尚无每字形字体后备功能(缺失的字符显示为方块),无 IME,从右到左的文本会重新排序但不会进行塑形(shaping),无 OpenType 特性。字体后备和 IME 即将推出。更多信息:Text and fonts。

22. 主题:深色与浅色

BUI 看起来像 Blender 是因为它使用了 Blender 的主题。它内置了两个主题:Blender 的深色主题(默认)以及它的浅色预设。两者均由 tools/gen_theme.py 根据 Blender 自身的 Blender_Dark.xml 和 Blender_Light.xml 预设(基于 Blender 5.x 的内置默认值)生成。在运行时切换,每个打开的窗口都会重新布局并重新绘制:

平台 浅色主题
C BK_set_theme("light");BK_theme_name(),BK_theme_color("region_bg", rgba)
Python blenderui.set_theme("light")(同时切换 Dear ImGui 扩展);theme_name(),theme_color(key)
Rust blenderui::set_theme("light"),或 Application::new().theme("light");theme_name(),theme_color(key)
JavaScript new App({ theme: "light" }),app.setTheme("light"),或 React 的 render(..., { theme: "light" })
Dear ImGui 其 Blender 风格遵循套件的主题(在后端启动前选择)
任何应用,无需修改代码 BUI_THEME=light(或 dark)

部件、面板、区域背景、styled HTML 样式表和 ImGui 的 Blender 风格都遵循该主题。无论哪种情况,纯 HTML 都保持浏览器的外观。在每个主题内部,你仍然可以自行给元素着色:tkinter 中的 bg / fg,Qt 语法的 setStyleSheet,组件上的 color=,Rust 和 JavaScript 中 HTML 的完整 CSS,以及 ImGui 自己的样式颜色。

BUI 还包含了 Blender 的图标:全部 781 个 Blender SVG 图标,使用 Blender 的 NanoSVG 代码进行光栅化,因此它们在任何缩放比例下都清晰锐利。通过 Blender 的名称使用它们:Button("Add", icon="ADD")、EnumRow(..., icons=[...])。

即将推出:加载除深色和浅色之外的任何 Blender 主题文件。更多信息:主题设置。


第四部分:按语言分类的指南

23. Python

pip install blenderui 在一个包中为你提供四个 API:

  • tkinter 语法(blenderui.tksyntax):所有公开的 tkinter 和 ttk 名称(7,846 个中的 7,846 个)。使用 blenderui-tk app.py 运行现有程序,或者首先调用 blenderui.tksyntax.install()。messagebox 和 simpledialog 具有 Blender 风格,colorchooser 是 Blender 的颜色选择器,filedialog 是 Blender 的文件浏览器(用于操作系统对话框的 BUI_FILEDIALOG=native)。接受纯外观的 Tk 选项(relief、ttk.Style 等),但外观来自 Blender 主题。指南:docs/tkinter.md。
  • Qt 语法(blenderui.qtsyntax):部件、布局(盒式、网格、表单、堆叠)、信号、QPainter、菜单、定时器和动画,作为 Blender 文件浏览器的 QFileDialog。部分支持:缺少 model/view 类、docks、工具栏、分割器和 Qt 样式表。指南:docs/qt.md。
  • Blender 组件(blenderui.components):45 个组件,从 NumberSlider 到 NodeEditor。参考:docs/components.md。
  • Dear ImGui(blenderui.imgui),pyimgui 风格。指南:docs/imgui.md。

你可以将它们混合在一个进程中:窗口、定时器和事件循环是共享的(bui.host)。每个交互都在 C 中运行,因此测试可以无头运行部件。在网站上:tkinter、Qt 语法、components、无头测试。

24. Rust

blenderui crate 是一个由 BUI 绘制的类网页框架:每个 HTML 元素、CSS、Tailwind、类 HTML 标记(rsx! / view!)或 gpui 风格的构建器、组件和钩子。抢先体验:

rsx! {
    // Tailwind as attributes, any variant, arbitrary values; typos fail to compile
    <button px-3 py-1 rounded-md bg-zinc-800 hover:bg-sky-600 md:dark:hover:bg-sky-400
            active:style="transform: scale(0.97)" onClick={move || save()}>
        "Save"
    </button>

    // control flow right in the markup
    <ul>{for item in items { <li class="py-1">{item}</li> }}</ul>
    {if dirty { <p text-amber-400>"Unsaved changes"</p> }}

    // real CSS, evaluated by the engine
    <div style="width: calc(100% - 2 * var(--gap)); background: color-mix(in oklch, var(--brand) 60%, white)" />
}
error: rsx!: `bg-sky-50O` is not a Tailwind utility; did you mean `bg-sky-500` or `bg-sky-50` or `bg-sky-100`?

包含内容:

  • 由 taffy 提供支持的布局:块级流和自动换行文本、flexbox 和网格(每个属性,包括 grid-template-areas 和命名行)、表格、relative / absolute / fixed 定位(固定盒子相对于窗口放置)、滚动以及双向文本。
  • CSS:选择器级别 4、嵌套、@layer / @media / @supports / @scope / @container / @font-face / @property、var() 和 env()、所有数学函数、CSS Color 4 和 5。
  • 效果和动画:@keyframes 动画和过渡(每个长名称属性、cubic-bezier()、steps()、linear()、可打断的过渡)、filter 和 backdrop-filter、mix-blend-mode、clip-path、遮罩、2D 变换、border-image。帧仅在有物体移动时渲染,无头窗口推进虚拟时钟(App::advance_clock)。
  • Tailwind CSS v4,在首次使用时编译,在 rsx! 和 tw! 中进行编译时检查。
  • zopra、React 或 Leptos 风格的组件和钩子,带有可选属性和键。
  • Blender 组件、styled 以及 主题。
  • 无头测试:Application::headless()、截图、模拟输入。

暂不支持:异步资源(use_resource)、通用组件、3D 变换、OpenType 特性、多栏布局。指南:docs/rust.md,crate README:bindings/rust/blenderui,以及网站的 Rust 部分(包括 效果和动画)。

25. JavaScript 和 React

  • @blender-ui/react:一个 React 19.3 渲染器。包含 HTML 主机组件、CSS 和 Tailwind、事件、Suspense、过渡、动作以及 Blender 组件。Hooks 保留了 React 的名称但持有了信号(signals):将其中一个传给 JSX,修改时就会更新该节点而无需重新渲染。README:bindings/react。
  • @blender-ui/native:一个带有信号(signals)、memo、effect 和 batch 的类 DOM API(含 Tailwind 的 createElement、appendChild、addEventListener、className,style)。README:bindings/node。
  • 速度快的原因:BUI 在其自身的 UI 线程上渲染,因此它从不阻塞 JavaScript;每个 tick 发送一批更改;样式只解析一次。
  • 独立应用:npx bui build app.jsx 可生成一个内嵌 QuickJS 引擎的约 7 MB 可执行文件;--bun 则生成一个 Bun 可执行文件;npx bui run app.jsx 无需构建即可在 QuickJS 上运行。
  • 测试:new App({ headless: true }) 在屏幕外渲染,win.click(x, y) 模拟输入。

限制:npm 软件包仅预构建了 Windows x64 版本;事件在 BUI 处理后到达 JavaScript(没有原生的 preventDefault),并且 event.target 是最深层的 监听 元素。指南:docs/javascript.md;网站:React、原生 API、信号、独立可执行文件。

26. Dear ImGui

语言 你使用的内容 构建者
C++ imgui.h + imgui_impl_bui.h CMake 目标 bui_imgui(bui::imgui)
C cimgui.h + cimgui_impl_bui.h CMake 目标 bui_cimgui
Python import blenderui.imgui as imgui wheel 中的 _buiimgui 扩展
Rust blenderui-imgui crate(在 crates.io 上) Cargo

C、Python 和 Rust 绑定是由 imgui.h 生成的。ImGui 在 BUI 上是无空转的:帧仅在有输入和动画播放时运行。Blender 小部件构建(BUI_IMGUI_BLENDER_WIDGETS=ON,Rust 特性 blender-widgets)替换了 Blender 的小部件,并且可以在运行时切换(ImGui_ImplBUI_SetBlenderWidgets、imgui.set_blender_widgets)。在该构建中,窗口标题栏、工具提示、普通弹出窗口、表格和颜色选择器保留了 ImGui 的绘制。

指南:docs/imgui.md、bindings/imgui/README.md(后端和 Blender 风格)、bindings/imgui/blender/README.md(小部件映射),以及网站的 Dear ImGui 章节。

27. C 与嵌入

工具包的概念:通过类型名称创建的 节点(frame、slider、button、panel、tree、canvas、filebrowser ……)、通过名称设置的 属性、C 语言中的 Tk 几何管理器(pack、grid、place)、一个拥有输入、弹出窗口、焦点和撤销操作的 上下文、一个接收事件的 宿主 以及 窗口。一个 custom 节点可以自行绘制和处理输入;Dear ImGui 就是以这种方式在 BUI 上运行的。指南:docs/c-api.md。

要将 BUI 嵌入到你自己的渲染器中,只需将工具包渲染到 BuiRaster 中并进行位块传输(正如 Qt 内部的 bui.qt.BuiCanvas 所做的那样)。一个独立的、更底层的层——渲染接收器(api/bui_c_api.h),将即时模式 GPU 缝合处的三角形、线条和文本传递给你的渲染器;Qt/QPainter 后端和 SVG 记录器就使用了它。指南:INTEGRATION.md。

28. 文档所在位置

指南 内容简介
网站 以下所有内容,包含教程、概念和参考资料,带有深色和浅色模式的截图
快速入门 构建、环境变量、窗口宿主、GPU 渲染、故障排除
tkinter 在 BUI 上运行 tkinter 应用、行为说明、限制
Qt 语法 类、信号、布局、绘制、覆盖范围
组件 bui.components 参考
Rust rsx!、hooks、HTML、CSS 和 Tailwind 覆盖范围、效果、动画、事件、无头测试
JavaScript 和 React @blender-ui/native、@blender-ui/react、更改如何跨越到 UI 线程、性能
Dear ImGui 来自 C++、C、Python 和 Rust 的 BUI 上的 Dear ImGui;Blender 组件构建;覆盖率
C API C 工具包 (BUI_kit.h)
Integration Embedding BUI drawing in C, C++, Qt or PySide hosts
Architecture Modules and what was taken from Blender
Syntax coverage The Qt and tkinter surface, exactly
macOS testing How to try the experimental Cocoa host on a Mac
Publishing Releasing to PyPI and crates.io
Changelog What changed

Part V: Performance

29. Before you read the numbers

These numbers were measured on a Ryzen 7 5800U laptop (Windows 11, on AC power) before the latest features landed: Dear ImGui, the Linux GPU renderer, the light theme, and the Blender search and expression code. They show the shape of things, not a promise for your machine. Rerun them with the tools in Run the benchmarks yourself.

30. Start time and battery

The same small settings window (a label, text field, checkbox, slider and button) written each of the five ways, from bench/apps, on BUI's CPU renderer (the default) and its GPU renderer (BUI_GPU=1). The last rows are one gpui-syntax source file, settings.rs, compiled unchanged against blenderui, Zed's gpui and gpui-fast. gpui has no form widgets, so there the controls are styled divs.

Median of 10 launches, from process start until the first frame is on screen, with Python 3.14 and Node 22:

Renderer Start time CPU used to start Memory (idle) Idle CPU per minute
Rust (HTML + CSS) CPU 38 ms 27 ms 13 MB 0 ms
GPU 42 ms 61 ms 31 MB 1.8 ms
JavaScript (React, Node.js) CPU 94 ms 100 ms 48 MB 68 ms
GPU 99 ms 131 ms 60 MB 71 ms
Blender components (Python) CPU 112 ms 86 ms 23 MB 0 ms
GPU 116 ms 125 ms 32 MB 1.7 ms
tkinter syntax (Python) CPU 139 ms 112 ms 25 MB 0.2 ms
GPU 159 ms 157 ms 43 MB 2.4 ms
Qt syntax (Python) CPU 156 ms 123 ms 27 MB 0 ms
GPU 161 ms 160 ms 36 MB 1.2 ms
Same gpui code: blenderui CPU 40 ms 27 ms 15 MB 0 ms
blenderui GPU 43 ms 60 ms 31 MB 2.1 ms
gpui GPU 231 ms 226 ms 29 MB 743 ms
gpui-fast GPU 226 ms 217 ms 31 MB 2.5 ms

On the same code, blenderui shows its first frame about 5× sooner than gpui and gpui-fast, spends about 8× less CPU getting there, and on the CPU renderer uses half the memory. Idle, gpui keeps about 1.2 % of a core busy; blenderui uses none.

Laptops can't report energy per app, so battery cost is measured by the CPU time an app causes: the CPU it spends starting up, and the CPU it uses over 20 seconds while the window sits idle (what drains a battery while an app is open). BUI redraws only when something changes and otherwise sleeps, so idle use is near zero on either renderer. The GPU renderer starts a little slower and uses more memory (the GPU device), and pays off on large, animated scenes (below). Node's event loop accounts for the JavaScript idle figure, Python's times include starting the interpreter, and memory doesn't count GPU memory.

31. Frame times

Python and C, on the full component demo (520×900):

Frame Time
Complete frame (everything redrawn) ~0.7 ms
Hover or number-scrub frame (damage only) ~0.05 ms
Native window: input event → on screen ~0.06 ms

Dear ImGui's demo window with every section open (27k triangles, 1280×800) is a full software redraw of about 5–8 ms.

Rust, against Zed's gpui and the faster fork gpui-fast. The scene is 3000 animated cells, and all apps ran in the same session (GPU rows on Direct3D 11):

blenderui (GPU) blenderui (CPU) gpui-fast gpui
FPS 59.9 57.6 31.5 19.9
CPU per frame 11.6 ms 22.0 ms 31.9 ms + GPU 50.5 ms + GPU
RAM 40 MB 30 MB 65 MB 77 MB
First frame ~5 ms ~5 ms ~200 ms ~220 ms
Idle CPU 0 % 0–0.9 % 0–0.4 % 0.9–1.4 %

Full results, the GPU renderer, and the method

blenderui (GPU) blenderui (CPU) gpui-fast gpui
FPS, 1000 animated cells 60 60 49.2 36.4
CPU per frame, 1000 / 100 cells 4.7 / 1.2 ms 10.2 / 4.6 ms 20.5 / 2.7 ms + GPU 27.7 / 4.7 ms + GPU
Uncapped FPS, 100 / 1000 / 3000 cells 1491 / 309 / 94 649 / 202 / 76 - -
RAM, 1000 / 100 cells 35 / 28 MB 24 / 21 MB 45 / 47 MB 47 / 40 MB
GPU memory 19–22 MB none 19–24 MB 16–21 MB

The GPU renderer is off by default. Turn it on with Application::new().gpu(true), tk.Tk(gpu=True), bui.set_gpu(True) or BUI_GPU=1:

  • Every primitive is one instance of a single shader, so a frame is a few instanced draw calls (Direct3D 11 on Windows, OpenGL 3.3 on Linux).
  • Text and images are cached in atlases.
  • Frames render into a persistent target, so only damaged regions redraw.
  • On Windows, presentation uses a flip-model swap chain with dirty rects.

Its shaders evaluate the software raster's formulas: the parity test finds 10 of 172,800 pixels that differ by more than 3/255, on both backends.

gpui-fast's heavy-scene frame rate varies between sessions on this laptop (30–60 fps), so compare within one session. Method, raw data and how to rerun: bindings/rust/bench/README.md.

32. JavaScript and React: startup and animation

Startup. These launch a small React app (the counter above) that quits once its first frame is on screen. Each figure is the median of 12 launches after a warm-up launch (node bench/startup.js).

First frame Launch to exit RAM at first frame What you ship
QuickJS executable (bui build) 68 ms 104 ms 19 MB 7.3 MB
Bun executable (bui build --bun) 79 ms 147 ms 35 MB 114 MB
Bun (bun app.js) 80 ms 152 ms 35 MB Bun + your app
Node.js (node app.js) 108 ms 187 ms 45 MB Node + your app
Rust (for reference: the benchmark app) 5 ms ~92 ms 3.5 MB

First frame is measured from the runtime's start. Launch to exit is wall time measured from outside, including process creation and teardown.

Animation. The same scene as the Rust table above, in one mode: one cell changes per frame, GPU renderer, medians of 3 runs. CPU is the whole process, counted with the cycle counter as for Rust.

  • Plain JS uses @blender-ui/native.
  • React with signals passes the changing values as signals, so the grid renders once.
  • React with state re-renders the grid every frame.
  • React's production build is used throughout.
3,000 cells FPS CPU per frame RAM
Rust 59.6 12.0 ms 43 MB
QuickJS: plain JS / React with signals / React with state 59.1 / 58.7 / 19.7 16.3 / 14.9 / 51.0 ms 56 / 61 / 67 MB
Bun 1.4: plain JS / React with signals / React with state 58.8 / 58.4 / 55.8 13.1 / 13.3 / 20.8 ms 80 / 85 / 118 MB
Node 22: plain JS / React with signals / React with state 55.8 / 56.7 / 54.8 14.4 / 14.0 / 18.9 ms 88 / 93 / 121 MB
1,000 cells FPS CPU per frame RAM
Rust 60 4.8 ms 32 MB
QuickJS: plain JS / React with signals / React with state 60 / 60 / 56.8 6.0 / 5.6 / 17.8 ms 38 / 40 / 43 MB
Bun: plain JS / React with signals / React with state 60 / 60 / 60 5.2 / 5.2 / 7.5 ms 58 / 63 / 81 MB
Node: plain JS / React with signals / React with state 60 / 60 / 60 5.6 / 5.6 / 6.9 ms 68 / 71 / 97 MB

What this means:

  • JavaScript costs little over Rust: 0.4–2.4 ms per frame, for sending changes to BUI's UI thread.
  • Signals make React as cheap as the plain API: the component doesn't re-render.
  • QuickJS starts fastest and uses the least memory, from a 7 MB file. It has no JIT, so re-rendering a 3,000-element list every frame drops it to 20 FPS. Keep big lists on signals, or build with --bun.

Method, the memory breakdown and how to rerun: docs/javascript.md.

33. Run the benchmarks yourself

Every number above comes from a tool in this repo. Build in release mode, close other heavy apps, and plug the laptop in. Run one benchmark (and one build) at a time.

Start time and battery: the five ways and gpui (bench/, Windows). The same settings window written each way, on the CPU and the GPU renderer, plus one gpui-syntax file compiled for blenderui, gpui and gpui-fast. It reports start time, CPU used to start, memory and idle CPU, and writes bench/results.json.

cmake -S . -B build && cmake --build build --config Release --target buipy  # Python extension
cd bindings/node && npm run build && cd ../..                                # Node addon
cd bindings/rust && cargo build --release --example bench_app && cd ../..    # Rust app
cd bindings/rust/bench && cargo build --release --bins && cd ../../..        # gpui comparison
py -3 bench/run.py 10 20        # launches per app, seconds of idle measurement

Frame cost: 100, 1,000 and 3,000 animated cells, against gpui and gpui-fast (bindings/rust/bench/, Windows). A 1000×800 window filled with N rounded, numbered cells, in three modes:

  • anim: every cell changes every frame, the worst case.
  • one: one cell changes per frame, like a hover or a ticking value.
  • idle: nothing changes.

Each app measures its own CPU cycles, frames and memory, and run.py adds GPU utilisation and GPU memory from Windows' performance counters.

cd bindings/rust/bench
set GPUI_FXC_PATH=C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\fxc.exe   # gpui needs fxc.exe
cargo build --release
py run.py --reps 3                                   # all apps, 100 / 1000 / 3000 cells, about 12 minutes
py run.py --cells 3000 --apps blenderui,blenderui-gpu --reps 1
target\release\bui-bench --cells 3000 --mode anim   # one run by hand (--mode one / idle, --uncapped)
set BUI_GPU=1 && target\release\bui-bench --cells 3000 --mode anim   # on the GPU renderer

Method and full results: bindings/rust/bench/README.md.

JavaScript: the same cells, and startup (bindings/node/bench/):

cd bindings/node
node bench/cells.js 3000                       # DOM-like API, one cell changing per frame
node bench/cells.js 3000 --react               # React re-rendering the grid
node bench/cells.js 3000 --react --signals     # React with signals: no re-render
node bench/cells.js 3000 --gpu                 # on the GPU renderer
bun bench/cells.js 3000                        # on Bun
node cli.js run bench/cells.js -- 3000         # on the 5 MB QuickJS runtime
node bench/startup.js                          # launch to first frame: Node, Bun and both executables

tkinter: canvas frame cost (headless, any platform):

py bindings/python/bench/tk_canvas_bench.py 3000 all    # move 3,000 canvas items every frame
py bindings/python/bench/tk_canvas_bench.py 3000 one    # move one

Inside any app: run it with BUI_PROFILE=1 to print per-phase frame times (layout, paint, raster, present) every 60 frames; any value turns it on. Use BUI_GPU=1 / BUI_GPU=0 to pick the renderer. More: Profiling.


Part VI: Reference

34. Platforms

Windows Linux macOS
Window Native (Win32) Native (X11; Wayland through XWayland) Native (Cocoa), experimental
Python (pip install) Works: wheels Works: wheels (native X11 host) Works: wheels
Rust (cargo add blenderui) Works Works Experimental
JavaScript / React Works: prebuilt on npm Works: build the addon from source (npm run build); native X11 windows Headless (see below)
C, C++, Dear ImGui Works Works Experimental
Renderer CPU raster, or GPU (Direct3D 11) CPU raster, or GPU (OpenGL 3.3) CPU raster
HiDPI Per-monitor DPI Xft.dpi Retina (backing scale)

Works means it works and is tested. Experimental means it's written but has had little hands-on testing on real hardware (macOS: one Apple Silicon Mac so far, plus CI).

macOS. The macOS window host is new: it's a port of Blender's Cocoa code (GHOST). So far it has been checked by CI and tried by hand on one Apple Silicon Mac by Krish Kalaria, who reported issue #3 and fixed it in pull request #4. If you have another one, please try it and tell us what happens: docs/macos-testing.md. Python can still use the stdlib Tk window there (BUI_HOST=tk; this needs Python's tkinter, for example from python.org or brew install python-tk). Node runs BUI on its own UI thread and macOS only allows windows on the main thread, so JavaScript stays headless on macOS for now.

Linux. libX11 and libGL are loaded when the first window opens, so BUI also imports and renders headless on servers and in containers without them, and falls back to the CPU raster where OpenGL is missing. Building from source needs the X11 headers (libx11-dev / libX11-devel); no GL headers are needed. The C, Python, window and GPU parity tests pass on Linux, on Mesa's software OpenGL and on an NVIDIA GPU.

Packages:

Package Platforms Notes
blenderui on PyPI Windows x86_64, Linux x86_64 (manylinux), macOS arm64 and x86_64 CPython 3.11 to 3.14. No musllinux or 32-bit wheels.
blenderui on crates.io Any platform with CMake and a C compiler Rust 1.88+. blenderui-imgui (Dear ImGui) is on crates.io too.
@blender-ui/native, @blender-ui/react on npm Windows x64 Node.js 18+ or Bun. Elsewhere, build the addon from source.
C and C++ (CMake) Windows, Linux, macOS CMake 3.20+, a C11 and C++20 compiler.

35. Environment variables

Most are read by the C library, so they work the same from every language. On/off variables look at the first character: 1, y, Y, t or T is on. Where a variable and an app-wide API call set the same thing, the variable wins.

Variable What it does
BUI_GPU 1 draws native windows on the GPU (Direct3D 11 / OpenGL 3.3). Overrides bui.set_gpu(), Application::gpu(), BUI_gpu_set_default(). 0 turns those defaults off; a window that asks for the GPU itself still gets it.
BUI_SLUG GPU text: 1 from glyph outlines (Slug, the default), 0 from a per-glyph atlas.
BUI_GPU_DEBUG 1 prints why the GPU renderer couldn't start.
BUI_GPU_WARP 1 (Windows) creates the Direct3D 11 device on WARP, the software rasterizer. Handy in VMs and CI.
BUI_GPU_MAX_TEXTURE Testing: the largest texture side the GPU renderers give one image (at least 64), so images over it are tiled as on devices with a small limit.
BUI_GPU_EGL default or surfaceless (Linux): the EGL platform for offscreen GPU rendering.
BUI_RASTER_THREADS 1 to 8 (Windows): the most threads the software raster uses for big canvases.
BUI_PROFILE Prints frame times every 60 frames. Any value turns it on, even 0.
BUI_THEME dark (default) or light: Blender's theme, when the app doesn't set one.
BUI_UI_SCALE A fixed UI scale (1.25, 1.5 ...) instead of the display's. Clamped to 0.5–4.
BUI_HOST tk (Python): use the stdlib Tk window even where a native host exists.
BUI_MAC_TK 0 (Python on macOS): skip starting a hidden Tk root before the first native window.
BUI_FONT, BUI_FONT_DIR A font file to replace Inter, and an extra font folder to search first.
BUI_FILEDIALOG native (or system, tk), Python: file dialogs use the OS's instead of Blender's File Browser.
BUI_BOOKMARKS The File Browser's bookmarks file.
BUIPY_BUILD_DIR Running Python from a source checkout: the folder holding the built buipy extension.
BUI_NODE_ADDON @blender-ui/native: load this .node addon instead of the bundled one.
BUI_HEADLESS 1: bui build apps and bui run render off-screen (same as --headless).
BUI_SOURCE_DIR, BUI_TEXT_GDI Build time, Rust: build the C library from this checkout; use GDI text.

Every variable, with defaults and where it's read: Environment variables and docs/getting-started.md.

36. Building from source and CMake options

You need CMake 3.20+, a C11 / C++20 compiler (MSVC, GCC/MinGW or Clang) and Python 3.11+ with headers. On Linux, also the X11 headers.

git clone https://github.com/ppmpreetham/BUI && cd BUI
cmake -S . -B build && cmake --build build -j 2
ctest --test-dir build                      # C, C++, ImGui and Python test suites

export PYTHONPATH=$PWD/bindings/python      # Windows: set PYTHONPATH=%CD%\bindings\python
python examples/components_demo.py

cd bindings/rust && cargo test              # Rust: CSS, Tailwind, rsx!, hooks, effects, headless windows
Option Default What it does
BUI_BUILD_TESTS ON C test programs and the ctest suites
BUI_BUILD_EXAMPLES OFF The C examples (bui_kit_hello, bui_svg_demo, the site screenshot programs)
BUI_BUILD_PYTHON_BINDINGS ON The buipy extension (and the ImGui extension); skipped without Python's dev files
BUI_BUILD_IMGUI ON with a C++ compiler Dear ImGui on BUI, its C API, demo and test
BUI_IMGUI_BLENDER_WIDGETS OFF Dear ImGui with Blender's widgets
BUI_BUILD_QT_BACKEND OFF The C++ Qt/QPainter render-sink backend (needs Qt 5 or 6)
BUI_BUILD_UI OFF Also compile Blender's original ui/ sources (reference only)
BUI_TEXT_FREETYPE ON Render glyphs with FreeType like Blender's BLF; OFF uses stb_truetype
BUI_TEXT_GDI OFF Windows' GDI text and system fonts
BUI_SLUG ON Slug as the GPU's default text method (switchable at run time)
BUI_IMAGE_SVG ON SVG images through the kit's nanosvg (no extra code: Blender's icons already use it)
BUI_WARNINGS_AS_ERRORS OFF GCC and Clang only: when OFF the build adds -Wno-error; it never adds -Werror or /WX itself

Rebuild the docs screenshots with python tools/gen_doc_images.py (and the website's dark and light shots with --site). More: CMake options.

37. Coverage

API Coverage Status
tkinter and ttk 7,846 of 7,846 public names; nine real programs behave identically Complete
Qt syntax About 220 of 920 public method names on 25 core widget classes (~24%) Partial
Blender components (Python) 45 components (10 of them also in Rust and JavaScript) Complete
HTML elements (Rust, JavaScript) 113 of 113 Complete
CSS properties (Rust, JavaScript) 261 of 745 W3C properties render, 129 more parse, 355 not recognized (mostly drafts, speech, paged media and SVG) Partial
CSS 2.1 screen properties 90 of 90 Complete
Flexbox / Grid 12 of 12 / 23 of 23 Complete
Dear ImGui, C 399 of 399 functions Complete
Dear ImGui, Python and safe Rust 366 of 399 functions (Rust FFI: 385) Partial
Image formats (every language) PNG, JPEG, WebP, GIF, BMP, TGA, QOI, HDR, PNM, SVG; not yet AVIF, EXR, TIFF, JPEG XL, HEIC Partial

How each was counted: Coverage, SYNTAX_COVERAGE.md, docs/rust.md, docs/imgui.md.

38. Repository layout

Path Contents
kit/ The C runtime: nodes, input, widget drawing (the interface_widgets.cc port), menus, geometry managers, canvas, the File Browser, themes (kit_theme_presets.c), the Win32, X11 and Cocoa window hosts; kit/intern/image/ (images: decoders, cache, worker pool; third-party decoders in kit/extern/); kit/intern/blender/ holds Blender files copied in (string search, text-edit undo, float precision). API: kit/include/BUI_kit.h
gpu/ Software raster, GPU renderer on Direct3D 11 (gpu_d3d11.c, shaders/) and OpenGL 3.3 (gpu_gl.c), textured meshes, Slug text, text engine (FreeType or stb_truetype + embedded Inter, or GDI)
core/, types/ Blender's blenlib subset (including string_search.cc and expr_pylike_eval.cc) and data types
ui/, compat/ Blender's original interface sources, kept for reference (not compiled by default; kit/ re-ports them in C), and the seams replacing Blender's application APIs
api/ Stable C ABI for render-sink hosts
bindings/python/ The buipy extension and the bui / blenderui packages (tkinter, Qt, components, ImGui)
bindings/rust/ blenderui, blenderui-sys, blenderui-macros, blenderui-tailwind, blenderui-imgui, blenderui-jsview, blenderui-node, blenderui-qjs, and the benchmark
bindings/node/, bindings/react/ @blender-ui/native and @blender-ui/react
bindings/imgui/ Dear ImGui (vendored), the BUI backend, Blender style, the C API and its generator, the Blender-widgets build
backends/qt/ C++ Qt/QPainter render-sink backend
bench/ The start time and battery benchmark; bench/images/ (decoders, thumbnail grid)
examples/, tools/ Demos, the website's screenshot programs (examples/site/), the icon and theme generators, the docs screenshot generator
website/ The documentation website, bui.preetham.top

39. Examples

File Shows
examples/tk_unchanged_app.py A plain tkinter app: menus, notebook, form, table, text, canvas
examples/components_demo.py Every Blender component in a properties-editor layout
examples/qt_syntax_demo.py Qt syntax: layouts, form widgets, signals
examples/tk_syntax_demo.py tkinter syntax laid out like a Blender properties editor
examples/kit_hello.c The C kit: a slider and a button in a native window
examples/imgui_demo.cpp Dear ImGui's demo window on BUI, from C++
examples/imgui_demo_c.c The same from C (cimgui.h)
examples/imgui_blender_compare.cpp ImGui widgets next to BUI's native Blender widgets
examples/site/ The website's screenshot programs: a Render Properties editor and a node editor (Python components), tkinter and ttk dialogs, a Qt material editor, an ImGui inspector (C++ and Python), a C modifier stack, and a JavaScript task list (DOM and React)
hello.rs Rust: a component, hooks, Tailwind attributes and a Blender slider
showcase.rs Rust: HTML, a stylesheet, flex/grid, tables, Blender form widgets
forms.rs Rust: form controls as plain HTML, inside styled, and as Blender components
site_dashboard.rs, site_dashboard_builder.rs Rust: the same dashboard in rsx! and in gpui builders
site_effects.rs, site_grid.rs, site_animation.rs Rust: CSS effects, grid and @keyframes
site_plain_html.rs, site_styled.rs, site_components.rs Rust: the three looks
blenderui-imgui demo Dear ImGui from Rust (cargo run -p blenderui-imgui --example demo)
bindings/node/examples/counter.js JavaScript: the DOM-like API
bindings/react/examples/counter.jsx React in JSX (bun examples/counter.jsx)

Part VII: The project

40. Status and roadmap

Done

Area What works
C runtime (kit/) Nodes, Tk-style geometry managers, input, Blender widget drawing, damage rendering, undo, animation, text area, canvas, scroll views, Blender's string search, expression evaluator, text-edit undo and float precision, Blender's Dark and Light themes
Native windows Windows (Win32) and Linux (X11; Wayland through XWayland), in C: DPI-aware, frame-paced animation, clipboard, cursors, window-manager features
tkinter syntax Complete: every public tkinter and ttk name (7,846 of 7,846)
Components 45 Blender components, including the color ramp, curve and node editors
Rust HTML (113/113 elements), Flexbox 12/12, Grid 23/23, CSS 2.1 90/90, math functions, var(), CSS Color 4/5, Selectors 4, @layer / @media / @supports / @scope / @container / @font-face / @property, @keyframes animations and transitions, filters / blend modes / masks / clip-path / 2D transforms / border-image, bidi text, background images, object-fit, Tailwind v4, rsx!, #[component] with optional props and key, hooks, Blender components, browser-look form controls, headless testing
Dear ImGui The real Dear ImGui 1.93 on BUI (CPU, Direct3D 11, OpenGL), Blender theme and Inter; C++ / C (399/399 functions) / Python and Rust (366–385/399); the Blender-widgets build
Rendering Software raster (parallel, damage-tracked), GPU renderer (Direct3D 11 on Windows, OpenGL 3.3 on Linux), textured meshes, portable text engine (FreeType, as in Blender)
JavaScript / React @blender-ui/native: a DOM-like API over napi-rs, with its own UI thread, only changes crossing the boundary, vsync-paced requestAnimationFrame and signals. @blender-ui/react: a React 19.3 renderer whose hooks (useState, useMemo, useEffect ...) hold signals that update nodes without re-renders. Both on Node.js and Bun, with headless testing. bui build makes standalone executables: about 7 MB with the embedded QuickJS runtime, or Bun's with --bun (guide)
Packaging blenderui on PyPI, with a CI workflow for wheels on all platforms (Linux wheels with the native X11 host); blenderui on crates.io; @blender-ui/react and @blender-ui/native on npm
Docs The website, bui.preetham.top

Partial or in progress

Area What's missing
Qt syntax (~24%) Model/view classes, docks, toolbars, splitters, Qt stylesheets (details)
CSS (261 of 745 W3C properties render, 129 more parse) 3D transforms, OpenType font features, multi-column layout; hit testing ignores 2D transforms. Most of the remaining W3C list is drafts, speech, print and SVG (breakdown)
Text No text wrapping around floats; right-to-left text is reordered (bidi) but not shaped (Arabic joining); no OpenType features; no per-glyph font fallback yet (characters missing from the font show as boxes); no IME
Rust components No async resources (use_resource); no generic components
Dear ImGui Python and safe Rust skip the va_list, void* and C-callback functions (reachable from C and the Rust FFI); with Blender widgets, window title bars, tooltips, plain popups, tables and the color picker keep ImGui's drawing
JavaScript / React packages npm packages are prebuilt for Windows x64 only (on Linux, build the addon from source); events reach JavaScript after BUI handled them (no native preventDefault), and event.target is the deepest listening element
macOS Experimental. The native Cocoa window host is written, checked by CI and tried by hand on one Apple Silicon Mac (how to help); JavaScript is headless there (Node's UI thread isn't the main thread); no GPU renderer yet
Themes Dark and Light work; loading other Blender theme files doesn't yet

Planned (coming soon)

Target Plan
WebAssembly The kit in the browser: the raster blits to <canvas>, DOM events feed the kit; @blender-ui/react and the DOM-like API on top, unchanged
egui BUI panels inside egui, plus a Blender-styled theme
More from Blender's source Per-glyph font fallback (BLF), IME, search-as-you-type popups, units in number fields, widget drag and drop
macOS GPU and JavaScript windows The GPU renderer on Metal (or the OpenGL backend), and Node windows on the main thread
More GPU backends Vulkan / WebGPU drawing the same instance stream as Direct3D 11 and OpenGL
Qt parity Measure against PySide6, run the same programs on Qt and BUI, fill in the missing classes, then install() so import PySide6 programs run unchanged
More languages MyGo, C#, Go, Zig, Swift, Kotlin and Lua over the same C header; Godot, Unity and Bevy; Flutter

Our roadmap has no dates on it. Like a good render, it's done when the noise goes away.

41. FAQ

Do I need Blender installed? No. BUI contains the parts of Blender's UI code it needs and builds on its own. Is it really my unchanged tkinter app? Yes. blenderui-tk app.py makes import tkinter return BUI's implementation, which covers every public tkinter and ttk name. Nine real programs are tested to behave identically on the standard library and on BUI. See the limitations for the few differences. Is it a web browser in Rust? No. There's no browser engine and no network. BUI takes HTML elements and CSS as a UI language: they are laid out with taffy and drawn by BUI's engine, with Blender's widgets where you ask for them, in a native window. JavaScript is optional: the Rust crate needs none, and the JavaScript packages drive the same engine. Is the Dear ImGui support a re-implementation? No. It's the real Dear ImGui (vendored, MIT), running on a BUI backend, so every function behaves as upstream. The optional Blender-widgets build swaps only how ImGui's widgets are drawn and how they react to the mouse, by compiling an edited copy of imgui_widgets.cpp. How is it different from Electron, Electrobun or GPUIX?

  • Electron ships Chromium with every app: on the order of 100 MB or more.
  • Electrobun runs on Bun and renders in the system's webview. It's small to download, but the UI is still a browser page.
  • GPUIX, like BUI, renders React natively: React drives Zed's GPUI.

BUI also renders natively, with Blender's widgets, a much larger part of CSS, Tailwind, and hooks backed by signals. bui build makes a 5 MB executable that reaches its first frame in about 70 ms. Do the macros cost performance? rsx!, #[component], css! and tw! expand at compile time into the same builder calls you would write by hand. Tailwind classes are compiled to CSS once, on first use. Hooks are a few nanoseconds per read or write. Can I change the colors? Yes. Switch between Blender's Dark and Light themes at run time (Themes), color individual widgets in every API, and use full CSS on plain HTML elements in Rust and JavaScript. Loading any other Blender theme file is coming soon. Does it run on my Mac? Probably, but nobody has checked yet: the Cocoa host is written and experimental. Python can always fall back to the Tk window (BUI_HOST=tk). If you try it, the notes in docs/macos-testing.md say what to report. Is it ready for production? Much of it is complete and tested (the C kit, tkinter, the components, Rust, Dear ImGui, JavaScript), and some parts are partial or experimental (Qt syntax, parts of CSS and text, macOS). Status and roadmap shows which is which. Pin your version, and test on the platforms you ship. Who wrote it? The repository is built entirely using LLMs, with no human-written code. The drawing and interaction code is ported from Blender's sources, and the behavior is checked by tests: C, Python, Rust and JavaScript suites, the tkinter side-by-side programs and the GPU parity test. Why GPL? BUI is derived from Blender's source code, which is GPL-2.0-or-later, so BUI is too. Can I use BUI in a commercial app? The GPL allows commercial use. What it asks is that when you distribute a program that includes BUI, you distribute it under the GPL too, with its source code available. If that doesn't fit your project, talk to a lawyer before you ship. (This is a summary, not legal advice.)

Star History

Star History Chart

42. Contributing and publishing

Contributions are welcome: bug reports, missing tkinter/Qt behavior, CSS features, platform hosts and docs. If you have a Mac, running the Cocoa host and reporting what happens is one of the most useful things you can do right now. Read CONTRIBUTING.md first, and please follow the Code of Conduct. Report security issues privately, as SECURITY.md describes.

Releases go to PyPI (blenderui, wheels built by CI on a v* tag), crates.io (the eight Rust crates, in dependency order) and npm (@blender-ui/native, @blender-ui/react). The steps are in docs/publishing.md.

43. License and credits

License. BUI is derived from Blender sources and is licensed GPL-2.0-or-later. The SPDX header is kept in every file. Third-party parts keep their own licenses:

Part What it does in BUI License
Blender sources Widget drawing, interaction rules, theme, icons, blenlib, GHOST window code, the File Browser and node editor GPL-2.0-or-later
Dear ImGui by Omar Cornut The immediate-mode GUI that runs on BUI MIT
Inter by Rasmus Andersson The embedded UI font, as in Blender (gpu/extern/Inter-LICENSE.txt) SIL Open Font License 1.1
FreeType 2.13.3 Glyph loading, hinting and rendering, like Blender's BLF FreeType License or GPLv2 (dual-licensed)
stb_truetype by Sean Barrett Font metrics and character maps, and the fallback rasterizer Public domain
NanoSVG by Mikko Mononen, as edited by Blender Rasterizing Blender's SVG icons Zlib
Slug by Eric Lengyel The GPU glyph coverage algorithm (shader code "Copyright 2017 by Eric Lengyel") MIT
Tailwind CSS The v4 default theme values behind the Tailwind support MIT

The Rust and JavaScript bindings also depend on crates and packages such as taffy, napi-rs, React and QuickJS, each under its own license.

Acknowledgments. Thank you to:

  • Blender and its community: the UI this project is built from, including its GHOST window code and blenlib.
  • Dear ImGui by Omar Cornut: the immediate-mode GUI that runs on BUI.
  • taffy: flexbox, grid and block layout for the Rust crate.
  • Tailwind CSS: the v4 theme behind the Tailwind support.
  • FreeType: glyph rendering, as in Blender.
  • stb_truetype: font metrics and the fallback rasterizer.
  • NanoSVG by Mikko Mononen: the icon rasterizer, via Blender's edited copy.
  • Inter by Rasmus Andersson: the embedded UI font, as in Blender.
  • Slug by Eric Lengyel: the GPU glyph coverage algorithm.
  • gpui, zopra and Leptos: the APIs the Rust syntax follows.

BUI is Blender's UI, minus the default cube. You're free to add your own.

来源:Hacker News-首页 · github.com