How to get selection order?

Hi, I want to write an addon that operates on multiple selected objects. However, it is important for me to know the order in which the objects (more than 2) are selected. However

bpy.context.selected_objects

always returns the objects in the same order. Is there any way to get the order in which the objects have been selected?

Thank you,
Martin

Unfortunately, no.

1 Like

what? why is this not possible? can’t be that hard to implement!? unfortunately, I am not at all familiar with the blender source code :frowning:

just an idea here

is not there a way to use a panel with poll and a dynamic list for selected object
and the dynamic list would have the order of selected objects!

happy bl

@RickyBlender: sounds interesting. and how would you catch the event, when the user selects an object?

what? why is this not possible?

My thoughts exactly :wink:

I have been monitoring this thread quite closely since this seems to be a simple task but I could not find a simple solution by searching though the documentation. All I found was this ‘bug report’:

https://developer.blender.org/T21703

So, I started thinking about others ways to retrieve the selection order.

Here is the basic idea/theory:

  1. save the current active object
  2. go back in time by using undo()
  3. save the active object at that point in time
  4. go back in time by using undo() and save the active object… until there is only one selected object left
  5. use redo() several times in order to get back to the initial state

After that you will have saved a list of objects and their selection order.

I have put together a small proof of concept script. (I just started to check out python/blender scripting a few days ago - so don’t get confused if things look a little funny :wink:


import bpy  

class PrintSelectionOrder(bpy.types.Operator):  
    bl_idname = "object.print_selection_order"  
    bl_label = "Print Selection Order"
 
    def execute(self, context):  
        count = 0
        
        # start list with current active object:
        order = list([bpy.context.active_object.name])

        #keep going back in time (undo) as long as objects are selected
        while len(bpy.context.selected_objects) >= 2:
            bpy.ops.ed.undo()
            order.insert(0,bpy.context.active_object.name) # add previous active object
            count += 1

        self.report({'INFO'}, ', '.join(order)) # Output Order

        # Get back to the future (redo, redo, redo, ...)
        for x in range(0, count):
            bpy.ops.ed.redo()
        return {'FINISHED'}  
        
def register():  
    bpy.utils.register_class(PrintSelectionOrder)  
  
if __name__ == "__main__":  
    register()

Of course, there are still a lot of things which need to be considered in order to make this usable. (e.g. objects which get selected, de-selected and re-selected, damages which may be caused by using undo()/redo() this way, …) And it is quite clear that this will only work if the objects have been selected one after the other - things like rectangle select will most definitely break this. But maybe it is a start - and depending on the circumstances/intended use: probably better than nothing :slight_smile:

Good Night!

check the curve tools addon the author shares some code on this


seen same feature in oscurart tools I think also in animation nodes addon

thx RickyBlender and Helge. I will check both possibilities out. Helges idea is a great hack, very good idea. But of course, it would be better to retrieve this information in a more reliable way. But anyway, as a first implementation this would work already.

@liero: thx for the hint. I contacted already the developer of curve-tools

so I think there are some possibilities. Let’s hope that it will become soon a feature in one of the next releases of Blender. I put this already up on the developers mailing list

hey, thanks for the hint. that is really a hidden feature. after

import selection_utils

at the console it works and I can access the information with

selection_utils.selected

would be better to have the selection order by default available and not by switching to this special selection mode. But as a first start this helps already.

Thank you!

That’s great! There really had to be a solution. :slight_smile:

Anything changed since 2015? Is there an easy way to get selection order in 2.8?

I second that comment, I am actively looking for this especially in 2.80
There’s some workaround but that makes it less user friendly tough.
Also there’s bpy.ops.object.select_order() but not sure how to best use it. Might update my post if I find something :slight_smile:

Hi there,

I had this Problem too. Therefore I write a very easy shortcut, which when I press him, he write me the selected objects in a .txt file.
That means first select object, then press shortcut (k), then select next, press k and so on.

After that I call a little function which I called order_selection() and he write me the selection order in the python console



bl_info = {
    "name": "Work Macro",
    "category": "Object",
}

import bpy

class WorkMacro(bpy.types.Operator):
    """Work Macro"""
    bl_idname = "object.work_macro"
    bl_label = "Work Macro"
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):
        path="" 
        fileHandle = open (path + 'Order.txt', 'a' )
        fileHandle.write (bpy.context.scene.objects.active.name)
        fileHandle.write ('\n')
        fileHandle.close()

        return {'FINISHED'}


# store keymaps here to access after registration
addon_keymaps = []


def register():
    bpy.utils.register_class(WorkMacro)

    # handle the keymap
    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
    kmi = km.keymap_items.new(WorkMacro.bl_idname, 'K', 'PRESS', ctrl=False, shift=False)
    addon_keymaps.append(km)

def unregister():
    bpy.utils.unregister_class(WorkMacro)

    # handle the keymap
    wm = bpy.context.window_manager
    for km in addon_keymaps:
        wm.keyconfigs.addon.keymaps.remove(km)
    # clear the list
    del addon_keymaps[:]


if __name__ == "__main__":
    register()

import os

def order_selection():
    path="" 
    fileHandle = open(path + 'Order.txt')
    Aname=[]
    for line in fileHandle:
        Aname.append(line.rstrip())
    fileHandle.close()
    os.remove(path + 'Order.txt')
    A=[]
    for i in range(0, len(Aname)):
        A.append(bpy.data.objects[Aname[i]])
    print(A)

Continuing the discussion from How to get selection order?:

Hello
I’m not good at English

If there are two selection items, you can get the selection order.
in selected_objects of two, the active_object and the equal one is the second selected item.

selected = bpy.context.selected_objects
num_selected = len(selected)
if num_selected != 2:
	raise Error("need 2 item select")
if bpy.context.active_object == selected[1]:
	item_2nd = selected[1]
	item_1st = selected[0]
else:
	item_2nd = selected[0]
	item_1st = selected[1]

Hi. Sorry I am writing in this old thread. IS there any solution for blender 2.8?
I have to rename a lot of objects and I need to have the selection order. The trick with undos works, but is extremely slow. Is there any faster solutions?

The script in the first post need just an update for selecting the active object:

view_layer = bpy.context.view_layer        
active = view_layer.objects.active   
selected = bpy.context.selected_objects

num_selected = len(selected)
if num_selected != 2:
	raise Error("need 2 item select")
if active == selected[1]:
	item_2nd = selected[1]
	item_1st = selected[0]
else:
	item_2nd = selected[0]
	item_1st = selected[1]


The problem is that I have a lot more than 2 objects.

What function exactly you search for?

A shorthand here can be:

...
if num_selected == 2:
    item_1st, item_2nd = selected
    if item_1st == active: 
        item_1st, item_2nd = item_2nd, item_1st

:sweat_smile:

1 Like