6
$\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$
5
$\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.

| improve this answer | |
$\endgroup$
3
$\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?.

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.