I have a script (from Populate UIlist with Certain Collections) that creates new collections into the scene and populate the UIlist with them which can be selected via the ui. Everything is working properly until I rename one of the already created collection, then that new renamed collection cannot be selected anymore.
After I rename a collection and try to selecte it via ui I get this message in the console:
File "\Text", line 34, in update_func TypeError: bpy_struct: item.attr = val: ViewLayer.active_layer_collection does not support a 'None' assignment LayerCollection type File "\Text", line 18, in update_func
Here is the script:
import bpy
from bpy.props import (IntProperty,
BoolProperty,
StringProperty,
CollectionProperty,
PointerProperty)
from bpy.types import (Operator,
Panel,
PropertyGroup,
UIList)
-------------------------------------------------------------------
Functions
-----------------------------------------------------------------
def update_func(self, context):
scn = context.scene
idx = scn.custom_index
item = scn.custom[idx]
def recurLayerCollection(layerColl, collName):
found = None
if (layerColl.name == collName):
return layerColl
for layer in layerColl.children:
found = recurLayerCollection(layer, collName)
if found:
return found
layer_collection = bpy.context.view_layer.layer_collection
layerColl = recurLayerCollection(layer_collection, item.name)
bpy.context.view_layer.active_layer_collection = layerColl
-------------------------------------------------------------------
Operators
-------------------------------------------------------------------
class CUSTOM_OT_actions(Operator):
"""Move items up and down, add and remove"""
bl_idname = "custom.list_action"
bl_label = "List Actions"
bl_description = "Move items up and down, add and remove"
bl_options = {'REGISTER'}
action: bpy.props.EnumProperty(
items=(
('REMOVE', "Remove", ""),
('ADD', "Add", "")))
def invoke(self, context, event):
scn = context.scene
idx = scn.custom_index
def find_layer_collection_recursive(find, col):
for c in col.children:
if c.collection == find:
return c
return None
# List of object references
objs = bpy.context.selected_objects
# create the collection
my_collection = bpy.data.collections.new("MY CUSTOM NAME")
# link it to the scene
bpy.context.scene.collection.children.link(my_collection)
# now find the matching layer collection for the collection you created
found = find_layer_collection_recursive(my_collection, bpy.context.view_layer.layer_collection)
if found:
# once it's found, set the active layer collection to the one we found
bpy.context.view_layer.active_layer_collection = found
# Loop through all objects
for ob in objs:
my_collection.objects.link(ob)
act_coll = context.view_layer.active_layer_collection.collection
item = scn.custom.add()
item.coll_ptr = act_coll
item.name = item.coll_ptr.name
scn.custom_index = (len(scn.custom)-1)
info = '%s added to list' % (item.name)
self.report({'INFO'}, info)
return {"FINISHED"}
class CUSTOM_OT_clearList(Operator):
"""Clear all items of the list"""
bl_idname = "custom.clear_list"
bl_label = "Clear List"
bl_description = "Clear all items of the list"
bl_options = {'INTERNAL'}
@classmethod
def poll(cls, context):
return bool(context.scene.custom)
def invoke(self, context, event):
return context.window_manager.invoke_confirm(self, event)
def execute(self, context):
if bool(context.scene.custom):
context.scene.custom.clear()
self.report({'INFO'}, "All items removed")
else:
self.report({'INFO'}, "Nothing to remove")
return{'FINISHED'}
-------------------------------------------------------------------
Drawing
-------------------------------------------------------------------
class CUSTOM_UL_items(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
split = layout.split(factor=0.3)
split.label(text="Index: %d" % (index))
split.prop(item.coll_ptr, "name", text="", emboss=False, icon="COLLECTION_NEW")
def invoke(self, context, event):
pass
class CUSTOM_PT_objectList(Panel):
"""Adds a custom panel to the TEXT_EDITOR"""
bl_idname = 'TEXT_PT_my_panel'
bl_space_type = "TEXT_EDITOR"
bl_region_type = "UI"
bl_label = "Custom Object List Demo"
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
rows = 2
row = layout.row()
row.template_list("CUSTOM_UL_items", "", scn, "custom", scn, "custom_index", rows=rows)
col = row.column(align=True)
col.operator("custom.list_action", icon='ZOOM_IN', text="").action = 'ADD'
row = layout.row()
col = row.column(align=True)
row = col.row(align=True)
row.operator("custom.clear_list", icon="X")
-------------------------------------------------------------------
Collection
-------------------------------------------------------------------
class CUSTOM_objectCollection(PropertyGroup):
coll_ptr: PointerProperty(
name="Collection",
type=bpy.types.Collection,
update=update_func
)
-------------------------------------------------------------------
Register & Unregister
-------------------------------------------------------------------
classes = (
CUSTOM_OT_actions,
CUSTOM_OT_clearList,
CUSTOM_UL_items,
CUSTOM_PT_objectList,
CUSTOM_objectCollection,
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
# Custom scene properties
bpy.types.Scene.custom = CollectionProperty(type=CUSTOM_objectCollection)
bpy.types.Scene.custom_index = IntProperty(update=update_func)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.custom
del bpy.types.Scene.custom_index
if name == "main":
register()