I am trying to use an outliner operation after selecting an object via bpy. For this I used a context override.
Why does this script not delete the Cube the first time it is run? It only works when the cube is selected before the script is run.
Does the outliner need to be refreshed after selecting the cube?
I need to use a bpy.ops.outliner operation. The delete op is just a placeholder for testing purposes. My specific usecase is adding a library override via bpy.ops.outliner.id_operation. The alternative bpy.ops.object.make_override_library() seems to be buggy in 2.9
import bpy
bpy.data.objects["Cube"].select_set(True)
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'OUTLINER':
override = bpy.context.copy()
override["area"] = area
bpy.ops.outliner.delete(override)
tl;dr The outliner can’t see the new selection until after the script has run.
Outliner operators work on proxy data called tree elements, which is separate from bmain objects. Tree synchronization happens only before the outliner is getting ready to redraw, which will only be at the end of the script. In other words, when you select an object and call bpy.ops.outliner.delete(), the outliner doesn’t know the new selection yet.
One workaround is to use a timer to call the delete operator outside of the main script execution. The timer fires instantly, but still after the script has been executed and the internal outliner tree has been synchronized. The downside is the outliner selection may blip for a split second.
import bpy
ob = bpy.data.objects["Cube"]
ob.select_set(True)
def outliner_delete_delayed(override):
if ob in set(bpy.data.objects):
bpy.ops.outliner.delete(override, 'INVOKE_DEFAULT')
return 0.0
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'OUTLINER':
override = bpy.context.copy()
override["area"] = area
bpy.app.timers.register(lambda: outliner_delete_delayed(override))
break