Outliner Duplicate Hierarchy objects (not Collection)

Hello,
I’m trying to duplicate selected object with it hierarchy without pre selecting objects inside.
What I have:

import bpy

# Set the area to the outliner
area = bpy.context.area
old_type = area.type 
area.type = 'OUTLINER'

# Selecting Children and Parent objects
myObj = bpy.context.active_object
bpy.ops.object.select_grouped(type='CHILDREN_RECURSIVE')
myObj.select_set(True)

# Outliner Copy/Paste 
bpy.ops.outliner.id_copy()
bpy.ops.outliner.id_paste()

# Selecting Pasted Objects (Make them active)
selection_names = [obj.name for obj in bpy.context.selected_objects]
for o in selection_names:
   obj = bpy.context.scene.objects.get(o)
   bpy.context.view_layer.objects.active = obj
   obj.select_set(True)

# Reset the area 
area.type = old_type

The problem is that I cant get id_copy/paste work with hierarchy, it only duplicate selected object.
Sorry if it obvious, I have very low skill in python.

The outliner selection is not exposed to the python API. You can access collections and iterate over the objects in a collection and copy those objects, rather than using the id_copy() and id_paste() operators.

import bpy

collection_src = "Collection"
collection_dst = "Collection"

collection = bpy.data.collections[collection_src]

# If source and destination are the same iterating needs a copy
# of the input list using list() to prevent an infinite loop
for ob in list(collection.objects):
    copy = ob.copy()
    # Also copy mesh data (remove for linked mesh data)
    copy.data = ob.data.copy()
    
    # Link to destination collection
    bpy.data.collections[collection_dst].objects.link(copy)

You can get the active collection with bpy.context.collection

Doing this recursively can be more complicated depending on how you want to handle collections and objects that are linked in the subtree multiple times. Here is a simple example that doesn’t prevent duplicating an object more than once. It will copy each object in the active collection recursively.

import bpy

def duplicate_children(collection, dst=None):
    collection_dst = dst
    if not collection_dst:
        collection_dst = collection

    for ob in list(collection.objects):
        copy = ob.copy()
        copy.data = ob.data.copy()
        collection_dst.objects.link(copy)
    
    for child in collection.children:
        duplicate_children(child, dst)

collection = bpy.context.collection
duplicate_children(collection)

# Also can specify destination collection
duplicate_children(collection, bpy.data.collections['copies'])
1 Like

Found an easy way, no need id_copy/paste at all
just

bpy.ops.object.duplicate()
dupli_obj = bpy.context.object

And with Collection checking I’ll be sure that it will copy in proper collection.
Thanks!

The bpy.ops.object.duplicate() operator will work, but note that operators have more overhead so that will take more time to execute for large numbers of objects.

1 Like