How to get selection order?

Does your system gracefully handle Undo/Redo actions? I tried writing my own selection order tracker a few months back but Blender’s kludgy Undo system invalidates references when it runs which would destroy the list.

1 Like

Yes, can handle undo/redo.

I tested the latest available from here: https://github.com/oscurart/Blender-28-Addons and it works perfectly well to add things to the list order, or subtract them from the list order when working in a forward direction, but frequently did not handle Undo/Redo well. After an Undo the selection order sometimes changed:

  1. Individually Shift+add objects in an order other than the creation order

List order before undo:
['Torus', 'Cube', 'Cone', 'Camera']

  1. Click empty space to deselect everything.
  2. Undo.

List order after undo:
['Cube', 'Camera', 'Cone', 'Torus']

Or other times the menu in your screenshot disappears because the reference in memory was invalidated by the Undo, such as after undoing a Rename or just several Undos in a row after deselecting objects (restoring selection with Undo):

Traceback (most recent call last):
  File "C:\Portable\blender-2.90.0-windows64\2.90\scripts\addons\oscurart_tools\object\selection.py", line 60, in draw
    select_osc()
  File "C:\Portable\blender-2.90.0-windows64\2.90\scripts\addons\oscurart_tools\object\selection.py", line 49, in select_osc
    print([ob.name for ob in bpy.selection_osc])
  File "C:\Portable\blender-2.90.0-windows64\2.90\scripts\addons\oscurart_tools\object\selection.py", line 49, in <listcomp>
    print([ob.name for ob in bpy.selection_osc])
ReferenceError: StructRNA of type Object has been removed

location: <unknown location>:-1

location: <unknown location>:-1

Tested Blender 2.83 and 2.90, windows64

As I mentioned, I tried writing my own selection order tracker a few months ago and I experienced the same result; I was able to make an add-on that very nicely added and subtracted objects from the list when working without Undo, but Blender’s Undo/Redo system broke the object references which requires storing and rebuilding the list in a complicated way with hacky workarounds (I gave up instead of trying to hack around the Undo system. I might come back to it at a later date.).

This wouldn’t be a problem if objects in Blender had a unique ID that could be referenced but for some unfathomable reason they don’t.

1 Like

Mmmmm… I dont know.
Its a walkaround… Blender need this tool in core.

1 Like

getting the selection order of objects reliably, and without exposing yourself to any potential access violations is pretty straightforward if you don’t mind abusing the fact that every object in bpy is basically a dictionary. in the past, when I’ve needed to do that I just write and re-index a value on the objects themselves during a depsgraph update. if you do it this way you avoid potential crashes due to caching objects, disappearing references due to users renaming objects, undo/redo is handled automatically because the data is stored on the object, same with delete, copy, paste, duplicate, etc.

since I can’t share the addons where I’ve used this technique in the past I put together a handy .blend that demonstrates how to use it.

object_selection_order.blend (946.4 KB)
(edit: updated script for added clarity)

from my test:

(Selecting)
selection_order: [bpy.data.objects['first']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third'], bpy.data.objects['Suzanne']]

(Deselecting)
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second']]
selection_order: [bpy.data.objects['first']]

(Undo Deselecting)
selection_order: [bpy.data.objects['first'], bpy.data.objects['second']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third'], bpy.data.objects['Suzanne']]

(Redo Deselecting)
selection_order: [bpy.data.objects['first'], bpy.data.objects['second'], bpy.data.objects['third']]
selection_order: [bpy.data.objects['first'], bpy.data.objects['second']]
selection_order: [bpy.data.objects['first']]
2 Likes

Ah that’s a lovely piece of work.

I did have something like that in mind, although the hacky way I was going to try would be adding a (permanent) unique hash to each object and then store ordered hashes in a list. Your method is simpler and more direct/elegant, and simple is good in this case (KISS principle) which is better than my convoluted idea. :smiley:

I did notice that sometimes an object can get ‘stuck’ in the list. Maybe something to do with the depsgraph selection_change_handler?

That is the exact issue he commented on in the script on line 27 as a TODO.

TODO: this doesn’t handle re-selecting an already selected object, but the logic for something like that is pretty straight-forward. I’ve omitted it for clarity.

Its interesting code although I’m a bit wary of hooking depsgraph.updates. Seems to me that’s pretty much every change possible (even stuff the user is not directly aware of) which is filtered afterwards on wether its a selection update. That feels a bit backwards to me where I have a hunch its easy to not be aware of a condition where it still triggers but is not a selection update.

Then again thats pretty much what testure says so himself. If there is no direct way to hook into selection events we are kinda stuffed. Heck, if that was more exposed we probably wouldn’t have to jump through these hoops in the first place. :sweat_smile:

Thanks for sharing testure! :+1: