Creating CollectionProperties for Custom UIList Classes.

Sorry for the second active thread I have open atm, i’ve researched this issue extensively and couldn’t find any useful information, both on the internet and through other people’s plugin code. I’m currently trying to create a custom UIList class and CollectionProperty to list different object types within my scene. I want to feed the CollectionProperty into the custom UIList class in order to select them in the list, and perform various actions depending on the one selected. I could just use a box UI type, and it’s a rather easy solution to use, but it’s not a very elegant and flexible UI element like the list type is.

Im aware of the great example posted by CoDEManX (I think thats how his name is capitalised? owo) on using custom UIList classes, which was very helpful. It doesn’t however provice any information as to how you need to structure a collection property in order for the UIList to interpret and generate the required information on the list, as it uses a pre-existing collection property for materials.

Although I can get the list UI element to appear, it seems that theres some kind of hidden property structure that informs the class on how many entries to create, as well as potentially other elements that I can’t figure out, as no entries are created in the list. Here’s my current code:

CollectionProperty Creation Code:


class GT_SetVisibilityCollection(bpy.types.PropertyGroup):    name = bpy.props.StringProperty(name="High-Poly", default="Unknown")
    name2 = bpy.props.StringProperty(name="Low-Poly", default="Unknown")
    name3 = bpy.props.StringProperty(name="Collision", default="Unknown")
    name4 = bpy.props.StringProperty(name="Cage", default="Unknown")
    name5 = bpy.props.StringProperty(name="Skeletal Mesh", default="Unknown")
    
    print(name4)
        
# Defines object-centric data generated specifically for menu operation
class GT_Object_Menu_Data(PropertyGroup):
    
    vertex_groups = EnumProperty(
        name="Select Vertex Group",
        items=GetVertexGroups,
        update=Update_ObjectVGOrigin)
        
    frozen_bases = EnumProperty(
        name="Select Frozen Base",
        items=GetFrozenItems)
        
    view_base_list = BoolProperty(
        name="View Base List",
        description="Toggles the list of every base object in the model",
        default=False)
        
    view_freeze_list = BoolProperty(
        name="View Freeze List",
        description="Toggles the list of every freeze state this high-poly object has",
        default=False)
        
    view_component_list = BoolProperty(
        name="View Component List",
        description="Toggles the list of every component object the selected base has",
        default=False)
        
    visibility_collection = CollectionProperty(
        name="Visibility Collection",
        description="Defines the object categories used in visibility changes",
        type=GT_SetVisibilityCollection)
        
    visibility_collection_index = IntProperty(
        name="Yoes",
        default=0)
        

Custom UIList code:


class UI_Components_List(bpy.types.UIList):

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        groups = datas
        
        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            
            layout.prop(groups, "name", text="", emboss=False, icon_value=icon)
            layout.label(text="Rawr", translate=False)

        # '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)



UI Creation Code


//////////////////////// - USER INTERFACE - ////////////////////////


#Generates the UI panel inside the 3D view
class GT_Tools(bpy.types.Panel):
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "objectmode"
    bl_label = "Global Tools"
    bl_category = "Game Tools"


    def draw(self, context):
        layout = self.layout
        
        scn = context.scene.GTScn
        ob = context.object.GTObj
        mnu = context.object.GTMnu
        
        obj = context.object

        layout.template_list("UI_Components_List", "default", mnu, "visibility_collection", mnu, "visibility_collection_index", rows=2, maxrows=6)        
        # ARGUMENTS >>>>>>>> CLASS NAME >> LIST ID (n/a) >> dataptr >>>>>>>>> propname >>>>> active_dataptr >>>>>   



Anyone know how to correctly structure data in a CollectionProperty for use with UILists?

Another example:

import bpy
import random
from bpy.props import IntProperty, EnumProperty, CollectionProperty, PointerProperty
from bpy.types import PropertyGroup, UIList, Panel, Operator


class Entry(PropertyGroup):
    type = EnumProperty(
        items=(
            ('A', "Option A", ""),
            ('B', "Option B", ""),
        )
    )
    val = IntProperty()
    

class Group(PropertyGroup):
    coll = CollectionProperty(type=Entry)
    index = IntProperty()


class SCENE_UL_list(UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        
        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            layout.prop(item, "name", text="", emboss=False)
            layout.prop(item, "val", text="")
            layout.prop(item, "type", text="")
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)


class SCENE_OT_list_populate(Operator):
    bl_idname = "scene.list_populate"
    bl_label = "Populate list"
    
    def execute(self, context):
        for i in range(3):
            item = context.scene.prop_group.coll.add()
            item.name = random.sample(("foo", "bar", "asdf"), 1)[0]
            item.val = random.randint(1, 100)
            item.type = 'A' if random.random() > 0.5 else 'B'
            
        return {'FINISHED'}

class SCENE_PT_list(Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "UIList Panel"
    bl_idname = "SCENE_PT_list"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

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

        layout.operator("scene.list_populate")
        layout.template_list("SCENE_UL_list", "", sce.prop_group, "coll", sce.prop_group, "index")


def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.prop_group = PointerProperty(type=Group)

def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.prop_group

if __name__ == "__main__":
    register()


Collections need to be populated by the user (or via an operator, which needs to be triggered by the user), unlike dynamic EnumProperty() which can be populated without user interaction.

1 Like

Ahh, i didn’t understand how the collection property had to be implemented, thanks a lot for clearing that up, it works now :smiley:

Sorry, to revive this old thread, but I’m trying to do this exact thing in Blender 2.80 and it’s not working. It’s just giving me this error:

ValueError: bpy_struct “Scene” registration error: prop_group could not register

It doesn’t seem to accept the “Group” class a valid type for the prop_group PointerProperty. If I register it with a different type such as bpy.types.Object, then prop_group gets registered just fine.
Anybody know what’s going on here?