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?
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.)
Oh, thats a bummer 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?
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:
I asked on blender.stackexchange as well, and guess what, someone figured it out! Check out Crantisz very helpful answere here
I am so happy it works!
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])
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])