3
$\begingroup$

I am trying to do the following using the Python API:

  • import an armature from an fbx file
  • add it to the asset browser
  • delete the armature from the scene, but not from the file, so that I can keep it in the Asset Browser

Fbx import is working nicely and so is adding it to the asset browser. However, when I try to delete the armature from the scene, I only found a way to delete it from the file entirely, which of course removes the armature from the Asset Browser.

However, I noticed that when I don't delete the armature via Python but via right-click on the object in the outliner and then select "delete hierarchy" this fulfills my requirements: The armature is gone from the scene, but it remains in the Asset Browser.

When I manually select Delete instead of Delete hierarchy, the armature is deleted completely from the file as it also happens in my Python approach.

What is different in Delete hierarchy compared to Delete and how can I reproduce this in Python?

Here is my Python approach for Delete hierarchy, which actually deletes "too much":

bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[fbxArmatureName].select_set(True)
for child in bpy.data.objects[fbxArmatureName].children:
    child.select_set(True)
bpy.ops.object.delete()

Can anyone help me out?

$\endgroup$
6
  • $\begingroup$ i didn't check and i am not sure, but did you check "use_fake_user"? maybe this makes the difference? $\endgroup$
    – Chris
    Jan 20 at 6:41
  • $\begingroup$ maybe it is is a bug that it is left in the asset browser when doing delete hierarchy. it makes no sense that it would remain. $\endgroup$ Jan 20 at 7:58
  • $\begingroup$ @Chris Yes, I tried to set a fake user, but that didn't make a difference. $\endgroup$
    – StS
    Jan 20 at 10:39
  • $\begingroup$ @HarryMcKenzie It might also be a bug, but there should be a way to remove an object from a scene without deleting it completely, I would have thought. $\endgroup$
    – StS
    Jan 20 at 10:39
  • 1
    $\begingroup$ @HarryMcKenzie Agreed, but at least it does the work for now. Maybe I will find a nicer solution at some point. $\endgroup$
    – StS
    Jan 20 at 11:30

1 Answer 1

5
$\begingroup$

You have to call bpy.ops.outliner.delete(hierarchy=True) using the with keyword with context inside the Outliner. Make sure the Outliner is open in one of the areas in your window.

import bpy

bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects["Armature"].select_set(True)
for child in bpy.data.objects["Armature"].children:
    child.select_set(True)

area_type = 'OUTLINER'
areas  = [area for area in bpy.context.window.screen.areas if area.type == area_type]

if len(areas) <= 0:
    raise Exception(f"Make sure an Area of type {area_type} is open or visible in your screen!")

with bpy.context.temp_override(area=areas[0]):
    bpy.ops.outliner.delete(hierarchy=True)

If you don't have any Outliner open you can switch to Outliner before bpy.ops.outliner.delete(hierarchy=True) is called so you are in the Outliner context.

import bpy

bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects["Armature"].select_set(True)
for child in bpy.data.objects["Armature"].children:
    child.select_set(True)

area_type = 'OUTLINER'
bpy.context.area.ui_type = area_type

with bpy.context.temp_override(area=bpy.context.area):
    bpy.ops.outliner.delete(hierarchy=True)

EDIT UPDATE:

The bpy.ops.outliner.delete function does not function as expected. As mentioned in this thread, 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.

So you need to use a timer as workaround:

import bpy

armature = bpy.data.objects["Armature"]

def select_objects():
    bpy.ops.object.select_all(action='DESELECT')
    armature.select_set(True)
    for child in armature.children:
        child.select_set(True)
    bpy.context.view_layer.objects.active = armature

def delete_objects():
    area_type = 'OUTLINER'
    areas  = [area for area in bpy.context.window.screen.areas if area.type == area_type]

    with bpy.context.temp_override(area=areas[0]):
        bpy.ops.outliner.delete(hierarchy=True)

select_objects()
bpy.app.timers.register(delete_objects, first_interval=0.01)
$\endgroup$
4
  • $\begingroup$ Thanks! This is almost working. The only problem is that the selection does not seem to work. In fact, the script deletes the previously selected object from the scene instead of the one specified in the script. When I print bpy.context.selected_objects right before bpy.ops.outliner.delete(hierarchy=True) it prints the correctly selected object, but the deletion affects another one. Do I need to select the object to be deleted in a different way when I use bpy.ops.outliner.delete(hierarchy=True)? I also tried moving the selection block inside thewith statement, but to no avail. $\endgroup$
    – StS
    Jan 20 at 10:31
  • $\begingroup$ yeah i noticed that when i select the object, it will immediately work. but if i did not select anything, then the first run would just select it, and then on the second run would delete. if you figure it out, feel free to update the answer. $\endgroup$ Jan 20 at 10:49
  • $\begingroup$ you need to use a workaround timer because bpy.ops.outliner.delete(hierarchy=True) doesn't work as expected. please see the new update explanation $\endgroup$ Jan 20 at 11:25
  • 1
    $\begingroup$ I was literally just editing the answer with the timer workaround, when a notice popped up, that you edited the answer yourself. So, yeah, the timer solves the issue. Thanks a lot! $\endgroup$
    – StS
    Jan 20 at 11:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .