likes
comments
collection
share

【Blender 开发】Add-on 模板代码解析

作者站长头像
站长
· 阅读数 16

Add-on 模板插件

启用该插件

【Blender 开发】Add-on 模板代码解析

其上的信息对应于后文的 bl_info 结构体

注册该插件的代码段为

# ------------------------------------------------------------------------
#    注册 Registration
# ------------------------------------------------------------------------

# 需要注册的类
classes = (
    MyProperties,
    WM_OT_HelloWorld,
    OBJECT_MT_CustomMenu,
    OBJECT_PT_CustomPanel
)

def register():
    # 导入函数
    from bpy.utils import register_class

    # 遍历要注册的类,并注册
    for cls in classes:
        register_class(cls)
        
    bpy.types.Scene.my_tool = PointerProperty(type=MyProperties)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.my_tool


if __name__ == "__main__":
    register()

插件加载后的效果

【Blender 开发】Add-on 模板代码解析

该界面 UI 对应的代码段为

  • 属性的定义
# ------------------------------------------------------------------------
#    窗口属性定义 Properties
# ------------------------------------------------------------------------

class MyProperties(PropertyGroup):

    # RNA 属性(全局属性)  x : 属性类(内容)
    my_bool: BoolProperty(
        name="开/关",
        description="我是布尔类型",
        default = False
        )

    my_int: IntProperty(
        name = "整数值",
        description="我是整数值",
        default = 23,
        min = 10,
        max = 100
        )

    my_float: FloatProperty(
        name = "浮点值",
        description = "我是浮点值",
        default = 23.7,
        min = 0.01,
        max = 30.0
        )

    my_float_vector: FloatVectorProperty(
        name = "浮点矢量值",
        description="Something",
        default=(0.0, 0.0, 0.0), 
        min= 0.0, # float
        max = 0.1
    ) 

    my_string: StringProperty(
        name="用户输入",
        description=":",
        default="",
        maxlen=1024,
        )

    my_path: StringProperty(
        name = "目录",
        description="选择一个目录:",
        default="",
        maxlen=1024,
        subtype='DIR_PATH'
        )

    my_enum: EnumProperty(
        name="下拉列表:",
        description="应用数据给属性.",
        items=[ ('OP1', "选项 1", ""),
                ('OP2', "选项 2", ""),
                ('OP3', "选项 3", ""),
               ]
        )

注意:数字属性 subtype 可选如下

  1. PIXEL
  2. PERCENTAGE
  3. ANGLE
  4. DISTANCE
  5. UNSIGNED
  6. FACTOR
  7. TIME
  8. NONE

注意:字符串属性 subtype 可选如下

  1. FILE_PATH
  2. DIR_PATH
  3. FILE_NAME
  4. BYTE_STRING
  5. PASSWORD
  6. NONE

注意:矢量属性 subtype 可选如下

  1. COLOR
  2. VELOCITY
  3. EULER
  4. XYZ
  5. NONE
  6. TRANSLATION
  7. CCELERATION
  8. QUATERNION
  9. COLOR_GAMMA
  10. DIRECTION
  11. MATRIX
  12. AXISANGLE
  13. LAYER
  • 面板布局的设置
# ------------------------------------------------------------------------
#    对象模式的面板 Panel in Object Mode
# ------------------------------------------------------------------------

class OBJECT_PT_CustomPanel(Panel):
    bl_label = "我的面板"
    bl_idname = "OBJECT_PT_custom_panel"
    bl_space_type = "VIEW_3D"   
    bl_region_type = "UI"
    bl_category = "我的小工具"
    bl_context = "objectmode"

    # poll 函数在 3D 面板更新时调用,用来判断显示面板:当前是_ 必须存在 context.object
    @classmethod
    def poll(self,context):
        return context.object is not None

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        mytool = scene.my_tool

        # 把属性放入布局
        layout.prop(mytool, "my_bool")
        layout.prop(mytool, "my_enum", text="") 
        layout.prop(mytool, "my_int")
        layout.prop(mytool, "my_float")
        layout.prop(mytool, "my_float_vector", text="")
        layout.prop(mytool, "my_string")
        layout.prop(mytool, "my_path")

        # 放置操作项
        layout.operator("wm.hello_world")

        # 放置菜单
        layout.menu(OBJECT_MT_CustomMenu.bl_idname, text="预设", icon="SCENE")

        # 放置分隔符
        layout.separator()

注意bl_space_type 字段可选的类型如下

  1. EMPTY
  2. NLA_EDITOR
  3. NODE_EDITOR
  4. INFO
  5. VIEW_3D
  6. IMAGE_EDITOR
  7. LOGIC_EDITOR
  8. FILE_BROWSER
  9. TIMELINE
  10. SEQUENCE_EDITOR
  11. PROPERTIES
  12. CONSOLE
  13. GRAPH_EDITOR
  14. CLIP_EDITOR
  15. OUTLINER
  16. DOPESHEET_EDITOR
  17. TEXT_EDITOR
  18. USER_PREFERENCES

注意bl_region_type 字段可选的类型如下

  1. WINDOW
  2. HEADER
  3. CHANNELS
  4. TEMPORARY
  5. UI
  6. TOOLS
  7. TOOL_PROPS
  8. PREVIEW

自定义操作项

点击插件的 操作项

【Blender 开发】Add-on 模板代码解析

  • 点击前的面板界面

【Blender 开发】Add-on 模板代码解析

  • 点击后控制台打印

【Blender 开发】Add-on 模板代码解析

对应代码段为

# ------------------------------------------------------------------------
#    操作项 Operators
# ------------------------------------------------------------------------

class WM_OT_HelloWorld(Operator):
    bl_label = "Print Values Operator" # 工具显示名称,也就是 F3 搜的名称
    bl_idname = "wm.hello_world"       # 工具 id,也就是控制台可以直接调用的名称,比如 bpy.ops.wm.hello_world


    # 执行函数(工具的功能)
    def execute(self, context):
        scene = context.scene
        mytool = scene.my_tool

        # 在控制台打印以下值
        print("Hello World")
        print("bool state:", mytool.my_bool)
        print("int value:", mytool.my_int)
        print("float value:", mytool.my_float)
        print("string value:", mytool.my_string)
        print("enum state:", mytool.my_enum)
        
        # 需要返回值给 bl 接受,FINISHED 代表完成,也就是正常退出的意思
        return {'FINISHED'}

自定义菜单

【Blender 开发】Add-on 模板代码解析

对应代码段为

# ------------------------------------------------------------------------
#    菜单 Menus
# ------------------------------------------------------------------------

class OBJECT_MT_CustomMenu(bpy.types.Menu):
    bl_label = "Select"
    bl_idname = "OBJECT_MT_custom_menu"

    # draw 函数用于绘制菜单
    def draw(self, context):
        # 创建一个布局
        layout = self.layout

        # 把操作项放入布局(下面是官方的操作项,你可以填自己的)
        layout.operator("object.select_all", text="Select/Deselect All").action = 'TOGGLE'
        layout.operator("object.select_all", text="Inverse").action = 'INVERT'
        layout.operator("object.select_random", text="Random")

完整代码与详细注释

代码是 Blender 自带的插件模板

bl_info = {
    "name": "Add-on Template", # 插件名称
    "description": "",         # 插件描述
    "author": "blender",       # 插件作者
    "version": (0, 0, 3),      # 插件版本
    "blender": (3, 0, 0),      # 适用于 Blender 版本
    "location": "3D View > Tools",  # 生效区域
    "warning": "",             # 本插件的警告信息(在首选项-插件-插件信息显示)
    "wiki_url": "",            # 本插件的 wiki 链接(在首选项-插件-插件信息显示)
    "tracker_url": "",         # 本插件的 tracker 链接(在首选项-插件-插件信息显示)
    "category": "Development"  # 本插件所属分类,可以(在首选项-插件-插件分类筛选)
}


import bpy  # 导入 blender 模块主体

# 从 props(bl 属性模块)导入对应的属性类
# 不导也行,你也可以这样写 myInt = bpy.props.IntProperty(XX)
from bpy.props import (StringProperty,
                       BoolProperty,
                       IntProperty,
                       FloatProperty,
                       FloatVectorProperty,
                       EnumProperty,
                       PointerProperty,
                       )

# 从 props(bl 类型模块)导入对应的 UI 类                      
from bpy.types import (Panel,
                       Menu,
                       Operator,
                       PropertyGroup,
                       )


# ------------------------------------------------------------------------
#    窗口属性定义 Properties
# ------------------------------------------------------------------------

class MyProperties(PropertyGroup):

    # RNA 属性(全局属性)  x : 属性类(内容)
    my_bool: BoolProperty(
        name="开/关",
        description="我是布尔类型",
        default = False
        )

    my_int: IntProperty(
        name = "整数值",
        description="我是整数值",
        default = 23,
        min = 10,
        max = 100
        )

    my_float: FloatProperty(
        name = "浮点值",
        description = "我是浮点值",
        default = 23.7,
        min = 0.01,
        max = 30.0
        )

    my_float_vector: FloatVectorProperty(
        name = "浮点矢量值",
        description="Something",
        default=(0.0, 0.0, 0.0), 
        min= 0.0, # float
        max = 0.1
    ) 

    my_string: StringProperty(
        name="用户输入",
        description=":",
        default="",
        maxlen=1024,
        )

    my_path: StringProperty(
        name = "目录",
        description="选择一个目录:",
        default="",
        maxlen=1024,
        subtype='DIR_PATH'
        )

    my_enum: EnumProperty(
        name="下拉列表:",
        description="应用数据给属性.",
        items=[ ('OP1', "选项 1", ""),
                ('OP2', "选项 2", ""),
                ('OP3', "选项 3", ""),
               ]
        )

# ------------------------------------------------------------------------
#    操作项 Operators
# ------------------------------------------------------------------------

class WM_OT_HelloWorld(Operator):
    bl_label = "Print Values Operator" # 工具显示名称,也就是 F3 搜的名称
    bl_idname = "wm.hello_world"       # 工具 id,也就是控制台可以直接调用的名称,比如 bpy.ops.wm.hello_world


    # 执行函数(工具的功能)
    def execute(self, context):
        scene = context.scene
        mytool = scene.my_tool

        # 在控制台打印以下值
        print("Hello World")
        print("bool state:", mytool.my_bool)
        print("int value:", mytool.my_int)
        print("float value:", mytool.my_float)
        print("string value:", mytool.my_string)
        print("enum state:", mytool.my_enum)
        
        # 需要返回值给 bl 接受,FINISHED 代表完成,也就是正常退出的意思
        return {'FINISHED'}

# ------------------------------------------------------------------------
#    菜单 Menus
# ------------------------------------------------------------------------

class OBJECT_MT_CustomMenu(bpy.types.Menu):
    bl_label = "Select"
    bl_idname = "OBJECT_MT_custom_menu"

    # draw 函数用于绘制菜单
    def draw(self, context):
        # 创建一个布局
        layout = self.layout

        # 把操作项放入布局(下面是官方的操作项,你可以填自己的)
        layout.operator("object.select_all", text="Select/Deselect All").action = 'TOGGLE'
        layout.operator("object.select_all", text="Inverse").action = 'INVERT'
        layout.operator("object.select_random", text="Random")

# ------------------------------------------------------------------------
#    对象模式的面板 Panel in Object Mode
# ------------------------------------------------------------------------

class OBJECT_PT_CustomPanel(Panel):
    bl_label = "我的面板"
    bl_idname = "OBJECT_PT_custom_panel"
    bl_space_type = "VIEW_3D"   
    bl_region_type = "UI"
    bl_category = "我的小工具"
    bl_context = "objectmode"

    # poll 函数在 3D 面板更新时调用,用来判断显示面板:当前是_ 必须存在 context.object
    @classmethod
    def poll(self,context):
        return context.object is not None

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        mytool = scene.my_tool

        # 把属性放入布局(注意和定义的顺序不一样)
        layout.prop(mytool, "my_bool")
        layout.prop(mytool, "my_enum", text="") 
        layout.prop(mytool, "my_int")
        layout.prop(mytool, "my_float")
        layout.prop(mytool, "my_float_vector", text="")
        layout.prop(mytool, "my_string")
        layout.prop(mytool, "my_path")

        # 放置操作项
        layout.operator("wm.hello_world")

        # 放置菜单
        layout.menu(OBJECT_MT_CustomMenu.bl_idname, text="预设", icon="SCENE")

        # 放置分隔符
        layout.separator()

# ------------------------------------------------------------------------
#    注册 Registration
# ------------------------------------------------------------------------

# 需要注册的类
classes = (
    MyProperties,
    WM_OT_HelloWorld,
    OBJECT_MT_CustomMenu,
    OBJECT_PT_CustomPanel
)

def register():
    # 导入函数
    from bpy.utils import register_class

    # 遍历要注册的类,并注册
    for cls in classes:
        register_class(cls)
        
    bpy.types.Scene.my_tool = PointerProperty(type=MyProperties)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.my_tool


if __name__ == "__main__":
    register()

注意:bl_info 中的 Category 字段对应可选的分类有

  • 3D View
  • Compositing
  • Lighting
  • Object
  • Rigging
  • Text Editor
  • Add Mesh
  • Development
  • Material
  • Paint
  • Scene
  • UV
  • Add Curve
  • Game Engine
  • Mesh
  • Physics
  • Sequencer
  • User Interface
  • Animation
  • Import-Export
  • Node
  • Render
  • System

(完)