Creating an UI list procedurally?

Hello i’m trying to implement an UI list feature in my addon, so let’s take the case of creating a UI list of all 'DISPLACE' modifier, for testing purpose

let’s try to show all “DISPLACE” modifiers of context object to the user in an UI list form:

this is the code i wrote for that

import bpy
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty, EnumProperty, PointerProperty, CollectionProperty

class SCATTER_UL_ui_list(bpy.types.UIList):

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            if item:
                layout= layout.row(align=True)
                layout.scale_y = 1.2

                A  = bpy.context.object
                Am = A.modifiers[item.name]                

                layout.label(text=Am.name)
                row = layout.row(align=True)
                row.prop(Am ,"show_viewport", text="" )
                row.prop(Am ,"show_render"  , text="" )
                #of course will create error because of course, will need to update this list again and again and again... 
                #the need of updating this list shouldn't be like this in the first place
                row.operator('object.modifier_remove',  text="" , icon='CANCEL').modifier = item.name 
                row.scale_x = 0.875

            else:
                layout.label(text="")



class UIListPanelExample(bpy.types.Panel):

    bl_idname      = 'TEST_PT_my_panel'
    bl_space_type  = "VIEW_3D"
    bl_category    = "Scatter BETA"
    bl_region_type = "UI"
    bl_label       = "TEST"
    bl_context     = "objectmode"

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

        obj = bpy.context.object
        coll = layout.box().column()
        coll.separator(factor=1.5)

        #particle counter
        A  = bpy.context.object
        Am = [m for m in A.modifiers if m.name[:7] == 'SCATTER']
        Am_count = str(len(Am)).zfill(2)
        As = [m for m in Am if m.particle_system.settings['is_selected'] == 1]
        As_count = str(len(As)).zfill(2)

        rwoo = coll.row()
        rwoo.separator(factor=0.5)
        rwoo.template_list("SCATTER_UL_ui_list", "", obj, "ui_list", obj, "ui_list_index")
        rwoo.separator(factor=0.5)

        rwoo = coll.row()
        rwoo.separator(factor=0.5)
        rwoobox = rwoo.box()

        rwoo2 = rwoobox.row(align=False)
        rwoo2.operator(TEST_OT_update.bl_idname, text="Redraw" , icon='SORT_DESC'  )
        rwoo2.operator("object.modifier_add", text="Add displace modif" , icon='ADD').type='DISPLACE' #+ again, the need to re update the list, over and over... 
        rwoo2.scale_y = 0.9

        rwoo.separator(factor=0.5)
        coll.separator(factor=1)




class SCATTER_ui_list_propGroup(bpy.types.PropertyGroup):
    name : StringProperty()    #name="Name",subtype='NONE',default="default name")
    idx  : IntProperty()      #name="Index",subtype='NONE',default=0,min=0)


class TEST_OT_update(bpy.types.Operator):
    bl_idname = "test.update"
    bl_label = ""
    bl_description = ""

    def execute(self, context):
        update_uilist()
        return {'FINISHED'}

#create/refresh ui list of all scatter modifier in context obj 
#need to be used on addon register, on any .blend opening, any modifier removal, any modifier creation, and change of active object
def update_uilist():

    A = bpy.context.object
    sc = [m.name for m in A.modifiers if m.type == 'DISPLACE']

    A.ui_list.clear()
    for s in sc:
        item = A.ui_list.add()
        item.idx = len(A.ui_list)
        item.name = s


def register():
    bpy.utils.register_class(TEST_OT_update)
    bpy.utils.register_class(UIListPanelExample)
    bpy.utils.register_class(SCATTER_UL_ui_list)
    bpy.utils.register_class(SCATTER_ui_list_propGroup)
    #reg UI list related
    bpy.types.Object.ui_list       = CollectionProperty(type=SCATTER_ui_list_propGroup)
    bpy.types.Object.ui_list_index = IntProperty()



def unregister():
    bpy.utils.unregister_class(TEST_OT_update)
    bpy.utils.unregister_class(UIListPanelExample)
    bpy.utils.unregister_class(SCATTER_UL_ui_list)
    bpy.utils.unregister_class(SCATTER_ui_list_propGroup)
    #unreg UI list related
    del bpy.types.Object.ui_list


if __name__ == "__main__":
    register()

    #create ui list on launch
    update_uilist()

The problem with that code is that the CollectionProperty approach require to constantly update the list. on any active object context change, on any modifier addition/removal, on blender launch, and addon load/unload

so my question is what follows: is there any procedural solutions to create an UI list witouth the need of constatly update it every time ?

a bit like this really simple code below ( in the way it is just dispaying everything in that list and nothing else )

...rest of the code...
def draw...:
    a = bpy.context.object
    displace_list = [m for m in a.modifiers if m.type == 'DISPLACE']

    for m in displace_list:
        layout.label(m.name)
        ...rest of the code...

ps: in my case, all i care in ui list are the scrolling abilities, maybe there’s a simple solution for that ?

2 Likes