【Blender开发】UI:使用UIList 基础

https://sinestesia.co/blog/tutorials/using-uilists-in-blender/

完整代码

我偷偷加了个清除所有项,自己研究

【Blender开发】UI:使用UIList 基础

import bpy
from bpy.props import StringProperty, IntProperty, CollectionProperty
from bpy.types import PropertyGroup, UIList, Operator, Panel

bl_info = {
    "name": "test",
    "author": "yl",
    "description": "",
    "blender": (3, 0, 0),
    "version": (0, 0, 1),
    "location": "",
    "warning": "",
    "category": "Yuelili",
    "doc_url": "https://yuelili.com"
}

class ListItem(PropertyGroup):
    """用于列表中单个项目的属性组"""

    name: StringProperty(
        name="Name",
        description="项目名称",
        default="未命名")

    random_prop: StringProperty(
        name="Porperty",
        description="其他属性",
        default="")

class MY_UL_List(UIList):
    """ UI List 测试 """

    def draw_item(self, context, layout, data, item, icon, active_data,
                  active_propname, index):
        # 这里可以自定义图标,取决于你的项目...
        custom_icon = 'OBJECT_DATAMODE'

        # draw_item必须处理三种布局类型... 通常'DEFAULT'和'COMPACT'可以共享同一个代码。

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            # 开始行布局:以标签(图标+文本)或非浮雕的文本字段。
            # 这样使该行在列表中容易被选择! 后者还可以让通过ctrl点击重命名。
            # 使用标签的icon_value,因为给定的图标是一个整数值,而不是一个枚举ID。
            layout.label(text=item.name, icon=custom_icon)

        # 'GRID'布局类型应尽可能紧凑(通常是一个单一的图标!)
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon=custom_icon)

class LIST_OT_NewItem(Operator):
    """ 添加子项到列表 """

    bl_idname = "my_list.new_item"
    bl_label = "添加一项"

    def execute(self, context):

        context.scene.my_list.add()

        return{'FINISHED'}

class LIST_OT_DeleteItem(Operator):
    """ 从列表中删除选中项 """

    bl_idname = "my_list.delete_item"
    bl_label = "Deletes an item"

    @classmethod
    def poll(cls, context):
        return context.scene.my_list

    def execute(self, context):
        # 从scene接收collection和list_index
        my_list = context.scene.my_list
        index = context.scene.list_index

        # 删除项,更改当前选择项索引
        my_list.remove(index)
        context.scene.list_index = min(max(0, index - 1), len(my_list) - 1)

        return{'FINISHED'}

class LIST_OT_MoveItem(Operator):
    """ 在列表中移动项目 """

    bl_idname = "my_list.move_item"
    bl_label = "Move an item in the list"

    direction: bpy.props.EnumProperty(items=(('UP', 'Up', ""),
                                             ('DOWN', 'Down', ""),
                                             ("CLEAR","Clear","")
                                      ))

    @classmethod
    def poll(cls, context):
        return context.scene.my_list

    def move_index(self):
        """ 移动项目索引,并限制在0~项目数 """
        my_list = bpy.context.scene.my_list
        index = bpy.context.scene.list_index
        list_length = len(my_list) - 1

        if self.direction == 'CLEAR':
            my_list.clear()

        new_index = index + (-1 if self.direction == 'UP' else 1)

        bpy.context.scene.list_index = max(0, min(new_index, list_length))

    def execute(self, context):
        my_list = context.scene.my_list
        index = context.scene.list_index

        neighbor = index + (-1 if self.direction == 'UP' else 1)
        my_list.move(neighbor, index)
        self.move_index()
        return{'FINISHED'}

class PT_ListExample(Panel):
    """ UI list测试面板 """

    bl_label = "UI_List 示例"
    bl_idname = "SCENE_PT_LIST_DEMO"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

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

        row = layout.row()
        # template_list 现在需要两个新参数。
        # 第一个参数:注册UIList的要用的标识符(如果你只要默认列表,没有自定义的绘制代码,使用 "UI_UL_list")。
        # 第二个参数:通常可以保留为空字符串。
        # 这是个额外的ID,用于区分列表,以防你在某个区域多次使用同一个列表。
        row.template_list("MY_UL_List", "The_List", scene,
                          "my_list", scene, "list_index")

        row = layout.row()
        row.operator('my_list.new_item', text='NEW')
        row.operator('my_list.delete_item', text='REMOVE')
        row.operator('my_list.move_item', text='UP').direction = 'UP'
        row.operator('my_list.move_item', text='DOWN').direction = 'DOWN'
        row.operator('my_list.move_item', text='CLEAR').direction = 'CLEAR'

        if scene.list_index >= 0 and scene.my_list:
            item = scene.my_list[scene.list_index]

            row = layout.row()
            row.prop(item, "name")
            row.prop(item, "random_prop")

# 注册

def register():

    bpy.utils.register_class(ListItem)
    bpy.utils.register_class(MY_UL_List)
    bpy.utils.register_class(LIST_OT_NewItem)
    bpy.utils.register_class(LIST_OT_DeleteItem)
    bpy.utils.register_class(LIST_OT_MoveItem)
    bpy.utils.register_class(PT_ListExample)

    bpy.types.Scene.my_list = CollectionProperty(type=ListItem)
    bpy.types.Scene.list_index = IntProperty(name="Index for my_list",
                                             default=0)

def unregister():

    del bpy.types.Scene.my_list
    del bpy.types.Scene.list_index

    bpy.utils.unregister_class(ListItem)
    bpy.utils.unregister_class(MY_UL_List)
    bpy.utils.unregister_class(LIST_OT_NewItem)
    bpy.utils.unregister_class(LIST_OT_DeleteItem)
    bpy.utils.unregister_class(LIST_OT_MoveItem)
    bpy.utils.unregister_class(PT_ListExample)

if __name__ == "__main__":
    register()

 

 

全流程

定义属性

创建一些属性来保存列表中的数据。

【Blender开发】UI:使用UIList 基础

class ListItem(PropertyGroup):
    """代表列表中一个项目的属性组"""

    name: StringProperty(
        name="Name",
        description="项目名称",
        default="未命名")

    random_prop: StringProperty(
        name="Porperty",
        description="其他属性",
        default="")

我们需要一个 Collection 属性来保存列表数据,以及 Integer 属性来保存索引号。

来添加到register()函数中。

def register():
    bpy.types.Scene.my_list = CollectionProperty(type = ListItem)
    bpy.types.Scene.list_index = IntProperty(name = "Index for my_list",
                                             default = 0)

 

列表小部件

使用draw_item函数

  • layout: UILayout, 不会为None,用于绘制元素的布局。
  • data:AnyType,包含在集合属性中的对象(RNA 对象)。
  • item:AnyType,集合(collection)中当前绘制的项目(item) 。
  • icon:整数 [0, inf],集合中项目的图标。如材料或纹理有自定义的图标ID。
  • active_data:AnyType, 不会为None,包含在集合内活动属性的RNA对象。
  • active_property:字符串,可选,Identifier of property in active_data, for the active element。
  • index: 整数 [0, inf],可选,集合中当前项目的索引。
  • flt_flag: 整数 [0, inf],可选,The filter-flag result for this item 。
class MY_UL_List(UIList):
    """ UIList测试 """

    def draw_item(self, context, layout, data, item, icon, active_data,
                  active_propname, index):
        # 这里可以自定义图标,取决于你的项目...
        custom_icon = 'OBJECT_DATAMODE'

        # 确保你的代码支持这3种layout类型
        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            layout.label(text=item.name, icon=custom_icon)

        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon=custom_icon)

此时可以使用temple_list函数调用

        row = layout.row()
        row.template_list("MY_UL_List", "The_List", scene,
                          "my_list", scene, "list_index")

让用户修改列表

添加项目非常简单。只需调用列表中的 add() 方法。

class LIST_OT_NewItem(Operator):
    """ 添加子项到列表 """

    bl_idname = "my_list.new_item"
    bl_label = "添加一项"

    def execute(self, context):

        context.scene.my_list.add()

        return{'FINISHED'}

删除项目

首先需要检查是否有要删除的内容。

使用 poll() 方法检查列表中是否至少有一项。 poll() 还会自动禁用 ui 中的操作项按钮。

通过使用列表索引调用 remove() 来完成的(也就是 ui 中选择的内容)。

最后必须修复索引。0<=值<列表长度

class LIST_OT_DeleteItem(Operator):
    """ 从列表中删除选中项 """

    bl_idname = "my_list.delete_item"
    bl_label = "Deletes an item"

    @classmethod
    def poll(cls, context):
        return context.scene.my_list

    def execute(self, context):
        # 从scene接收collection和list_index
        my_list = context.scene.my_list
        index = context.scene.list_index

        # 删除项,更改当前选择项索引
        my_list.remove(index)
        context.scene.list_index = min(max(0, index - 1), len(my_list) - 1)

        return{'FINISHED'}

移动项目

需要更多考虑,因为必须确保索引正确。

move() 方法允许在两个不同的索引处交换项目。

最后调用自己的 move_index() 方法来更改索引,将其限制在 0 和列表长度之间。

class LIST_OT_MoveItem(Operator):
    """ 在列表中移动项目 """

    bl_idname = "my_list.move_item"
    bl_label = "Move an item in the list"

    direction: bpy.props.EnumProperty(items=(('UP', 'Up', ""),
                                             ('DOWN', 'Down', ""),
                                             ("CLEAR","Clear","")
                                      ))

    @classmethod
    def poll(cls, context):
        return context.scene.my_list

    def move_index(self):
        """ 移动项目索引,并限制在0~项目数 """
        my_list = bpy.context.scene.my_list
        index = bpy.context.scene.list_index
        list_length = len(my_list) - 1

        if self.direction == 'CLEAR':
            my_list.clear()

        new_index = index + (-1 if self.direction == 'UP' else 1)

        bpy.context.scene.list_index = max(0, min(new_index, list_length))

    def execute(self, context):
        my_list = context.scene.my_list
        index = context.scene.list_index

        neighbor = index + (-1 if self.direction == 'UP' else 1)
        my_list.move(neighbor, index)
        self.move_index()
        return{'FINISHED'}

项目数据

显示列表项中的数据也很容易,只需使用索引即可。但要确保有效

        if scene.list_index >= 0 and scene.my_list:
            item = scene.my_list[scene.list_index]

            row = layout.row()
            row.prop(item, "name")
            row.prop(item, "random_prop")

 

给TA充电
共{{data.count}}人
人已充电
BlenderBlender开发

【Blender开发】UI:各种按键名称

2022-1-16 10:46:39

BlenderBlender开发

【Blender开发】属性组转字典格式

2022-1-17 3:01:53

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
搜索