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
scene.materials_list.clear()
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.