Custom UI List consisting of only Geometry nodes type modifiers?

I wanted to draw a UIList consisting of modifiers. This UI List should only contain GeometryNodes modifier and those starts with a particular name like Terrain_

I have gone through multiple resources like questions asked by other users and now the below code successfully creates a UI List that consists of all the modifiers on the object

is it possible to show only those modifiers which meet these two particular conditions:-

  1. Modifier type is ‘NODES’
  2. Name of modifier starts with Terrain_

I can get the desired modifiers using this method but I don’t know how to imply this condition in the code

a = bpy.context.active_object

ml = [m for m in a.modifiers if m.type == 'NODES' and m.name.startswith("Terrain_")]

for x in ml:
    print(x.name)

Is there any way to deal with this problem??

Complete code:

import bpy

class OBJECT_OT_modifier_move(bpy.types.Operator): #move active modifier down in stack
    bl_idname = "object.modifier_move"
    bl_label = "Modifier Action"

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

    def execute(self, context):

        ob = context.object
        idx = ob.modifier_active_index
        try:
            mod = ob.modifiers[idx]
        except IndexError:
            pass
        else:
            if self.direction == 'DOWN' and idx < len(ob.modifiers) - 1:
                if bpy.ops.object.modifier_move_down(modifier=mod.name) == {'FINISHED'}:
                    ob.modifier_active_index += 1
            elif self.direction == 'UP' and idx >= 1:
                if bpy.ops.object.modifier_move_up(modifier=mod.name) == {'FINISHED'}:
                    ob.modifier_active_index -= 1                

        return {"FINISHED"}
class MODIFIER_OT_DeleteItem(bpy.types.Operator):
    """Delete the selected item from the list."""

    bl_idname = "modifier.delete_item"
    bl_label = "Deletes a scatter item"

    def execute(self, context):
        ob = context.active_object
        idx = ob.modifier_active_index
        am = ob.modifiers.active
        try:
            mod = ob.modifiers[idx]
        except IndexError:
            pass
        else:
            ob.modifiers.remove(am)
            if idx >= 1:
                ob.modifier_active_index -= 1
                
        return{'FINISHED'}


class MODIFIER_UL_listtype(bpy.types.UIList): #custom UIList type for modifiers

    def draw_item(self, context, layout, data, item, active_data, active_propname, index):
        
        modifiers = item
        
        if self.layout_type in {'DEFAULT', 'COMPACT'}:

            layout.prop(modifiers, "name", text="", emboss=False, icon="NODETREE")
            show = layout.row(align=True)
            show.prop(item, "show_viewport", text="")
            show.prop(item, "show_render", text="")

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


class OBJECT_PT_Modifiers(bpy.types.Panel): #panel draw class
    """Creates a Panel in the Object properties window"""
    bl_label = "Modifiers"
    bl_idname = "OBJECT_PT_Modifiers"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"


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

        ob = bpy.context.object
        rows = 2

        row = layout.row()
        row.template_list("MODIFIER_UL_listtype", "", ob, "modifiers", ob, "modifier_active_index", rows=rows)
        col = row.column(align=True)
        col.operator("object.modifier_add", icon='ADD', text="")# This operator will be replaced with the original which will create a geometry nodes modifier with a particular name starting with Terrain_
        col.operator("modifier.delete_item", icon='REMOVE', text="")
        col.separator()
        col.operator("object.modifier_move", text='', icon='TRIA_UP').direction = 'UP'
        col.operator("object.modifier_move", text='', icon='TRIA_DOWN').direction = 'DOWN'


def register():
    bpy.utils.register_class(OBJECT_OT_modifier_move)
    bpy.utils.register_class(MODIFIER_OT_DeleteItem)
    bpy.utils.register_class(MODIFIER_UL_listtype)
    bpy.utils.register_class(OBJECT_PT_Modifiers)
    bpy.types.Object.modifier_active_index = bpy.props.IntProperty()


def unregister():
    bpy.utils.unregister_class(OBJECT_OT_modifier_move)
    bpy.utils.unregister_class(MODIFIER_OT_DeleteItem)
    bpy.utils.unregister_class(MODIFIER_UL_listtype)
    bpy.utils.unregister_class(OBJECT_PT_Modifiers)
    del bpy.types.Object.modifier_active_index


if __name__ == "__main__":
    register()

You could probably use an enum property with a function as its list items. Then your function would return a tuple containing just the things you want… This way you can dynamically populate the list each time it draws.

1 Like

Can you please explain to me more about this?? or an example would be better for me to understand

Okay, I read over your code and all you want to do is add a ‘filter_items’ function to your UIList class. Here is an example that filters out items by the name you wanted, you can modify to filter stuff how you want based on this:

def filter_items(self, context, data, property):
        mods = getattr(data, property)
        flt_flags = [self.bitflag_filter_item] * len(mods)
        flt_order = []
        for idx, mod in enumerate(mods):
            if not mod.name.startswith("Terrain_"):
                flt_flags[idx] &= ~self.bitflag_filter_item
        return flt_flags, flt_order

Note that flt_order is used to change the order the items appear in the list, but I’m not using it in this example to do anything.

1 Like