发布时间:2019/10/14 10:50:04   更新时间:2020/07/31 19:49:31
本文官网地址:https://docs.blender.org/api/current/info_overview.html
本文基于Blender2.80。
提示:译解是指翻译和注解。【注解】是作者的一些个人解释,用【】符号括起来。
本文档的目的是说明Python和Blender如何配合使用,涵盖了某些功能,这些功能从阅读API参考和示例脚本可能并不明显。
Blender具有嵌入式Python解释器,该解释器在Blender启动时加载,并在Blender运行时保持活动状态。该解释器运行脚本来绘制用户界面,并且还用于Blender的一些内部工具。
Blender的嵌入式解释器提供了典型的Python环境,因此教程中有关如何编写Python脚本的代码也可以在Blender的解释器运行。Blender将其Python模块(例如bpy和mathutils)提供给嵌入式解释器,因此可以将它们导入脚本中,并可以访问Blender的数据,类和函数。处理Blender数据的脚本将需要导入模块才能正常工作。
这是一个简单的示例,该示例将连接到名为Cube的对象的顶点移动:
import bpy
bpy.data.objects["Cube"].data.vertices[0].co.x += 1.0
这将直接修改Blender的内部数据。在交互式控制台中运行此命令时,将看到3D视口更新。
在开发自己的脚本时,可能有助于了解Blender如何设置其Python环境。许多Python脚本与Blender捆绑在一起,可以用作参考,因为它们使用脚本作者编写工具时所用的相同API。脚本的典型用法包括:用户界面,导入/导出,场景操作,自动化,定义自己的工具集和定制。
在启动时,Blender会在scripts/startup/目录中扫描Python模块并将其导入。该目录的确切位置取决于您的安装。请参阅目录布局文档。
【在你的安装目录比如C:\Program Files\Blender Foundation\Blender\2.80\scripts\startup\bl_operators,可以找到这些脚本。】
这看起来似乎很明显,但是重要的是要注意直接执行脚本与将脚本作为模块导入之间的区别。
通过直接执行脚本来扩展Blender意味着脚本定义的类在脚本完成执行后仍可在Blender中使用。以这种方式使用脚本会使将来访问它们的类(例如注销它们)比将脚本作为模块导入更加困难。当脚本作为模块导入时,其类实例将保留在模块内部,以后可以通过再次导入该模块进行访问。
因此,最好避免直接执行通过注册类扩展Blender的脚本。
这里有一些直接在Blender中运行脚本的方法:
* 在文本编辑器中加载并按Run Script。
* 输入或粘贴到交互式控制台中。
* 使用Blender从命令行执行Python文件,例如:
blender --python /home/me/my_script.py
要作为模块运行:
* 最明显的方法是从文本窗口或交互式控制台执行命令import some_module
* 打开为文本块,然后勾选“注册”选项,这将与blend文件一起加载。
* 复制到其中一个目录中scripts/startup,它们将在启动时自动导入。
* 定义为附加组件,启用附加组件会将其作为Python模块加载。
Blender的某些功能最好保持为可选,除了在启动时加载的脚本外,我们还将附加组件保存在其自己的目录中scripts/addons,并且仅在从用户首选项中选择时才在启动时加载。
附加组件和内置Python模块之间的唯一区别是,附加组件必须包含bl_info变量,Blender用它来读取元数据(例如名称,作者,类别和URL)。
用户首选项加载项列表使用bl_info显示有关每个加载项的信息。
在文本编辑器中运行Python脚本对于测试很有用,但是您需要扩展Blender以使工具像其他内置功能一样易于访问。
Blender Python API允许集成:
这是有意限制的。当前,对于更高级的功能(例如网格修改器,对象类型或着色器节点),必须使用C / C ++。
对于Python集成,Blender定义了所有类型都通用的方法。这是通过创建Blender类的Python子类来工作的,该子类包含由父类指定的变量和函数,这些变量和函数已预先定义为与Blender接口。
例如:
import bpy
class SimpleOperator(bpy.types.Operator):
bl_idname = "object.simple_operator"
bl_label = "Tool Name"
def execute(self, context):
print("Hello World")
return {'FINISHED'}
bpy.utils.register_class(SimpleOperator)
首先请注意,我们子类化了一个bpy.types的成员,这对于可以与Blender集成并使用的所有类都是通用的,因此我们在注册时知道这是操作符而不是面板。
这两个类属性都以bl_前缀开头。这是用于将Blender属性与添加的属性区分开的约定。
接下来看执行函数,该函数接受一个操作符和一个当前上下文的实例。通用前缀不用于功能。
最后,调用register函数,该函数接收该类并将其加载到Blender中。请参阅类注册。
对于继承,Blender不会对所使用的类继承类型施加任何限制,注册检查将使用父类中定义的属性和函数。
类混合示例:
import bpy
class BaseOperator:
def execute(self, context):
print("Hello World BaseClass")
return {'FINISHED'}
class SimpleOperator(bpy.types.Operator, BaseOperator):
bl_idname = "object.simple_operator"
bl_label = "Tool Name"
bpy.utils.register_class(SimpleOperator)
请注意,这些类未定义__init__(self)函数。虽然__init__()和__del__()会在定义后被调用,但类实例的生命周期仅涵盖整个执行过程。例如,一个面板将在每次重绘时都有一个新实例,因此,几乎没有理由在面板实例中存储变量。相反,应将持久变量存储在Blenders ata中,以便在Blender重新启动时可以恢复状态。
注意:
模态操作符是一个例外,在Blender运行时会保留其实例变量,请参见模态操作符模板。
因此,一旦在Blender中注册了类,实例化类并调用函数就由Blender决定。实际上,您无法像大多数Python API所期望的那样从脚本中实例化这些类。
要运行操作符符,您可以通过操作符api调用它们,例如:
import bpy
bpy.ops.object.simple_operator()
为用户界面类提供了一个绘制上下文,按钮窗口,文件头,工具栏等,然后在显示该区域时绘制它们,因此Python脚本不会直接调用它们。
启动时加载的Blender模块要求register()和unregister()功能。这些是Blender从您的代码中调用的唯一函数,否则它们是常规的Python模块。
一个简单的Blender / Python模块如下所示:
import bpy
class SimpleOperator(bpy.types.Operator):
""" See example above """
def register():
bpy.utils.register_class(SimpleOperator)
def unregister():
bpy.utils.unregister_class(SimpleOperator)
if __name__ == "__main__":
register()
这些功能通常出现在包含类注册的脚本底部,有时还会添加菜单项。您也可以将它们用于内部目的,以设置自己的工具的数据,但要小心,因为加载新的blend文件时不会重新运行注册。
使用register / unregister调用,因此可以在Blender运行时切换加载项和重新加载脚本。如果将注册调用放置在脚本主体中,则将在导入时调用注册,这意味着在导入模块或将其类加载到Blender之间没有区别。
当脚本从另一个模块导入类时,这变得很麻烦,从而难以管理正在加载哪些类以及何时加载。
最后两行仅用于测试:
if __name__ == "__main__":
register()
这允许脚本直接在文本编辑器中运行以测试更改。register()将脚本作为模块导入时,此调用将不会运行,因为__main__它保留用于直接执行。
在Blender中注册类会导致将类定义加载到Blender中,并与现有功能一起使用。
加载此类后,您可以bpy.types使用bl_idname而不是此类的原始名称从中访问它。
加载类时,Blender会进行完整性检查,以确保找到了所有必需的属性和函数,属性具有正确的类型以及函数具有正确数量的参数。
通常,您不需要担心这一点,但是如果类定义存在问题,则会在注册时提出:
使用函数参数会raise异常:def execute(self, context, spam)
ValueError: expected Operator, SimpleOperator class "execute" function to have 2 args, found 3
使用bl_idname = 1会raise错误
TypeError: validating class error: Operator.bl_idname expected a string type, not int
上面已经描述了将类加载到Blender中的情况,对于简单的情况,调用bpy.utils.register_class(SomeClass)就足够了,但是当有很多类或包子模块具有自己的类时,将它们全部列出以进行注册可能很麻烦。
为了更方便地进行装载/卸载,存在bpy.utils.register_module(模块)和bpy.utils.unregister_module(模块)功能。
定义许多自己的操作符,面板菜单等的脚本,您只需要编写:
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
在内部,Blender收集可注册类型的子类,并按定义它们的模块存储它们。通过将模块名称传递给bpy.utils.register_module,Blender可以注册由此模块及其子模块创建的所有类。
定制Blender时,您可能希望将自己的设置分组在一起,毕竟,它们可能必须与其他脚本共存。要对这些属性类进行分组,需要为组内的组或组内的集合定义,您将发现自己必须处理注册/注销的顺序。
定制属性组本身就是需要注册的类。
假设您要存储自定义引擎的材料设置。
# Create new property
# bpy.data.materials[0].my_custom_props.my_float
import bpy
class MyMaterialProps(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
def register():
bpy.utils.register_class(MyMaterialProps)
bpy.types.Material.my_custom_props = bpy.props.PointerProperty(type=MyMaterialProps)
def unregister():
del bpy.types.Material.my_custom_props
bpy.utils.unregister_class(MyMaterialProps)
if __name__ == "__main__":
register()
注意
必须先注册该类,然后才能在属性中使用该类,否则将引发错误:
ValueError:
bpy_struct
"Material"
registration
error:
my_custom_props
could
not
register
# Create new property group with a sub property
# bpy.data.materials[0].my_custom_props.sub_group.my_float
import bpy
class MyMaterialSubProps(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
class MyMaterialGroupProps(bpy.types.PropertyGroup):
sub_group = bpy.props.PointerProperty(type=MyMaterialSubProps)
def register():
bpy.utils.register_class(MyMaterialSubProps)
bpy.utils.register_class(MyMaterialGroupProps)
bpy.types.Material.my_custom_props = bpy.props.PointerProperty(type=MyMaterialGroupProps)
def unregister():
del bpy.types.Material.my_custom_props
bpy.utils.unregister_class(MyMaterialGroupProps)
bpy.utils.unregister_class(MyMaterialSubProps)
if __name__ == "__main__":
register()
注意
最下层的类需要首先注册,并且unregister()是register()的镜像
可以在Blender运行时添加和删除属性,通常在注册或注销时发生,但是对于某些特殊情况,在脚本运行时修改类型可能很有用。
例如:
# add a new property to an existing type
bpy.types.Object.my_float = bpy.props.FloatProperty()
# remove
del bpy.types.Object.my_float
这对于您自己定义的PropertyGroup子类同样适用。
class MyPropGroup(bpy.types.PropertyGroup):
pass
MyPropGroup.my_float = bpy.props.FloatProperty()
…相当于:
class MyPropGroup(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
在某些情况下,数据说明符可能不在Blender中,例如在renderman shader定义中,将它们定义为类型并即时删除它们可能很有用。
for i in range(10):
idname = "object.operator_%d" % i
def func(self, context):
print("Hello World", self.bl_idname)
return {'FINISHED'}
opclass = type("DynOp%d" % i,
(bpy.types.Operator, ),
{"bl_idname": idname, "bl_label": "Test", "execute": func},
)
bpy.utils.register_class(opclass)
注意
type()被称为定义类。这是在Python中创建类的另一种语法,更适合于动态构造类。
从上一个示例中调用操作符:
>>> bpy.ops.object.operator_1()
Hello World OBJECT_OT_operator_1
{'FINISHED'}
>>> bpy.ops.object.operator_2()
Hello World OBJECT_OT_operator_2
{'FINISHED'}