1
$\begingroup$

I'd like to improve my duplicator script. bpy.ops.object.duplicate() seems to require me to select the object first. I'd like to just feed the object straight to it instead.

Is there a smarter way to handle it than I did so far?

def duplicator(name):
    # select the object
    obj = bpy.data.objects.get(name)
    obj.select_set(True)

    # Loop 3 times, duplicate it, name it
    for i in range(1,4):
        # duplicate selected object
        bpy.ops.object.duplicate()

        # name new object with a string and number
        newObj = bpy.context.selected_objects[0]
        newName = 'Plane_' + str ( i )
        newObj.name = newName


duplicator('Plane_0')
$\endgroup$
1
  • $\begingroup$ new_obj = obj.copy() $\endgroup$
    – batFINGER
    Mar 18, 2020 at 10:24

2 Answers 2

4
$\begingroup$

Another take

Before closing as dupe, thought I would put together another take on this for example sake.

Added a method that simply yields n copies of the given object with a new name and suffix. If linked is False the object copies all share the same mesh.

import bpy
from bpy import context 

def object_duper(ob, name, n=1, linked=False):
    assert(ob)
    for i in range(n):
        copy = ob.copy()
        if ob.data and linked:
            copy.data = copy.data.copy()
        copy.name = f"{name}_{i + 1}"
        yield copy

# testing

ob = context.object

for o in object_duper(ob, ob.name, 5):
    context.scene.collection.objects.link(o)

This method simply dupes the objects which will naturally be added to bpy.data.objects collection. I have not put in the link to collection. Could emulate your answer method with, and pass the collection

def dupe_n_link(collection, ob, name, n=1, linked=False):
    for o in object_duper(ob, name, n=n, linked=linked):
        collection.objects.link(o)

Can think of many reasons to create multiple duplicates, may not always wish to add all dupes to same collection, may wish to add one to each collection

ob = context.object

colls = bpy.data.collections

for c, o in zip(colls, object_duper(ob, "Basis", n=len(colls))):
    c.objects.link(o)
$\endgroup$
2
  • $\begingroup$ is it possible to unlink the material as well as you do with the mesh in the duplicate object by using the linked=True flag. I want my material in the duplicate object to have the same properties as the material in the original object but at the same time once the duplicate object is created any changes in one of the two materials later on not to affect the other one. $\endgroup$
    – ttsesm
    May 6, 2021 at 14:15
  • 1
    $\begingroup$ Ok I think I found the solution copy.active_material = copy.active_material.copy() seems to do the trick. $\endgroup$
    – ttsesm
    May 6, 2021 at 15:10
3
$\begingroup$

Thanks @batFINGER and brockmann.

To duplicate an object and add it to a collection, you could use the following code

import bpy

def duplicator(
        ob_name, new_ob_name, 
        col_name, amount):

    new_obs = []

    for i in range(1, amount):

        # get the object
        ob = bpy.context.scene.objects.get(ob_name)

        # copy, name and store new_name
        new_ob = ob.copy()
        new_name = new_ob_name + str ( i )
        new_ob.name = new_name
        new_obs.append(new_name)

        # place it in the collection
        col = bpy.context.scene.collection.children.get(col_name)
        #bpy.context.scene.collection.children.link(newCol) <-- in case you need to add the collection to the scene
        col.objects.link(new_ob)
        print ( col.name ) 

    return new_obs

duplicator('Cube', 'Cube_', 'coverage', 5)

```
$\endgroup$
5
  • $\begingroup$ Depending on what you do, there is still a lot which can be optimized like getting the object as well as the collection per scene C.scene.objects.get(ob_name) instead of the data block, python comes with enumerate() and I would not recommend to hard code the collection name: blender.stackexchange.com/a/167440/31447 $\endgroup$
    – brockmann
    Mar 18, 2020 at 12:22
  • $\begingroup$ Hi brockmann, wow, thank you, this is nice. I've updated the script a bit to reflect the changes. Any reason why you prefer using C.scene.objects instead of bpy.data.objects ? $\endgroup$
    – Henk Kok
    Mar 18, 2020 at 13:17
  • $\begingroup$ That's close but won't work unfortunately. C is a convenience variable in the console to get the context so either create the variable before C = bpy.context or use bpy.context.scene.objects.get(ob_name). Why? It's safer, you can limit the scope to the scene in context rather than iterating through all objects in the entire blend file. Also consider renaming your arguments to something like ob_name, new_ob_name, coll_name and amount -> according to pep: python.org/dev/peps/pep-0008 $\endgroup$
    – brockmann
    Mar 18, 2020 at 13:38
  • $\begingroup$ I was struggling a bit with the names, but your suggestion & pep-0008 are quite helpful. Updated code once again. Cheers dude! $\endgroup$
    – Henk Kok
    Mar 18, 2020 at 13:50
  • $\begingroup$ ... and the collection using get() as well like: col = C.scene.collection.children.get("Collection"). Once you have it, use it to link the objects col.objects.link(new_obj)... $\endgroup$
    – brockmann
    Mar 18, 2020 at 13:55

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