What is the last object created?

I asked this on stack exchange, but the “exchange” there makes me think that it’s probably more appropriate for a discussion here. If it’s not, please direct me to where it may be–as I am getting a lot of resistance to discussing it on stack exchange.

When building tools and processes, it’s almost always crucial to know what the last object created is. Many functions which create objects of various types in Blender don’t return those objects, nor is there an ordered list of any type accessible which can be used to discover what may have been created recently.

It was suggested that C.object is a good way to find this, but there are ways to create objects which do not place them in the current context, for one, and for two, it’s possible to change context without creating anything, which means that the context will not report the last created object.

The only way I can think of to reliably learn what object(s) a function may have created is to compare lists of all scene objects before and after running the function. This is not efficient, and seems like a very nasty and cumbersome workaround. And of course sorting through the difference might be fraught, if the function created more than one object which will not be reported in any order. This would imply additional detective work in some instances, so there still wouldn’t be a universal way to learn, in order, what objects were created recently.

It’d be great to know if there were a simpler, more efficient, more reliable way to discover what’s been created in the scene, in order of creation.

1 Like

Hi CMK_blender,

In the info editor you can read what object was created last. Maybe the info operators leads you to your goal here by reading in reverse order, and then stop at the first valid catch.

Another approach would be to catch every created object in the scene in a string. That way the last created object is stored in the string.

https://docs.blender.org/api/current/bpy.ops.info.html?highlight=info#module-bpy.ops.info

As told, just a few first rough ideas, untested. Next step is to check if this leads to somewhere …

Kind regards
Arunderan

Please be patient if I don’t understand what you’re saying. However, I don’t think the info editor reliably reports everything created, and it doesn’t do it in a way which would be programmatically useful. For example, if you duplicate something, it echos the command used to duplicate the object, but doesn’t deliver any information about what was generated.

I’m no expert on the bpy API, but I don’t think there is such a singular method. Usually if there is no way in the regular UI to accomplish the same thing then the Python API won’t help you (as it is a fairly thin wrapper). Or the result of some operation, i.e. adding an object using a bpy operator, updates the selection as a side-effect, implicitly encoding what got added to the scene.

Such a function could be an interesting addition to the Python API. I.e. a call to take a snapshot of the current scene at some point, do an operation, then take another snapshot and compare. But the addition order might not be that easy to retrieve, as that would mean keeping fine-grained track of the changes. The existing undo system and its history might have some of this info, but that might not be available through the API currently.

Here’s an example of the context not reflecting what was just created. This is meant to run in a fresh scene:

    import bpy
    o = bpy.data.objects['Cube']
    # clear the selection
    bpy.ops.object.select_all(action='DESELECT')
    for i in [0,1]:
        #select the Cube
        o.select_set(True)    
        # duplicate it
        bpy.ops.object.duplicate()
        # this should be the new object (but it isn't)
        temp = bpy.context.object            
        # register what we've got
        print('temp:', temp)   # fails on second loop  
        # delete the new object            
        bpy.data.objects.remove(temp, do_unlink=True)    
        # clear the selection
        bpy.ops.object.select_all(action='DESELECT')
temp: <bpy_struct, Object("Cube.001")>
temp: None

This will throw an error the second time through the loop, because C.object is None.

However if you laboriously compare lists of objects in the scene before and after running the duplication, it works as expected:

import bpy
o = bpy.data.objects['Cube']
# clear the selection
bpy.ops.object.select_all(action='DESELECT')
for i in [0,1]:
    #select the Cube
    o.select_set(True)   
    # list of all objects    
    objects = bpy.data.objects.values()
    # duplicate it 
    bpy.ops.object.duplicate()   
    #new list of all objects, and compare
    new_objects = bpy.data.objects.values()      
    for object in new_objects:
        if object not in objects:
            temp = object        
    # register what we've got
    print('temp:', temp)     
    # delete the new object            
    bpy.data.objects.remove(temp, do_unlink=True)    
    # clear the selection
    bpy.ops.object.select_all(action='DESELECT')
temp: <bpy_struct, Object("Cube.001")>
temp: <bpy_struct, Object("Cube.001")>

The (little c) context I came across this was in duplication, as above. However nothing I’ve come across so far which creates objects returns them. That’s probably more of a fundamental problem than lack of a list of objects in the scene ordered by creation time, but it’s also a much bigger one to repair, because a lot of different processes will create objects.

You need to set the active object as well as the selection, posted an answer here.

Why is what’s necessary in order to recover an object from its creation more complex than what’s necessary in order to create it?

Anyway, this misses the forest for the trees. This is just a single example, and I am not bothered about making C.object function or not in this case. The real problem is methods creating objects and not returning them. Thread isn’t about the example, really, but about this lack. Again

1 Like

In this case you’re better off avoiding operators, where possible.

For most common operations you can add the object, link it to a collection and manipulate it without running operators.

What’s a good way to duplicate an object without using operators?

Nearly all data-blocks have a copy method, objects included.

Thanks, that’s a start!

there are a few things to remember with creating any type of object- creating the object, the data and then linking it to something in the scene so you can actually see it.

duplicating an object with linked data and adding it to the scene collection:

new_obj = bpy.context.active_object.copy()
bpy.context.collection.objects.link(new_obj)

If you need it to have unique data:

new_obj = bpy.context.active_object.copy()
new_data = bpy.context.active_object.data.copy()
new_obj.data = new_data
bpy.context.collection.objects.link(new_obj)

There’s more than one way to do all of this, these two examples are probably the most straightforward- but you could also create the object and data manually using bpy.data directly. As you can see from the example though- doing it this way allows you to have a reference to the newly created object to then do additional work on (just be careful not to cache it off into some global scope, blender’s access violation crashes can be nasty if you don’t know where the landmines are)

2 Likes

Just for recommendation, you can use a Builder pattern to create the objects for you behind the scenes while you can use a user friendly API. Also do logging and state management additionally is a good idea.

one more trick, while we’re sharing- I am not above abusing set comparison to get the information I need if I’m in a pinch. For example, there’s no easy way to convert instanced collections into real objects, it’s far simpler to just run the built-in operator, but you’ll have some random number of objects that were created. In that situation I create a before and after set, then return the difference:

objects_before = {o for o in bpy.data.objects}
bpy.ops.object.duplicates_make_real()
created_objects = {o for o in bpy.data.objects}.difference(objects_before)
1 Like

I understand that we’re probably stuck with an API that does not return the object just created, simply because of backward compatibility. A bad design decision from long ago haunts us now.

A way to give us a better way to look up our objects without changing the return type would be to allow us to pass in the naming parameter when the object is created as a new kwarg. Note: This argument does not exist, but I’m proposing it as a way to extend the API in a future iteration.

bpy.ops.mesh.primitive_uv_sphere_add(segments=32 , ring_count=16 , radius=1.0 , calc_uvs=True , enter_editmode=False , align=‘WORLD’ , location=(0.0, 0.0, 0.0) , rotation=(0.0, 0.0, 0.0) , scale=(0.0, 0.0, 0.0), name=“My Unique Name” )

I haven’t looked at the implementation details, as I’m not a Blender developer (yet), but perhaps this wouldn’t be possible. Alternatively, instead of name, it could be an ID or a UUID.