Getting around invalidation of objects after user preforms an undo

Hey everyone,

I’m fairly new to the Blender API so forgive me if I am missing something obvious.
My problem is like so: I have multiple objects that I want to reference in code for later.

As an example, say I want to make a list that stores every selected object so I’ll create a list using
my list = bpy.context.selected_objects

This usually works fine up until Undo causes invalidation.

obj_invalid

The only solution I can think of right now is to find the objects again through their names using a for loop and bpy.data.objects.get("Suzanne") but this is less than ideal.
What if the user changes the name?
Sadly, this makes using this option unusable for the addon I want to create.

My question is: what is the best / most correct way of handling a situation like this?

Thanks!!

There’s not a whole lot you can do unfortunately. I wish there were more events we could subscribe to in python but there aren’t, and there is no event callback for undo/redo. The best you could do is subscribe to depsgraph update and manually check if something has changed.

1 Like

Oh I see. That’s really unfortunate! :frowning:

One way to track objects across undo is to tag them using their custom property (ID property).

This example sets a simple hash on the selected objects using set_hash() and a string as the hash key.
To retrieve the objects, you can use objs_from_hash() and supply the same key. It doesn’t have to be a hash, it could be any value as long as it’s unique enough to not collide, but also easily retrievable.

import bpy

def set_hash(prop, key):
    for o in bpy.context.selected_objects:
        o[prop] = float(hash(key))

def objs_from_hash(prop, key):
    objects = bpy.data.objects
    val = float(hash(key))
    return [o for o in objects if prop in o and o[prop] == val]


prop = "my_addon"
key = "some_seed"


if __name__ == "__main__":
# toggle between these

#    set_hash(prop, key)
    print(objs_from_hash(prop, key))
2 Likes

will this work in edit mode, in between bmesh changes? I’m assuming not since data.objects don’t get updated until you either leave edit mode or push the changes back to object mode manually.

ID properties exist on almost all bpy.types.ID subclass and were designed to be serialized along the with blend file. If a custom property is stored on the object data block, what happens to a mesh data block would not have any impact.

I wanted to post a quick update to this- at the time there were NOT any event callbacks for undo/redo, but in the last couple of weeks both have been added to bpy.app.handlers. happy day! https://docs.blender.org/api/blender2.8/bpy.app.handlers.html

Here’s hoping we get a much wider variety of events to subsribe to (fingers crossed for a bmesh update event)

2 Likes

Holy damn! Thank you so much for informing me about this, I can’t wait to check them out!!

I had a simiar issue and the workaroud I found was to add view layer update inside redo handler

    @persistent
    def redo_update_handler_post(scene):
        # fix crash in redo
        bpy.context.view_layer.update()

For UNDO crashing, adding an undo check point with

bpy.ops.ed.undo_push()

fixes the problem in some cases
More info here Addon Operators and Undo support

I really wish hacky stuff like this wasn’t necessary to track objects across undo/redo.

Me too :slight_smile:
Unfortunately, this is pretty common in blender. You have to do a lot of manual state / memory management in blender even when that should work out of the box. Besides that, undo/redo sits at the very core of blender and it is used even for things which are not suppose to be handled like that. For example: operators use undo/redo to create the illusion of interactivity when you scrub sliders in the UI. If you choose to run some backgroud scripting during this undo/redo process, blender will most likely crash without giving you a good reason to do so… :slight_smile: