mcpField
B
blender-python-addon

blender-python-addon

github.com/mindrally开发28768 次安装3 周前更新
代码仓库

关于 blender-python-addon

这项开发技能介绍如何使用 `bpy` API 构建 Blender 插件,涵盖项目结构、界面、安全场景操作、注册、测试与打包。

主要功能

  1. 01插件包与清单结构
  2. 02操作器、面板和属性
  3. 03安全的场景与网格操作
  4. 04注册和清理规范
  5. 05跨 Blender 版本测试
  6. 06打包扩展和插件

适用场景

  1. 01搭建新的 Blender 插件
  2. 02在 Blender 界面中提供操作和设置
  3. 03检查插件能否正确注册与卸载
  4. 04准备可安装的插件包

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill blender-python-addon

安装后在 Claude Code 中输入/blender-python-addon确认已加载。

详细介绍

Blender Python 插件开发

本技能介绍如何使用 bpy API 构建 Blender 插件,包括插件结构、操作器、面板、属性、安全地操作场景,以及跨 Blender 版本测试。

Blender 插件构建工作流

  1. 搭建插件框架 — 创建一个以 __init__.py 为入口的包,并使用 blender_manifest.toml(Blender 4.2+ 扩展)或 bl_info 字典(传统插件)描述名称、版本和支持的 Blender 版本。
  2. 定义数据 — 创建 PropertyGroup 类来管理成组设置,并将其注册到相应的 ID 类型上(例如,bpy.types.Scene.my_addon = PointerProperty(type=MyAddonSettings))。
  3. 实现操作器 — 为每项用户操作派生 bpy.types.Operator 子类;实现 poll() 来检查是否可用,使用 invoke() 进行交互式设置,并通过 execute() 执行实际操作。
  4. 构建 UI 面板 — 派生 bpy.types.Panel 子类,在相应编辑器中显示操作器和属性(3D 视口侧栏、属性编辑器等)。
  5. 注册所有内容 — 在 classes 元组中列出所有类,并在对应的 register()/unregister() 函数中完成注册和注销;属性指针应与相应的类一并注册。
  6. 在干净的配置中测试 — 使用 --factory-startup 启动 Blender,安装并启用插件,测试每个操作器,然后禁用插件并确认没有残留。
  7. 打包并发布 — 将插件文件夹压缩为 ZIP(或构建 .zip 扩展包),并确认可通过 Blender 的 Preferences > Add-ons(或 Extensions)面板顺利安装。

插件结构

  • 将插件入口放在 __init__.py 中,并提供清晰的 register() 和 unregister() 函数,使二者严格对应(所有已注册内容都必须按相反顺序注销)。
  • 当插件规模不再简单时,将操作器、面板、属性、偏好设置和实用工具代码拆分到不同模块;再从 __init__.py 导入并注册它们。
  • 根据目标 Blender 版本和打包方式,使用 bl_info(4.2 之前的传统插件)或 blender_manifest.toml(4.2+ 扩展),不要混用两种约定。
  • 保持 UI 标签简洁;如果项目支持本地化,可使用 bpy.app.translations 或 "Category"/标签约定来本地化面向用户的文本。

API 用法

  • 使用 bpy.types.Operator 实现操作,使用 bpy.types.Panel 构建 UI 布局,并使用 bpy.types.PropertyGroup 管理相关设置。
  • 为每个操作器显式定义 bl_idname(小写,采用 category.action 格式,例如 object.apply_custom_modifier)、bl_label 和 bl_options(例如 {'REGISTER', 'UNDO'})。
  • 在 poll() 中验证上下文,确保操作器可以运行——检查是否存在活动对象、模式是否正确或选择是否有效,让操作器按钮在不可用时显示为灰色,而不是运行时报错。
  • 使用 invoke() 进行交互式设置(显示对话框、读取鼠标位置),并使用 execute() 执行实际操作;如果 invoke() 不需要额外交互,应调用 self.execute(context)。
  • 成功时返回 {'FINISHED'},失败或用户取消时始终返回 {'CANCELLED'}——不要返回单独的 None 或无法识别的字符串。
  • 读取包含修改器、形态键或其他由依赖关系图驱动的结果的最终场景状态时,使用 bpy.context.evaluated_depsgraph_get() 和 object.evaluated_get(depsgraph)。
示例:操作器、面板和注册
bl_info = {
    "name": "Random Vertex Color",
    "author": "Example",
    "version": (1, 0, 0),
    "blender": (4, 0, 0),
    "category": "Mesh",
}

import bpy
import random


class MESH_OT_random_vertex_color(bpy.types.Operator):
    """Assign a random color to the active vertex color layer"""

    bl_idname = "mesh.random_vertex_color"
    bl_label = "Randomize Vertex Colors"
    bl_options = {"REGISTER", "UNDO"}

    @classmethod
    def poll(cls, context):
        obj = context.active_object
        return (
            obj is not None
            and obj.type == "MESH"
            and obj.mode == "OBJECT"
            and obj.data.color_attributes.active_color is not None
        )

    def execute(self, context):
        obj = context.active_object
        color_layer = obj.data.color_attributes.active_color
        for data in color_layer.data:
            data.color = (random.random(), random.random(), random.random(), 1.0)
        obj.data.update()
        self.report({"INFO"}, f"Randomized colors on '{obj.name}'")
        return {"FINISHED"}


class VIEW3D_PT_random_vertex_color(bpy.types.Panel):
    bl_label = "Vertex Color Tools"
    bl_idname = "VIEW3D_PT_random_vertex_color"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Tool"

    def draw(self, context):
        layout = self.layout
        layout.operator(MESH_OT_random_vertex_color.bl_idname, icon="COLOR")


classes = (
    MESH_OT_random_vertex_color,
    VIEW3D_PT_random_vertex_color,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)


def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()

数据和属性

  • 通过 PropertyGroup 类注册自定义属性,而不是将零散的全局状态塞进模块级变量中;模块级变量在文件重新加载后不会保留,也无法安全撤销。
  • 在注册了 bl_idname 且其值与插件模块名称一致的 AddonPreferences 子类中存储插件偏好设置(API 密钥、默认路径、可跨文件保留的 UI 开关)。
  • 使用 PointerProperty、CollectionProperty 和类型完整的属性(StringProperty、FloatProperty、BoolProperty 等),并显式设置 name= 和 description=,让工具提示和 Python API 自我说明。
  • 在 unregister() 中清理自定义属性、bpy.app.handlers 条目、计时器和键位映射——添加到 bpy.types.* 类或处理器列表中的任何内容都必须显式移除。

安全性和性能

  • 未经用户明确操作,绝不执行破坏性场景操作(删除对象、覆盖文件);对于任何破坏性操作,都不得在加载时静默自动执行。
  • 避免在模态操作器中阻塞 UI 线程;对于长时间运行的操作,应使用 context.window_manager.event_timer_add() 和模态状态机,而不是紧密循环。
  • 批量处理网格编辑;以编程方式编辑网格数据时,结构性更改应使用 bmesh 模块(bmesh.from_edit_mesh、bmesh.new()),而不是逐个循环处理 mesh.vertices。
  • 避免反复扫描大型场景,或在 draw() 方法中重复计算开销很大的数据——draw() 会在每次 UI 重绘时运行,因此应缓存结果,并仅在底层数据发生变化时使缓存失效。
  • 保持文件路径可配置,并使用 Blender 路径工具(bpy.path.abspath、bpy.utils.resource_path)解析路径,而不是硬编码特定操作系统的路径。

测试和调试

  • 在干净的 Blender 配置文件(blender --factory-startup)和具有代表性的生产场景中进行测试:前者可发现对其他插件的隐藏依赖,后者可发现性能和数据结构问题。
  • 添加冒烟测试:导入插件模块,调用 register(),通过 bpy.ops 运行每个核心操作器,然后无错误、无残留状态地调用 unregister()。
  • 使用 self.report({'ERROR'}, "message") 或 {'WARNING'}/{'INFO'} 向用户反馈操作器信息,而不是打印到控制台——大多数用户都看不到控制台。
  • 将特定版本的 API 差异(例如 Blender 3.x 和 4.x 之间的 API 变化)封装在小型辅助函数中,避免在插件其他部分散布版本检查。

常见错误

  • 忘记在 unregister() 中注销类、处理器、计时器和键位映射,导致禁用插件后 Blender 处于异常状态。
  • 在 Panel.draw() 方法中修改 Blender 数据(添加对象、更改网格数据)——draw() 只能读取数据并布局 UI。
  • 未先检查 context,就假定存在活动对象、非空选择或特定模式(Object/Edit/Sculpt)。
  • 硬编码仅在开发者机器上存在的绝对资源路径,而不是使用相对路径或 bpy.utils.resource_path。

评论

支持 Markdown