[SOLVED] How to create a dynamic UIList?

TL;DR: How can I create a UIList that will dynamically list the material slots of the selected object, and clear if nothing is selected. I also need to keep track of which item of the UIList is selected, in order to display different options for the material. Like the built-in editor: https://gyazo.com/c90f965c2dda187b77d7982324b9fb82

For my addon, I need to have a custom material editor similar to the built-in one. It should show a list of the material slots of the selected object, and none if no object is selected. When a particular material is selected, the preview should change to that material, and a set of options would appear for that particular material (exactly like the built-in material editor.)

I have defined a materials list, where each item has a pointer to a material and a name of that material:

class BGV_PT_material_item(bpy.types.PropertyGroup):
    """Group of properties representing an item in the material list."""

    material_ptr: bpy.props.PointerProperty(
        name="Material", type=bpy.types.Material)
    name: bpy.props.StringProperty(name="Name", default="Unknown")

# An ordered (by material index) list of GTA V materials in an object
class BGV_UL_materials_list(bpy.types.UIList):
    def draw_item(
        self, context, layout, data, item, icon, active_data, active_propname, index
        if self.layout_type in {"DEFAULT", "COMPACT"}:
            row = layout.row()
            row.prop(item.material_ptr, "name",
                     text="", emboss=False, icon_value=layout.icon(item.material_ptr))
        elif self.layout_type in {"GRID"}:
            layout.alignment = "CENTER"
            layout.prop(item.material_ptr, "name",
                        text="", emboss=False, icon_value=layout.icon(item.material_ptr))

This operator handles getting the materials of the selected object:

class BGV_OT_get_materials(bpy.types.Operator):
    """Finds all BGV objects and retrieves eacho object's materials"""

    bl_idname = "bgv.get_materials"
    bl_label = ""

    def execute(self, context):
        scene = context.scene

        if len(context.selected_objects) == 1:
            obj = context.selected_objects[0]
            if obj.parent:
                # If object is a game model
                if "BGV_MESH" in obj.parent:
                    # Loop through the object's material slots
                    for slot in obj.material_slots:
                        material = slot.material
                        item = scene.materials_list.add()
                        item.material_ptr = material
                        item.name = material.name
                        scene.material_index = len(
                            scene.materials_list) - 1

        return {"FINISHED"}

The problem I have is that the operator only runs once. I need, not only the UIList to be updated dynamically, but also for the panel to show the settings of the selected item of the UIList (See gif at top of post).

I have scoured the internet for a solution, and am surprised to find nothing. The only “solution” I could think of is using a constantly running modal with a threaded timer running every millisecond, but that seemed to cause issues when I tried (see kaio’s solution: Question about UI lock ups when running a python script), but maybe I implemented it incorrectly. If a modal timer is indeed a good solution, please explain how to correctly implement it in my case.

Is this just not possible, or is the Blender API not meant for what I am trying to do? Thanks.

(can’t seem to mark my own post as solution)

I managed to make the list update dynamically by setting the template_list() “dataptr” param to “context.active_object”. With “material_slots” as the propname.

    "BGV_UL_materials_list", "", context.active_object, "material_slots", scene, "material_index"
1 Like

glad you got it sorted out- I read your first post and was about to reply that this is basically the exact use case that is described in the docs for UI List but you probably saw that by now :slight_smile: