How can I get the currently selected objects in the outliner, if they are hidden?

Hi, I am making a script where I need to get the currently selcted objects in the outliner, this works fine in most cases with bpy.context.selected_objects.

The problem is that it does not work if the objects are hidden, I can get the active object with bpy.context.view_layer.objects.active, even if its hidden, but not all the other selected objects. bpy.context.view_layer.objects.selected does not seem to work either?

How can I get the selected objects?
image

I might be wrong, but I don’t think you can. I believe it’s the situation that Hidden doesn’t just mean from the view, but even from Blender selecting it via a script. It’s hidden, so blender can not see it and select it…

(Which is a crazy limitation. Please correct if I’m mistaken.)

2 Likes

Oh, thats a bummer :frowning: but some operations in Blender does work with hidden objects like Delete, Mark as Asset, Copy, Rename and Delete Hierarchy. So it must be possible right?
image

Some operations are possible when you do it directly over the data-block, as for example bpy.data.objects.remove(bpy.data.objects['Cube'])…
But not all operations work that way, specially operators with a specific poll function.

What you can do, it to set the object to visible, call the operator and then set them invisible again… most probably it all happens before a screen redraw (depends on the operation, of course).

I need to know the selected objects first, then it would work fine.

I tried this already, but it does not seem to work, even if its made visiable, if it was selected before being not hidden, its does not count as a “real selection” and does not get the orange outline:
image

I might misunderstand, do you have a example?

You can select them, and it’s very simple (bpy.data.objects['Cube'].select_set(True))…

But their selection won’t be part of the Context, and the poll functions from most operators will return false. :frowning:

1 Like

I asked on blender.stackexchange as well, and guess what, someone figured it out! :partying_face: Check out Crantisz very helpful answere here

I am so happy it works! :tada:

import bpy

class OUTLINER_OT_op(bpy.types.Operator):
    bl_idname = 'outliner.op'
    bl_label = 'Print selected items in Outliner'
 
    def execute(self, context):
        
        selection = context.selected_ids
        selected_collections = [sel for sel in selection if sel.rna_type.name != 'Collection']
        print(selected_collections)
        return {'FINISHED'}
 
bpy.utils.register_class(OUTLINER_OT_op)

override_context = bpy.context.copy()
area = [area for area in bpy.context.screen.areas if area.type == "OUTLINER"][0]
override_context['area'] = area
override_context['region'] = area.regions[-1]
 
with bpy.context.temp_override(**override_context):
    bpy.ops.outliner.op()
  • Edit, a even better way if you want to access to the selected objects in outliner without any bpy.context.temp_override, works even now if you do it in a UI re-draw.
for area in bpy.context.screen.areas:
    if 'OUTLINER' in area.type:
        for region in area.regions:
            if 'WINDOW' in region.type:
                override = {'area': area, 'region': region}
                break
        break
if override is not None:
    print([o for o in bpy.context.selected_ids if type(o) == bpy.types.Object])
3 Likes

Yah… that solves the problem with the Context!

1 Like

I don’t think you need the operator FWIW. It’s one of the benefits of the new syntax for context overrides, it isn’t strictly confined to operator overrides anymore.

import bpy

override_context = bpy.context.copy()
area = [area for area in bpy.context.screen.areas if area.type == "OUTLINER"][0]
override_context['area'] = area
override_context['region'] = area.regions[-1]
 
with bpy.context.temp_override(**override_context):
    print([o for o in bpy.context.selected_ids if type(o) == bpy.types.Object])
1 Like