# Blender Python API

Blender提供python接口,基于此接口,可以完成如下的功能:

  • 编辑用户界面可以编辑的任何数据(场景、网格、粒子等)。
  • 修改用户偏好、键盘映射和主题。
  • 使用自己的设置运行工具。
  • 创建用户界面元素,例如菜单、标题和面板。
  • 创建新工具。
  • 创建交互式工具。
  • 创建与 Blender 集成的新渲染引擎。
  • 订阅数据及其属性的更改。
  • 在现有的 Blender 数据中定义新设置。
  • 使用 Python 在 3D 视口中绘图。

# 关键概念

# 数据

# 访问数据块

你可以使用 Python API 以与动画系统或用户界面相同的方式访问 Blender 的数据;这意味着可以通过按钮更改的任何设置也可以使用 Python 进行更改。使用模块可以访问当前加载的 blend 文件中的数据bpy.data。它可以访问库数据,例如:

>>> bpy.data.objects
<bpy_collection[3], BlendDataObjects>
>>> bpy.data.scenes
<bpy_collection[1], BlendDataScenes>
>>> bpy.data.materials
<bpy_collection[1], BlendDataMaterials>

您会注意到,可以使用索引和字符串来访问集合的成员。与 Python 字典不同,这两种方法都可用;但是,成员的索引可能会在运行 Blender 时发生变化

>>> list(bpy.data.objects)
[bpy.data.objects["Cube"], bpy.data.objects["Plane"]]
>>> bpy.data.objects['Cube']
bpy.data.objects["Cube"]
>>> bpy.data.objects[0]
bpy.data.objects["Cube"]

一旦你有了数据块,比如材料、物体、集合等,它的属性就可以像使用图形界面更改设置一样访问。事实上,每个按钮的工具提示还会显示 Python 属性,这有助于找到脚本中要更改的设置。

>>> bpy.data.objects[0].name
'Camera'
>>> bpy.data.scenes["Scene"]
bpy.data.scenes['Scene']
>>> bpy.data.materials.new("MyMaterial")
bpy.data.materials['MyMaterial']

为了测试要访问哪些数据,使用 Python 控制台(它有自己的空间类型)很有用。它支持自动完成,让您可以快速探索文件中的数据。

通过控制台可以快速找到的数据路径示例:

>>> bpy.data.scenes[0].render.resolution_percentage
100
>>> bpy.data.scenes[0].objects["Torus"].data.vertices[0].co.x
1.0

# 数据创建/删除

当你熟悉其他 Python API 时,你可能会惊讶于 bpy API 中的新数据块无法通过调用类来创建:

>>> bpy.types.Mesh()
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
TypeError: bpy_struct.__new__(type): expected a single argument

这是 API 设计的故意设计。Blender Python API 无法创建存在于主 Blender 数据库(通过 访问bpy.data)之外的 Blender 数据,因为这些数据由 Blender 管理(保存、加载、撤消、附加等)。

通过 meshs 中的集合方法添加和删除数据bpy.data,例如:

>>> mesh = bpy.data.meshes.new(name="MyMesh")
>>> print(mesh)
<bpy_struct, Mesh("MyMesh.001")>
>>> bpy.data.meshes.remove(mesh)

# 自定义属性

Python 可以访问任何具有 ID 的数据块(可以链接并从中访问的数据bpy.data)上的属性。分配属性时,您可以选择自己的名称,这些名称将在需要时创建,如果它们已经存在,则会被覆盖。

该数据与 blend 文件一起保存并与对象一起复制,例如:

bpy.context.object["MyOwnProperty"] = 42

if "SomeProp" in bpy.context.object:
    print("Property found")

# Use the get function like a Python dictionary
# which can have a fallback value.
value = bpy.data.scenes["Scene"].get("test_prop", "fallback value")

# dictionaries can be assigned as long as they only use basic types.
collection = bpy.data.collections.new("MyTestCollection")
collection["MySettings"] = {"foo": 10, "bar": "spam", "baz": {}}

del collection["MySettings"]

请注意,这些属性只能分配基本 Python 类型:

  • 整数、浮点数、字符串
  • 整数或浮点数数组
  • 字典(仅支持字符串键,值也必须是基本类型)

这些属性在 Python 之外有效。它们可以通过曲线制作动画或用于驱动程序路径。

# 语境

虽然能够直接按名称或列表访问数据很有用,但更常见的做法是根据用户的选择进行操作。上下文始终可用bpy.context,可用于获取活动对象、场景、工具设置以及许多其他属性。

一些常见的用例是:

bpy.context.object
bpy.context.selected_objects
bpy.context.visible_bones

请注意,上下文是只读的,这意味着这些值不能直接修改。但可以通过运行 API 函数或使用数据 API 来更改它们。

因此会引发错误。但可以按预期工作。bpy.context.active_object = objbpy.context.view_layer.objects.active = obj

上下文属性会根据访问位置而变化。3D 视口具有与 Python 控制台不同的上下文成员,因此在访问上下文属性时请注意确保用户状态已知。

请参阅bpy.contextAPI 参考。

# 操作符(工具)

操作符是用户通常通过按钮、菜单项或快捷键访问的工具。从用户的角度来看,它们是一种工具,但 Python 可以通过模块使用自己的设置来运行它们bpy.ops。

例子:


>>> bpy.ops.mesh.flip_normals()
{'FINISHED'}
>>> bpy.ops.mesh.hide(unselected=False)
{'FINISHED'}
>>> bpy.ops.object.transform_apply()
{'FINISHED'}

提示 运算符速查表列出 了所有运算符及其默认值(以 Python 语法表示),以及生成的文档。这是了解 Blender 所有运算符的好方法。

# 操作符 Poll(

许多操作符都有一个“轮询”函数,用于检查光标是否位于有效区域或对象是否处于正确模式(编辑模式、权重绘制模式等)。当操作符的轮询函数在 Python 中失败时,会引发异常。

例如,bpy.ops.view3d.render_border()从控制台调用会引发以下错误:

RuntimeError: Operator bpy.ops.view3d.render_border.poll() failed, context is incorrect

在这种情况下,上下文必须是带有活动摄像机的 3D 视口。

为了避免在调用运算符的任何地方使用 try-except 子句,您可以调用运算符自己的poll()函数来检查它是否可以在当前上下文中运行该运算符。

if bpy.ops.view3d.render_border.poll():
    bpy.ops.view3d.render_border()

# 集成

Python脚本可以通过以下方式与Blender集成:

  • 通过定义渲染引擎。
  • 通过定义运算符。
  • 通过定义菜单、标题和面板。
  • 通过在现有菜单、标题和面板中插入新按钮。

在 Python 中,这是通过定义一个类来实现的,该类是现有类型的子类。

示例运算

import bpy


def main(context):
    for ob in context.scene.objects:
        print(ob)


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        main(context)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label)


# Register and add to the "object" menu (required to also use F3 search "Simple Object Operator" for quick access).
def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.simple_operator()

该脚本一旦运行,SimpleOperator就会在 Blender 中注册,可以从 Operator Search 中调用或添加到工具栏。

运行脚本:

  1. 启动 Blender 并切换到 Scripting 工作区。

  2. 单击文本编辑器中的新建按钮来创建一个新的文本数据块。

  3. 从上面的代码复制并将其粘贴到文本编辑器中。

  4. 单击运行脚本按钮。

  5. 将光标移到 3D 视图中,打开“操作符搜索”菜单,然后输入“Simple”。

  6. 点击搜索到的“简单操作符”项。

也可以看看 带有前缀的类成员bl_记录在 API 参考中bpy.types.Operator。

# 示例面板

面板被注册为一个类,就像一个操作符一样。请注意bl_用于设置其显示上下文的额外变量。

import bpy


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout

        obj = context.object

        row = layout.row()
        row.label(text="Hello world!", icon='WORLD_DATA')

        row = layout.row()
        row.label(text="Active object is: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")

        row = layout.row()
        row.operator("mesh.primitive_cube_add")


def register():
    bpy.utils.register_class(HelloWorldPanel)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

运行脚本:

  1. 启动 Blender 并切换到 Scripting 工作区。

  2. 单击文本编辑器中的新建按钮来创建一个新的文本数据块。

  3. 从上面的代码复制并将其粘贴到文本编辑器中。

  4. 单击运行脚本按钮。

查看结果:

  1. 选择默认立方体。

  2. 单击按钮面板中的对象属性图标(最右边;显示为一个小立方体)。

  3. 向下滚动即可看到名为“Hello World Panel”的面板。

  4. 更改对象名称也会更新Hello World Panel 的名称:字段。

注意行分布以及通过代码定义的标签和属性。

也可看看 bpy.types.Panel

# 类型

Blender 定义了许多 Python 类型,但也使用 Python 原生类型。Blender 的 Python API 可分为三类。

原生类型 在简单的情况下,返回数字或字符串作为自定义类型会很麻烦,因此将它们作为普通 Python 类型进行访问。

  • Blender 浮点数、整数、布尔值 -> 浮点数、整数、布尔值

  • Blender 枚举器 -> 字符串

>>> C.object.rotation_mode = 'AXIS_ANGLE'
  • Blender 枚举器(多个)-> 字符串集
# setting multiple camera overlay guides
bpy.context.scene.camera.data.show_guide = {'GOLDEN', 'CENTER'}

# passing as an operator argument for report types
self.report({'WARNING', 'INFO'}, "Some message!")

# 内部类型

bpy.types.bpy_struct用于 Blender 数据块和集合。还用于包含其自身属性的数据:集合、网格、骨骼、场景等。

有两种主要类型可以包装 Blender 的数据,一种用于数据块(内部称为bpy_struct),另一种用于属性。

>>> bpy.context.object
bpy.data.objects['Cube']
>>> C.scene.objects
bpy.data.scenes['Scene'].objects

请注意,这些类型引用了 Blender 的数据,因此对它们的修改是立即可见的。

# Mathutils

可从 访问的mathutils是向量、四元数、欧拉角、矩阵和颜色类型。某些属性(例如bpy.types.Object.location、 bpy.types.PoseBone.rotation_euler和 )bpy.types.Scene.cursor_location 可作为特殊数学类型访问,这些类型可一起使用并以各种有用的方式进行操作。

矩阵、向量乘法的示例:

bpy.context.object.matrix_world @ bpy.context.object.data.verts[0].co

笔记 mathutils 类型保留对 Blender 内部数据的引用,因此可以应用更改。 例子:

# modifies the Z axis in place.

bpy.context.object.location.z += 2.0

# location variable holds a reference to the object too.

location = bpy.context.object.location location *= 2.0

# Copying the value drops the reference so the value can be passed to

# functions and modified without unwanted side effects.

location = bpy.context.object.location.copy()

## 动画片
通过Python添加关键帧有两种方法。

第一种是直接通过关键属性,就像用户从按钮插入关键帧一样。您也可以手动创建曲线和关键帧数据,然后将路径设置为属性。以下是两种方法的示例。两者都在活动对象的 Z 轴上插入关键帧。

简单示例:

obj = bpy.context.object obj.location[2] = 0.0 obj.keyframe_insert(data_path="location", frame=10.0, index=2) obj.location[2] = 1.0 obj.keyframe_insert(data_path="location", frame=20.0, index=2)

使用低级函数:

obj = bpy.context.object obj.animation_data_create() obj.animation_data.action = bpy.data.actions.new(name="MyAction") fcu_z = obj.animation_data.action.fcurves.new(data_path="location", index=2) fcu_z.keyframe_points.add(2) fcu_z.keyframe_points[0].co = 10.0, 0.0 fcu_z.keyframe_points[1].co = 20.0, 1.0