Unique identifier for layer collections?

I’ve hit a roadblock in one of my projects, and I think it’s due to a design limitation. It’s not a bug per se because everything’s working as designed but it’s making the python API behave strangely around layer collections.

I’m making use of the collection linking in my scene. It’s the feature where, in the outliner, you drag a collection into another collection while holding CTRL, creating a 1-1 direct reference between the two child collections. It’s an awesome feature that I feel like is really underused in all the projects I’ve seen.
capture

The only way I know to select a Layer Collection using the API is by name, using the underlying collection name. It’s all good and well, since collections are bound to have a unique name by design.

However layer collections don’t. A view layer can have any number of layer collections that link to the same underlying bpy.types.Collection.

So in the case where every collection is mapped to a single layer collection, this code can be used :

import bpy

collection = bpy.data.collections["Cube"]
def find_collection(layer_collection):
    if layer_collection.collection == collection:
        return layer_collection
    for child_collection in layer_collection.children:
        return find_collection(child_collection)

print(find_collection(bpy.context.view_layer.layer_collection))

>> <bpy_struct, LayerCollection("Cube") at 0x0000022909E377E8>

And, it can be adapted to return a list of layer collections instead :

import bpy

collection = bpy.data.collections["Cube"]
def find_collection(layer_collection):
    if layer_collection.collection == collection:
        yield layer_collection
    for child_collection in layer_collection.children:
        yield from find_collection(child_collection)

print(list(find_collection(bpy.context.view_layer.layer_collection)))

[bpy.data.scenes['Scene']...LayerCollection, bpy.data.scenes['Scene']...LayerCollection, bpy.data.scenes['Scene']...LayerCollection]

But there is no actual way to diferrentiate between the 3 collections. context.selected_ids returns the same pointer when two linked collections are selected in the outliner.

So one can’t selectively and accurately include or exclude a particular child linked collection from a view layer using the API.

The problem shows when using the Collection Manager addon that’s shipped with Blender. It’s breaking apart when linked collections are used. It doesn’t take into account that a single collection can be used in many layer collections in a single view layer, and from my understanding it’s not possible using the API.

capture

So my question is : Is there a design limitation in the way layer collections are handled by the API and is there a preferred way to circumvent it ? Obviously the C/C++ codebase knows about which exact layer collection is acted upon when the user clicks on the exclusion checkbox so it may just be a matter of exposing it to the API ? Maybe I am missing something ?

Cheers

Edit: Apparently, when copy and pasting the code from the forum into Blender the quote marks around the strings are changed into a different kind of quote mark breaking the script.

I am not 100% sure if this is what you are looking for but the following code excludes one of your cube layers without affecting the other ones:

import bpy

collections = bpy.context.view_layer.layer_collection.children

collections[1].children[0].exclude = True

It works with names as well:

import bpy

collections = bpy.context.view_layer.layer_collection.children

collections[‘Collection 2’].children[‘Cube’].exclude = True

1 Like

Hey thanks for your input ! This is indeed one way to do it, but it requires you to know in advance either the name of the parent layer collection, or have a direct reference to it. And even if you have its name, if it is linked in more than one collection, you have the same problem that it doesn’t ensure that you’ll get the right one if you traverse the tree. If you’re using the API and user input in the outliner, you can’t access this information (AFAIK).

Since the method that seems to be widely accepted to get a collection layer from a collection, is to traverse the entire tree of layer collections in search of the collection that corresponds to the name you’re looking for. But in this case this method fails since it’s not a guaranteed 1-1 relationship (One collection can be referenced in many layer collections)

I thought about it a bit more and I think the most elegant solution is that when a collection is selected in the outliner, and an operator is executed, context.selected_ids should not return the bpy.types.Collection, but rather the bpy.types.LayerCollection.
Ideally it would return a construct with the selected bpy.types.ViewLayer since the outliner can display several at a time, and the relevant selected ids.

It makes sense to me because in reality what’s selected is the layer collection, not the underlying collection. In the context of the outliner, when we are in the View Layer mode, we’re actually dealing with layer collections. And the underlying bpy.types.Collection can easily be inferred from the layer collection with bpy.types.LayerCollection.collection.

I would be very surprised if I was the first person ever to have this problem. I’d like to get some feedback on whether I’m misguided about the whole view layer / layer collection system or if I’m genuinely missing a feature.

Cheers