7
$\begingroup$

The Blender 2.8 Python API allows me to retrieve all child collections of a collection using its children property. Given the following collection hierarchy

+------------------+
| Scene Collection |
+------------------+
  |
  |  +---+
  +--| a |
     +---+
       |
       |  +---+
       +--| b |
       |  +---+
       |    |
       |    |
       |    |  +---+
       |    +--| c |
       |    |  +---+
       |    |
       |    |  +---+
       |    +--| d |
       |       +---+
       |
       |  +---+
       +--| e |
          +---+

I can access the children of collection b (i.e. collections c and d) using the following code:

collection_b = bpy.data.collections['b']
children = collection_b.children

That way I can navigate down the collection tree. My question is how I can navigate upwards, i.e. how can I get the parent collection of say collection b?

I didn't find a property allowing me to retrieve the parent collection(s).

Essentially I'm interested in recursively making my way up all to the root (Scene Collection), e.g

d -> b -> a -> Scene Collection
$\endgroup$

4 Answers 4

7
$\begingroup$

The best I could come up with so far is a recursion that searches for the parent collection. Note that this is not a particularly efficient solution but at least it solves my problem. The code looks as follows:

import bpy


def get_parent_collection_names(collection, parent_names):
  for parent_collection in bpy.data.collections:
    if collection.name in parent_collection.children.keys():
      parent_names.append(parent_collection.name)
      get_parent_collection_names(parent_collection, parent_names)
      return


def turn_collection_hierarchy_into_path(obj):
  parent_collection = obj.users_collection[0]
  parent_names      = []
  parent_names.append(parent_collection.name)
  get_parent_collection_names(parent_collection, parent_names)
  parent_names.reverse()
  return '\\'.join(parent_names)


obj = bpy.context.view_layer.objects.active
print(turn_collection_hierarchy_into_path(obj))

This code will print out the collection hierarchy of the selected object as a Windows style path (backslash as separator).

Note that objects can be part of multiple collections which means you can actually create a graph, not just a tree. The above code will simply ignore that fact and just pick the first collection that the object is part of.

$\endgroup$
2
  • $\begingroup$ fyi, if you're starting with an object, you don't need to recursively iterate to get its direct parent. This will get the parent: bpy.data.objects['Cylinder'].users_collection Source: docs.blender.org/api/current/… $\endgroup$ Mar 12, 2022 at 14:37
  • $\begingroup$ although it's important to note the documentation says "Takes O(len(bpy.data.collections) + len(bpy.data.scenes)) time." for users_collection to get the parent collection, so it's likely doing this iteration logic for you. $\endgroup$ Mar 12, 2022 at 14:39
5
$\begingroup$

As long as there is no parent property, you can iterate through the collections of the scene Scene.collection.children and then cache the relationship into a lookup using a dictionary:

import bpy

def traverse_tree(t):
    yield t
    for child in t.children:
        yield from traverse_tree(child)

def parent_lookup(coll):
    parent_lookup = {}
    for coll in traverse_tree(coll):
        for c in coll.children.keys():
            parent_lookup.setdefault(c, coll.name)
    return parent_lookup


C = bpy.context

# Get all collections of the scene and their parents in a dict
coll_scene = C.scene.collection
coll_parents = parent_lookup(coll_scene)

# Get active collection reference
active_coll = C.view_layer.active_layer_collection.collection
#sub_coll = coll_scene.children.get("Collection")

# Print parent of active collection
print ("Parent of {} is {}".format(
        active_coll.name, 
        coll_parents.get(active_coll.name))
    )

traverse_tree(t) function from How to get all collections of the current scene?


Although pretty complex, you can also implement a custom parent property for the Collection type by using a PointerProperty to get some kind of native API feel like Collection.parent as pointed out by @batFINGER. Further reading: Create a tree of properties?.

$\endgroup$
1
4
$\begingroup$

Users of ID

Can get all collections used in scene collection hierarchy via

>>> collections_in_scene = [
        c for c in D.collections 
        if C.scene.user_of_id(c)
    ]
>>> collections_in_scene
[bpy.data.collections['Collection 1'], bpy.data.collections['Collection 1 1'], bpy.data.collections['Collection 1 1 1']]

Add the root collection

>>> collections_in_scene.append(C.scene.collection)

Then for any collection (assuming only those in scene are of interest) can get its parent(s)

enter image description here

>>> coll = C.collection
>>> coll
bpy.data.collections['Collection 1 1 1']

>>> parents = [c for c in collections_in_scene
...            if c.user_of_id(coll)
...            ]
>>> parents
[bpy.data.collections['Collection 1 1']]



>>> coll = D.collections['Collection 1']
>>> parents = [c for c in collections_in_scene
...            if c.user_of_id(coll)
...            ]
>>> parents
[bpy.data.scenes['Scene'].collection]
$\endgroup$
0
$\begingroup$

A quick simple way to create the parents (in a seperate dictionary), with the top level collections parents be None:

# create parent relations
parent = dict()
for c in bpy.data.collections:
    parent[c] = None
for c in bpy.data.collections:
    for ch in c.children:
        parent[ch] = c

With that, a parent can be obtained by:

parentCollection = parent[collection]
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .