# UIList(bpy_struct)

## Basic UIList Example

This script is the UIList subclass used to show material slots, with a bunch of additional commentaries.

Notice the name of the class, this naming convention is similar as the one for panels or menus.

Note

UIList subclasses must be registered for blender to use them.

    import bpy

    class MATERIAL_UL_matslots_example(bpy.types.UIList):
        # The draw_item function is called for each item of the collection that is visible in the list.
        #   data is the RNA object containing the collection,
        #   item is the current drawn item of the collection,
        #   icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
        #   have custom icons ID, which are not available as enum items).
        #   active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
        #   active item of the collection).
        #   active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
        #   index is index of the current item in the collection.
        #   flt_flag is the result of the filtering process for this item.
        #   Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
        #         need them.
        def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
            ob = data
            slot = item
            ma = slot.material
            # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
            if self.layout_type in {'DEFAULT', 'COMPACT'}:
                # You should always start your row layout by a label (icon + text), or a non-embossed text field,
                # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
                # We use icon_value of label, as our given icon is an integer value, not an enum ID.
                # Note "data" names should never be translated!
                if ma:
                    layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
                else:
                    layout.label(text="", translate=False, icon_value=icon)
                # And now we can add other UI stuff...
                # Here, we add nodes info if this material uses (old!) shading nodes.
                if ma and not context.scene.render.use_shading_nodes:
                    manode = ma.active_node_material
                    if manode:
                        # The static method UILayout.icon returns the integer value of the icon ID "computed" for the given
                        # RNA object.
                        layout.label(text="Node %s" % manode.name, translate=False, icon_value=layout.icon(manode))
                    elif ma.use_nodes:
                        layout.label(text="Node <none>", translate=False)
                    else:
                        layout.label(text="")
            # 'GRID' layout type should be as compact as possible (typically a single icon!).
            elif self.layout_type in {'GRID'}:
                layout.alignment = 'CENTER'
                layout.label(text="", icon_value=icon)

    # And now we can use this list everywhere in Blender. Here is a small example panel.
    class UIListPanelExample(bpy.types.Panel):
        """Creates a Panel in the Object properties window"""
        bl_label = "UIList Panel"
        bl_idname = "OBJECT_PT_ui_list_example"
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "object"

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

            obj = context.object

            # template_list now takes two new args.
            # The first one is the identifier of the registered UIList to use (if you want only the default list,
            # with no custom draw code, use "UI_UL_list").
            layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index")

            # The second one can usually be left as an empty string.
            # It's an additional ID used to distinguish lists in case you use the same list several times in a given area.
            layout.template_list("MATERIAL_UL_matslots_example", "compact", obj, "material_slots",
                                 obj, "active_material_index", type='COMPACT')

    def register():
        bpy.utils.register_class(MATERIAL_UL_matslots_example)
        bpy.utils.register_class(UIListPanelExample)

    def unregister():
        bpy.utils.unregister_class(MATERIAL_UL_matslots_example)
        bpy.utils.unregister_class(UIListPanelExample)

    if __name__ == "__main__":
        register()

## Advanced UIList Example - Filtering and Reordering

This script is an extended version of the `UIList` subclass used to show vertex groups. It is not used ‘as is’, because iterating over all vertices in a ‘draw’ function is a very bad idea for UI performances! However, it’s a good example of how to create/use filtering/reordering callbacks.

    import bpy

    class MESH_UL_vgroups_slow(bpy.types.UIList):
        # Constants (flags)
        # Be careful not to shadow FILTER_ITEM!
        VGROUP_EMPTY = 1 << 0

        # Custom properties, saved with .blend file.
        use_filter_empty = bpy.props.BoolProperty(name="Filter Empty", default=False, options=set(),
                                                  description="Whether to filter empty vertex groups")
        use_filter_empty_reverse = bpy.props.BoolProperty(name="Reverse Empty", default=False, options=set(),
                                                          description="Reverse empty filtering")
        use_filter_name_reverse = bpy.props.BoolProperty(name="Reverse Name", default=False, options=set(),
                                                         description="Reverse name filtering")

        # This allows us to have mutually exclusive options, which are also all disable-able!
        def _gen_order_update(name1, name2):
            def _u(self, ctxt):
                if (getattr(self, name1)):
                    setattr(self, name2, False)
            return _u
        use_order_name = bpy.props.BoolProperty(name="Name", default=False, options=set(),
                                                description="Sort groups by their name (case-insensitive)",
                                                update=_gen_order_update("use_order_name", "use_order_importance"))
        use_order_importance = bpy.props.BoolProperty(name="Importance", default=False, options=set(),
                                                      description="Sort groups by their average weight in the mesh",
                                                      update=_gen_order_update("use_order_importance", "use_order_name"))

        # Usual draw item function.
        def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
            # Just in case, we do not use it here!
            self.use_filter_invert = False

            # assert(isinstance(item, bpy.types.VertexGroup)
            vgroup = item
            if self.layout_type in {'DEFAULT', 'COMPACT'}:
                # Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag
                # parameter, which contains exactly what filter_items set in its filter list for this item!
                # In this case, we show empty groups grayed out.
                if flt_flag & self.VGROUP_EMPTY:
                    col = layout.column()
                    col.enabled = False
                    col.alignment = 'LEFT'
                    col.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
                else:
                    layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
                icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
                layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
            elif self.layout_type in {'GRID'}:
                layout.alignment = 'CENTER'
                if flt_flag & self.VGROUP_EMPTY:
                    layout.enabled = False
                layout.label(text="", icon_value=icon)

        def draw_filter(self, context, layout):
            # Nothing much to say here, it's usual UI code...
            row = layout.row()

            subrow = row.row(align=True)
            subrow.prop(self, "filter_name", text="")
            icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN'
            subrow.prop(self, "use_filter_name_reverse", text="", icon=icon)

            subrow = row.row(align=True)
            subrow.prop(self, "use_filter_empty", toggle=True)
            icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN'
            subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon)

            row = layout.row(align=True)
            row.label("Order by:")
            row.prop(self, "use_order_name", toggle=True)
            row.prop(self, "use_order_importance", toggle=True)
            icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN'
            row.prop(self, "use_filter_orderby_invert", text="", icon=icon)

        def filter_items_empty_vgroups(self, context, vgroups):
            # This helper function checks vgroups to find out whether they are empty, and what's their average weights.
            # TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)").
            #       Too slow for python!
            obj_data = context.active_object.data
            ret = {vg.index: [True, 0.0] for vg in vgroups}
            if hasattr(obj_data, "vertices"):  # Mesh data
                if obj_data.is_editmode:
                    import bmesh
                    bm = bmesh.from_edit_mesh(obj_data)
                    # only ever one deform weight layer
                    dvert_lay = bm.verts.layers.deform.active
                    fact = 1 / len(bm.verts)
                    if dvert_lay:
                        for v in bm.verts:
                            for vg_idx, vg_weight in v[dvert_lay].items():
                                ret[vg_idx][0] = False
                                ret[vg_idx][1] += vg_weight * fact
                else:
                    fact = 1 / len(obj_data.vertices)
                    for v in obj_data.vertices:
                        for vg in v.groups:
                            ret[vg.group][0] = False
                            ret[vg.group][1] += vg.weight * fact
            elif hasattr(obj_data, "points"):  # Lattice data
                # XXX no access to lattice editdata?
                fact = 1 / len(obj_data.points)
                for v in obj_data.points:
                    for vg in v.groups:
                        ret[vg.group][0] = False
                        ret[vg.group][1] += vg.weight * fact
            return ret

        def filter_items(self, context, data, propname):
            # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
            # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
            #   matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
            #   first one to mark VGROUP_EMPTY.
            # * The second one is for reordering, it must return a list containing the new indices of the items (which
            #   gives us a mapping org_idx -> new_idx).
            # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
            # If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
            # returning full lists doing nothing!).
            vgroups = getattr(data, propname)
            helper_funcs = bpy.types.UI_UL_list

            # Default return values.
            flt_flags = []
            flt_neworder = []

            # Pre-compute of vgroups data, CPU-intensive. :/
            vgroups_empty = self.filter_items_empty_vgroups(context, vgroups)

            # Filtering by name
            if self.filter_name:
                flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, vgroups, "name",
                                                              reverse=self.use_filter_name_reverse)
            if not flt_flags:
                flt_flags = [self.bitflag_filter_item] * len(vgroups)

            # Filter by emptiness.
            for idx, vg in enumerate(vgroups):
                if vgroups_empty[vg.index][0]:
                    flt_flags[idx] |= self.VGROUP_EMPTY
                    if self.use_filter_empty and self.use_filter_empty_reverse:
                        flt_flags[idx] &= ~self.bitflag_filter_item
                elif self.use_filter_empty and not self.use_filter_empty_reverse:
                    flt_flags[idx] &= ~self.bitflag_filter_item

            # Reorder by name or average weight.
            if self.use_order_name:
                flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name")
            elif self.use_order_importance:
                _sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)]
                flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True)

            return flt_flags, flt_neworder

base class — `bpy_struct`

subclasses — `CLIP_UL_tracking_objects`, `FILEBROWSER_UL_dir`, `GPENCIL_UL_layer`, `MASK_UL_layers`, `MATERIAL_UL_matslots`, `MESH_UL_shape_keys`, `MESH_UL_uvmaps_vcols`, `MESH_UL_vgroups`, `NODE_UL_interface_sockets`, `PARTICLE_UL_particle_systems`, `PHYSICS_UL_dynapaint_surfaces`, `RENDERLAYER_UL_renderlayers`, `RENDERLAYER_UL_renderviews`, `SCENE_UL_keying_set_paths`, `TEXTURE_UL_texpaintslots`, `TEXTURE_UL_texslots`, `UI_UL_list`

class `bpy.types.``UIList`(bpy_struct)

  UI list containing the elements of a collection

  `bitflag_filter_item`

    The value of the reserved bitflag ‘FILTER_ITEM’ (in filter_flags values)

    Type: | int in [0, inf], default 0, (readonly)

  `bl_idname`

    If this is set, the uilist gets a custom ID, otherwise it takes the name of the class used to define the uilist (for example, if the class name is “OBJECT_UL_vgroups”, and bl_idname is not set by the script, then bl_idname = “OBJECT_UL_vgroups”)

    Type: | string, default “”, (never None)

  `filter_name`

    Only show items matching this name (use ‘*’ as wildcard)

    Type: | string, default “”, (never None)

  `layout_type`
    - `DEFAULT` Default Layout, Use the default, multi-rows layout.
    - `COMPACT` Compact Layout, Use the compact, single-row layout.
    - `GRID` Grid Layout, Use the grid-based layout.

    Type: | enum in [‘DEFAULT’, ‘COMPACT’, ‘GRID’], default ‘DEFAULT’, (readonly)

  `use_filter_invert`

    Invert filtering (show hidden items, and vice-versa)

    Type: | boolean, default False

  `use_filter_show`

    Show filtering options

    Type: | boolean, default False

  `use_filter_sort_alpha`

    Sort items by their name

    Type: | boolean, default False

  `use_filter_sort_reverse`

    Invert the order of shown items

    Type: | boolean, default False

  `draw_item`(context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0)

    Draw an item in the list (NOTE: when you define your own draw_item function, you may want to check given ‘item’ is of the right type...)

    Parameters: | layout (`UILayout`, (never None)) – Layout to draw the item data (`AnyType`) – Data from which to take Collection property item (`AnyType`) – Item of the collection property icon (int in [0, inf]) – Icon of the item in the collection active_data (`AnyType`, (never None)) – Data from which to take property for the active element active_property (string, (optional argument, never None)) – Identifier of property in active_data, for the active element index (int in [0, inf], (optional)) – Index of the item in the collection flt_flag (int in [0, inf], (optional)) – The filter-flag result for this item

  `draw_filter`(context, layout)

    Draw filtering options

    Parameters: | layout (`UILayout`, (never None)) – Layout to draw the item

  `filter_items`(context, data, property)

    Filter and/or re-order items of the collection (output filter results in filter_flags, and reorder results in filter_neworder arrays)

    Parameters: | data (`AnyType`) – Data from which to take Collection property property (string, (never None)) – Identifier of property in data, for the collection
    Return (filter_flags, filter_neworder):
    | filter_flags, An array of filter flags, one for each item in the collection (NOTE: FILTER_ITEM bit is reserved, it defines whether the item is shown or not), int array of 1 items in [0, inf] filter_neworder, An array of indices, one for each item in the collection, mapping the org index to the new one, int array of 1 items in [0, inf]

Inherited Properties

`bpy_struct.id_data` |

Inherited Functions

`bpy_struct.as_pointer` `bpy_struct.driver_add` `bpy_struct.driver_remove` `bpy_struct.get` `bpy_struct.is_property_hidden` `bpy_struct.is_property_readonly` `bpy_struct.is_property_set` `bpy_struct.items` | `bpy_struct.keyframe_delete` `bpy_struct.keyframe_insert` `bpy_struct.keys` `bpy_struct.path_from_id` `bpy_struct.path_resolve` `bpy_struct.property_unset` `bpy_struct.type_recast` `bpy_struct.values`