前言
都说blender的python脚本编写起来很简单,虽然已经能够在脚本里调用一些api实现自动化,但实际写成插件还是有很多东西需要摸索的,这里记录一些关键知识点。
参考
先将一些重要的和有益的参考或学习资料列一下:
- 一篇相当棒的入门教程
Blender Python小白向入门 - 主要是第2期和第3期杂志关于插件一节写的很棒
blendercn电子杂志 - 其中Quickstart部分不错,不过需要一定经验后回头看才能看的更明白,其他时候主要是当作字典查询,其中有许多example值得参考
blenderAPI - blender软件中自带的Templates
概览
blender插件大部分内容都是写在class中的,无论是新建一个api接口,还是绘制一个新的界面,都是新建一个class,然后继承相应的类,所以找到相应的类,是编写插件的第一步。
然后新写的类都要通过下面的注册函数register才能成为blender的一部分,有注册就有注销,注册函数在启用插件时激活,注销函数在取消启用插件时激活。
bpy.utils.register_class(CLASS_NAME)
bpy.utils.unregister_class(CLASS_NAME)
下面是一个在顶部添加语言切换功能的面板示例代码,关键的部分有继承bpy.types.Header,定义bl_space_type等内容,这些参数可以通过查阅api获取,或者从别人的代码参考过来,绘制界面就是重写draw()函数。
class LanguageChange(bpy.types.Header):
bl_space_type = "TOPBAR"
bl_idname = "MY_HT_LanguageChange"
def draw(self, context):
if context.region.alignment != "RIGHT":
self.layout.prop(context.preferences.view,
"use_translate_interface", text="Language")
传入的context就表示当前上下文,通过打印dir(context)可知其包涵以下属性和方法:
['__doc__', '__module__', '__slots__', 'active_annotation_layer', 'active_base', 'active_bone', 'active_editable_fcurve', 'active_gpencil_frame', 'active_gpencil_layer', 'active_object', 'active_operator', 'active_pose_bone', 'annotation_data', 'annotation_data_owner', 'area', 'bl_rna', 'blend_data', 'collection', 'copy', 'edit_object', 'editable_bones', 'editable_fcurves', 'editable_gpencil_layers', 'editable_gpencil_strokes', 'editable_objects', 'engine', 'evaluated_depsgraph_get', 'gizmo_group', 'gpencil_data', 'gpencil_data_owner', 'image_paint_object', 'layer_collection', 'mode', 'object', 'objects_in_mode', 'objects_in_mode_unique_data', 'particle_edit_object', 'pose_object', 'preferences', 'region', 'region_data', 'rna_type', 'scene', 'screen', 'sculpt_object', 'selectable_objects', 'selected_bones', 'selected_editable_bones', 'selected_editable_fcurves', 'selected_editable_objects', 'selected_editable_sequences', 'selected_nla_strips', 'selected_objects', 'selected_pose_bones', 'selected_pose_bones_from_active_object', 'selected_sequences', 'selected_visible_fcurves', 'sequences', 'space_data', 'tool_settings', 'vertex_paint_object', 'view_layer', 'visible_bones', 'visible_fcurves', 'visible_gpencil_layers', 'visible_objects', 'visible_pose_bones', 'weight_paint_object', 'window', 'window_manager', 'workspace']
界面布局layout就不细说了,prop
和operator
两个控件用的比较多,前者用于显示各种属性值,后者就是一个按钮,按下可以执行关联的的api,这两个控件也不细说了,可以查阅api文档。
自定义方法
到目前位置我们还只能调用blender已有的属性和方法的api,我们实现自己的功能必然需要新建一个自己的api,同样新建api也是新写一个类,如下所示,继承bpy.types.Operator,然后定义bl_idname等内容,执行的具体代码在execute()函数中编写,同样这个类写完后记得注册后才能调用。
class PointsImport(bpy.types.Operator):
bl_idname = 'obj.import_points'
bl_label = '导入点云'
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
print('我的代码')
return {'FINISHED'}
调用的方法是在界面类里调用layout.operator("obj.import_points")
,可以看到,bl_idname 就是注册后api的名字。
自定义变量
除了自定义方法,有时候我们还需要自定义变量,可以参考文档 https://docs.blender.org/api/current/bpy.props.html 这一节,就是在方法的类中通过bpy.props.FloatProperty
等函数定义变量,调用的时候需要将实例赋值给一个变量,然后通过变量去调用和修改,可以参考文档的示例代码。
有时候我们希望从界面视图中输入一个值,然后将这个值作为自定义api的一个输入参数,如果在界面中直接调用我们创建的方法的属性变量,你就会发现在界面中这个值只能显示出来而不能修改。
要想获得一个可以在界面中修改的属性,需要在blender的data中创建一个数据,方法如下,在Scene中加入了一个变量,或者在WindowManager中添加一个变量,当然在其他数据块中添加变量也可以,添加的变量可以在blender api data面板中进行查看。
bpy.types.Scene.points_name = bpy.props.StringProperty()
bpy.types.WindowManager.points_name = bpy.props.StringProperty()
然后将这个值再赋值给方法的属性就可以了,参考我的下面代码。
class MyPanel(bpy.types.Panel):
bl_label = "面板" # 面板显示的名称
bl_idname = "MY_PT_Import" # 面板的id,每个面版类的id应保持唯一,建议格式大写NAME_PT_id
bl_space_type = "VIEW_3D" # 面板所处的模式,"VIEW_3D"=3D视窗
bl_region_type = "UI" # 面板所在的位置,"UI"=N面板
bl_category = "点云" # 面板所在的标签页
bpy.types.Scene.points_name = bpy.props.StringProperty(default='yp')
bpy.types.Scene.points_file_path = bpy.props.StringProperty(
default=r'C:\Users\kizx\Desktop\Projects\cv\ypa.npy', subtype='FILE_PATH')
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(context.scene, "points_name", text="名称")
row = layout.row()
row.prop(context.scene, "points_file_path", text="点云文件")
row = layout.row()
pc = row.operator("obj.import_points")
pc.path = context.scene.points_file_path
pc.name = context.scene.points_name