0
$\begingroup$

Is it possible to duplicate a collection from a Python script? When I do it manually, the Console shows bpy.ops.outliner.collection_duplicate(), however if I call it from script, there is a problem with context:

RuntimeError: Operator bpy.ops.outliner.collection_duplicate.poll() failed, context is incorrect

Does that mean that collection_duplicate() can be called only from the Outliner editor? If so, how to 'fake' it from a script?

$\endgroup$
2
$\begingroup$

This will duplicate the active collection in the outliner:

import bpy

for window in bpy.context.window_manager.windows:
    screen = window.screen

    for area in screen.areas:
        if area.type == 'OUTLINER':
            override = {'window': window, 'screen': screen, 'area': area}
            bpy.ops.outliner.collection_duplicate(override)
            break

From the documentation example here: Execution Context

$\endgroup$
  • $\begingroup$ Works great! Thank you! $\endgroup$ – user1566515 Nov 12 '19 at 13:37
3
$\begingroup$

API method

If you know the collection you wish to dupe, and the collection you wish to parent to, consider something like below. Because a collection can have many parents, I'm not totally sure without using the outliner how to determine which instance of the collection is being duped, and hence where in the hierarchy to paste.

As a test, I've copied the context collection to to the scene collection, with and without linked data. Using a method that recursively creates a new collection and populates it with object copies from the source.

import bpy
def copy_objects(from_col, to_col, linked):
    for o in from_col.objects:
        dupe = o.copy()
        if not linked and o.data:
            dupe.data = dupe.data.copy()
        to_col.objects.link(dupe)


def copy(parent, collection, linked=False):
    cc = bpy.data.collections.new(collection.name)
    copy_objects(collection, cc, linked)

    for c in collection.children:
        copy(cc, c, linked)

    parent.children.link(cc)

# test script
context = bpy.context
scene = context.scene
col = context.collection
print(col, scene.collection)
assert(col is not scene.collection)
parent_col = context.scene.collection

copy(scene.collection, col)
# and linked copy
copy(scene.collection, col, linked=True)

Note

For a totally linked copy, ie the objects and collections within are linked copies then

cc = collection.copy()

will do the trick.

Related

Change active collection

$\endgroup$
  • $\begingroup$ Thank you for a detailed answer. I tested it and it works as described. I marked another answer as correct because it appeared earlier and is a valid answer to this particular question. I also like your answer because it is a good working example of how to manipulate objects and collections. $\endgroup$ – user1566515 Nov 12 '19 at 13:36
  • 1
    $\begingroup$ Cheers and No probs. On a purely semantics lilt: Despite the big green tick, it's "accepted" (by you) rather than being "correct" which implies other answers are unaccepted rather than incorrect lol. If I had my way would change SE such that any or all answers could be accepted. and one is "favoured" To quote RickyBlender "Happy Blending" $\endgroup$ – batFINGER Nov 12 '19 at 14:03
  • $\begingroup$ Thank you for the explanation. I think "accepted" makes more sense :) $\endgroup$ – user1566515 Nov 12 '19 at 14:17

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.