# How to get one UIList to control the contents of another UIList?

I have two dynamic lists. One is for strings and another one for objects. When the string changes I need the other list to show the objects assigned to the string in the first list. So I would need something like a "global 2d array", but the only way I know of in Blender is assigning properties to ex. the scene. But I'm not familiar with ways to deal with stuff like: ["BOB", ("Camera", "Light", "Cube")] using properties. How would you guys solve this? Here'a a gif of the UI:

The code is far to big to paste here, but is based on p2or's UIList code: https://www.codepile.net/pile/0J6E3WxB

It's indeed tricky getting all the details sorted but it is possible to make it work properly:

### Data Structure

I'd suggest create a nested collection for your objects as part of the main collection (users), register e.g. per scene to get your data structure working and then test using the Console in the first place:

class CUSTOM_ObjectProps(PropertyGroup):
obj_ref: PointerProperty(
name="Object",
type=bpy.types.Object)

class CUSTOM_NameProps(PropertyGroup):
object_collection: CollectionProperty(
name = "Object Collection",
type = CUSTOM_ObjectProps)

class CUSTOM_SceneProps(PropertyGroup):
user_collection: CollectionProperty(
name = "Name Collection",
type = CUSTOM_NameProps)

...
def register():
bpy.types.Scene.ctm = PointerProperty(type=CUSTOM_SceneProps)
...


Proof using the Console

# Add User
>>> user.name = "Bob"
>>> user
bpy.data.scenes['Scene'].ctm.user_collection[0]

>>> user_obj.obj_ref = C.object
>>> user_obj.name = "My Object"
>>> user_obj
bpy.data.scenes['Scene'].ctm.user_collection[0].object_collection[0]

>>> user_obj.obj_ref
bpy.data.objects['Mball']


To speed up testing, operators for adding or printing the values might become handy:

class CUSTOM_OT_PrintOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "scene.ctm_print_operator"
bl_label = "Simple Print Operator"

def execute(self, context):
scn = context.scene
custom = scn.ctm
lookup_dict = {}

for uc, user in enumerate(custom.user_collection):
print ("User Index: {}, Name: {}, Ref: {}".format(uc, user.name, user))

for oc, obj in enumerate(user.object_collection):
print ("-> Object {}, Ref {}".format(oc, obj))
lookup_dict.setdefault(user, []).append(obj.obj_ref)

# In case, dicts are nice
print ("\nLookup:", lookup_dict)
return {'FINISHED'}


Proof using the Console

>>> bpy.ops.scene.ctm_print_operator()
User Index: 0, Name: Bob, Ref: <bpy_struct, CUSTOM_NameProps("Bob")>
-> Object 0, Ref <bpy_struct, CUSTOM_ObjectProps("Bob_Mball")>
-> Object 1, Ref <bpy_struct, CUSTOM_ObjectProps("Bob_Icosphere")>

Lookup: {bpy.data.scenes['Scene'].ctm.user_collection[0]: [bpy.data.objects['Mball'], bpy.data.objects['Icosphere']]}
{'FINISHED'}


### Drawing

Hardest part is getting both lists to draw properly. Since the actual names (from the first list) don't necessarily have to be unique, we obviously can not rely on any name value. One approach is introducing a custom index property to get track of the changes and keep it in sync (required for template_list anyway).

class CUSTOM_NameProps(PropertyGroup):
active_obj_index: IntProperty()
object_collection: CollectionProperty(
name = "Object Collection",
type = CUSTOM_ObjectProps)

user = custom.user_collection[custom.active_user_index]

...

layout.row().template_list(
listtype_name = "CUSTOM_UL_Sub",
list_id = "",
dataptr = user,
propname = "object_collection",
active_dataptr = user,
active_propname = "active_obj_index",
rows = min_rows
)


Notice, when using the wrong index value pointing to any non existing item will raise exceptions.

• Thank you very much. Will need to study this in detail and get back to you. Nov 27 '19 at 22:16