前言

都说blender的python脚本编写起来很简单,虽然已经能够在脚本里调用一些api实现自动化,但实际写成插件还是有很多东西需要摸索的,这里记录一些关键知识点。

参考

先将一些重要的和有益的参考或学习资料列一下:

  1. 一篇相当棒的入门教程
    Blender Python小白向入门
  2. 主要是第2期和第3期杂志关于插件一节写的很棒
    blendercn电子杂志
  3. 其中Quickstart部分不错,不过需要一定经验后回头看才能看的更明白,其他时候主要是当作字典查询,其中有许多example值得参考
    blenderAPI
  4. 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就不细说了,propoperator两个控件用的比较多,前者用于显示各种属性值,后者就是一个按钮,按下可以执行关联的的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
最后修改:2020 年 12 月 11 日 03 : 40 PM