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.
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.
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')
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.
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
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:
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)
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)
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.
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.