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 ", 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()