Python API 总览
这篇文章揭示了Python和blender如何联系在一起,涵盖了一些API文档和例子脚本中没有明确的功能
Python in blender
Blender嵌入一个python的解释器,该解释器在Blender启动时加载并激活。这个解释器可以运行脚本来绘制用户界面,也用于Blender一些内部工具
这是一个典型的Python环境,所以如何写Python脚本的教学同样适用。Blender提供bpy模块给解释器盘,这个模块可以导入脚本并获取blender数据,类型和功能。处理blender数据的脚本必须导入这个模块4
以下是一个简单的例子,移动立方体一个顶点:
importbpy
bpy.data.objects["Cube"].data.vertices[0].co.x +=1.0
这直接修改Blender的文件的内部数据,当你在终端运行此命令时可以看到3D试图的更新
默认环境
开发你的脚本之时,了解blender如何启动其python环境很有帮助,很多python脚本与blender捆绑可以作为参考文档,因为他们使用同样的API以确保脚本将工具写入。典型的脚本包括:用户界面,导入导出,场景处理,自动化,设置自定义工具和用户定制
加载脚本
加载方式显而易见,但是要区别执行单个文件和执行模块的区别
脚本扩展Blender,它定义类型超出执行脚本的范畴,这使直接获取这些类型比导入模块从模块获取类型要困难
因此,在不需要注册类型扩展blender的情况下直接执行脚本优先考虑
这是直接在blender运行脚本的一些方式
1. 通过文本编辑器加载然后按下运行脚本
2. 在终端中加载
3. 在系统终端中执行命令行运行python文件
4. blender --python /home/me/my_script.py
运行脚本模块的方式如下:
1. 明显的方式是: 从文本编辑器和终端命令中importsome_module命令
2. 以文本块打开,勾上注册选项,这回加载blend文件
3. 直接复制到路径scripts/startup,这样在启动时候会自动导入
4. 定义成一个插件,使插件可以以python模块加载
插件
一些blender功能可选保留,同时脚本在启动时候加载,插件都放在路径script/addons,只有在用户自定义界面启动选择了才能在启动时候加载
插件和内嵌python模块的区别是插件必须包含bl_info变量,它用来读取元数据,例如名字,作者,目录和URL
用户自定义插件罗列了每一个插件bl_info的信息
具体可以从See Addons查看bl_info
通过类型集成
运行脚本编辑器中的脚本很有效,但是想要像blender内嵌功能工具使用,api提供了这个集成功能:
- bpy.types.Panel
- bpy.types.Menu
- bpy.types.Operator
- bpy.types.PropertyGroup
- bpy.types.KeyingSet
- bpy.types.RenderEngine
这是故意的限制,对于高级功能例如网格修改,物体类型,着色器节点,必须使用C/C++
Python集成Blender定义的方法对于所有类型都通用。这通过blender类型的Python子类型工作,这些blender界面的基类包括变量和函数。
例如:
importbpy
classSimpleOperator(bpy.types.Operator):
bl_idname ="object.simple_operator"
bl_label ="Tool Name"
defexecute(self, context):
print("Hello World")
return {'FINISHED'}
bpy.utils.register_class(SimpleOperator)
首先,注意通过bpy.types派生,这是一个通用的方法,这样使用可以使得我们明确这是一个操作不是注册面板。所有类属性都以bl_前缀开头。这可以用来区分Blender属性和自定义类型
然后,执行函数中以operator和当前context为参数,前缀不必在此加
最后,注册函数被调用,这将该类加载入blender,详见ClassRegistration
关于继承,blender没有利用各种继承类的限制,注册检查使用的是基类的属性和函数
例子:
importbpy
classBaseOperator:
defexecute(self, context):
print("Hello World BaseClass")
return {'FINISHED'}
classSimpleOperator(bpy.types.Operator, BaseOperator):
bl_idname ="object.simple_operator"
bl_label ="Tool Name"
bpy.utils.register_class(SimpleOperator)
注意到这些类没有定义__init__函数,然而__init__()和__del__()如果定义了就会被调用,类实例生命周期只在执行期。例如每次重绘面板都会产生一个新的实例,因此仅仅在面板实例中存储变量。然而固定变量需要存储在blender数据中,这使得blender重启的时候可以保存状态
注意:模型操作是个例外,当Blender运行时保存模型操作的实例变量而不是存在blender数据中,请看模型操作模板
当blender中注册了类,实例化类型,调用函数,都是通过blender,实际上你无法从脚本实例化类,这和其他python api不同。
importbpy
bpy.ops.object.simple_operator()
用户界面类型通过给定的context绘制,按钮窗口,文件标题,工具条等等,当该区域显示时候绘制,不能直接以python脚本调用绘制。(?不是说没法从脚本实例化吗,是的!)
注册
模块注册
Blender在启动时加载模块,要求register()和unregister()函数。这是blender唯一的通过脚本代码调用的函数,在你的python脚本模块中
一个简单的blender/python模块是这样的:
importbpy
classSimpleOperator(bpy.types.Operator):
""" See example above """
defregister():
bpy.utils.register_class(SimpleOperator)
defunregister():
bpy.utils.unregister_class(SimpleOperator)
if __name__ =="__main__":
register()
这些函数通常放在包含类注册(有时添加菜单选项)的脚本底部,你也可以为了内部目的(为你自己的工具设置数据)来使用这些函数,但是要注意当一个新的blend文件加载时注册函数无法再次运行的。(注册一次)
类注册
blender注册一个类使得类定义被加载到blender中,类可以通过存在的函数来获取:
当类被加载,可以通过bpy.types获取(使用bl_idname,不是类的名字)
加载类之后,blender运行检查确保属性和功能可以找到,属性需要有正确的类型,功能函数要有正确的参数。
大部分时候你不需要关心这个,如果确实有问题,类定义会在注册时,通过函数参数def execute(self,context,spam)抛出一个异常:
ValueError: expected Operator, SimpleOperator class "execute" function to have 2 args, found 3使用bl_idname=1会引发如下错误:
TypeError: validating class error: Operator.bl_idname expected a string type, not int
多类
上述在blender加载类,简单的例子调用 bpy.utils.register_class足够了,当有很多类和包,子模块有自己的类,这样就显得冗余了
更方便的加载bpy.utils.register_module(module)和bpy.utils.unregister_module(module)函数
一个脚本定义看了很多自己的操作,面板菜单等等,你需要这样写:
defregister():
bpy.utils.register_module(__name__)
defunregister():
bpy.utils.unregister_module(__name__)
Blender集合了各个可注册类型子类,把他们存放在定义的模块中。通过bpy.utils.register_module注册所有的该模块和子模块的类
内部类依赖
用户属性也需要被注册才能使用
# Create new property
# bpy.data.materials[0].my_custom_props.my_float
importbpy
classMyMaterialProps(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
defregister():
bpy.utils.register_class(MyMaterialProps)
bpy.types.Material.my_custom_props= bpy.props.PointerProperty(type=MyMaterialProps)
defunregister():
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
importbpy
classMyMaterialSubProps(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
classMyMaterialGroupProps(bpy.types.PropertyGroup):
sub_group = bpy.props.PointerProperty(type=MyMaterialSubProps)
defregister():
bpy.utils.register_class(MyMaterialSubProps)
bpy.utils.register_class(MyMaterialGroupProps)
bpy.types.Material.my_custom_props= bpy.props.PointerProperty(type=MyMaterialGroupProps)
defunregister():
del bpy.types.Material.my_custom_props
bpy.utils.unregister_class(MyMaterialGroupProps)
bpy.utils.unregister_class(MyMaterialSubProps)
if __name__ =="__main__":
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
自定义的属性组的子类组也有类似操作:
classMyPropGroup(bpy.types.PropertyGroup):
pass
MyPropGroup.my_float= bpy.props.FloatProperty()
等价于:
classMyPropGroup(bpy.types.PropertyGroup):
my_float = bpy.props.FloatProperty()
动态类定义(高级)
数据,例如渲染着色器定义和删除类型on the fly
for i inrange(10):
idname ="object.operator_%d"% i
deffunc(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()函数来定义类,这是一个创建类的替代语义,适用于动态创建类
调用如下操作:
>>> bpy.ops.object.operator_1()
Hello World OBJECT_OT_operator_1
{'FINISHED'}
>>> bpy.ops.object.operator_2()
Hello World OBJECT_OT_operator_2
{'FINISHED'}