58
$\begingroup$

How can I completely remove an object (for example a camera) using python scripting?

Using something like:

scene.objects.unlink(camera)

Does not work, since it is still available in bpy.data.objects. However, using:

bpy.data.objects.remove(camera)

still doesn't remove the camera, since it continues to exists in bpy.data.cameras.

Is there a way to remove an object from all of these places at the same time?

$\endgroup$
1
  • 5
    $\begingroup$ camera is actually an object datablock, not a camera. You need to unlink the object from the scene, delete the camera (ob.data, which would be camera.data in your example) and finally delete the object. If not possible to remove datablocks if they have users however, and clearing the user count may lead to crashes later on - thus it's advisable to use the delete operator instead. $\endgroup$
    – CodeManX
    Commented Mar 17, 2015 at 13:21

5 Answers 5

74
$\begingroup$

You can use bpy.ops.object.delete() operator to remove selected objects:

import bpy

# Deselect all
bpy.ops.object.select_all(action='DESELECT')

# Select the object
bpy.data.objects['Camera'].select = True    # Blender 2.7x

# https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/Scene_and_Object_API
bpy.data.objects['Camera'].select_set(True) # Blender 2.8x
    
bpy.ops.object.delete() 

In order to remove multiple objects, you can iterate through all objects, set the select state in Blender 2.7x or call select_set(state) in Blender 2.8x based on a certain condition and finally call the operator one time.

Blender 2.7x

import bpy

# Delect objects by type
for o in bpy.context.scene.objects:
    if o.type == 'MESH':
        o.select = True
    else:
        o.select = False

# Call the operator only once
bpy.ops.object.delete()

# Save and re-open the file to clean up the data blocks
bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)

Blender 2.8x

import bpy

# Select objects by type
for o in bpy.context.scene.objects:
    if o.type == 'MESH':
        o.select_set(True)
    else:
        o.select_set(False)

# Call the operator only once
bpy.ops.object.delete()

# Save and re-open the file to clean up the data blocks
bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)

Context override (2.7x & 2.8x)

Instead of (de)selecting the object(s) beforehand, you can also pass a custom context (a list of objects in this case) to avoid changing the selection within the viewport:

import bpy

objs = [bpy.context.scene.objects['Camera'], bpy.context.scene.objects['Cube']]
bpy.ops.object.delete({"selected_objects": objs})

Example on how to remove objects by certain types:

import bpy

objs = [ob for ob in bpy.context.scene.objects if ob.type in ('CAMERA', 'MESH')]
bpy.ops.object.delete({"selected_objects": objs})

Further reading: https://docs.blender.org/api/current/bpy.ops.html#overriding-context


Context override (3.2x)

The previous methodology has been deprecated and a dedicated context manager context.temp_override has been added to the Python API:

import bpy

objs = [bpy.context.scene.objects['Camera'], bpy.context.scene.objects['Cube']]
with bpy.context.temp_override(selected_objects=objs):
    bpy.ops.object.delete()

Further reading: https://wiki.blender.org/wiki/Reference/Release_Notes/3.2/Python_API https://docs.blender.org/api/3.2/bpy.types.Context.html#bpy.types.Context.temp_override

$\endgroup$
17
  • 1
    $\begingroup$ Doesn't work, the camera is still available in bpy.data.cameras $\endgroup$
    – Jan Rüegg
    Commented Mar 17, 2015 at 12:38
  • 2
    $\begingroup$ Ah, good to know, then thats the information I was missing! $\endgroup$
    – Jan Rüegg
    Commented Mar 17, 2015 at 12:48
  • 1
    $\begingroup$ if before that you had selected some other object, it will also be selected and then removed... $\endgroup$
    – ntg
    Commented Aug 25, 2016 at 16:11
  • 1
    $\begingroup$ @ntg Yep, that's the nature of the operator and also mentioned in the answer ...remove selected objects. What's your question? How to avoid that behavior? $\endgroup$
    – p2or
    Commented Aug 25, 2016 at 16:37
  • 1
    $\begingroup$ @poor: No question, just an observation.... Running bpy.ops.object.select_all(action='DESELECT') beforehand will deselect everything, "avoiding" it. $\endgroup$
    – ntg
    Commented Aug 26, 2016 at 8:13
88
$\begingroup$

This can be done without using ops which makes it much faster then using the ops call.

Buried deep in the documentation is the remove function.

import bpy
objs = bpy.data.objects
objs.remove(objs["Cube"], do_unlink=True)

The first parameter of the remove function is the object to remove (in this case the default "Cube"), the second is a boolean about first unlinking the object.
If you run objs.remove(objs["Cube"]) with the cube sitting in your 3D view you will get an error:

RuntimeError: Error: Object 'Cube' must have zero users to be removed, found 1 (try with do_unlink=True parameter)

See this remove function only deletes data with no users. That is why you have to explicitly tell it to first unlink the object then delete it.

$\endgroup$
2
  • 5
    $\begingroup$ note that this will work in 2.77 and higher only. prior vers have no do_unlink. :-/ $\endgroup$
    – Mechanic
    Commented Sep 5, 2017 at 4:43
  • 4
    $\begingroup$ Note that the documentation and behaviour now have do_unlink=True as the default, making it unnecessary to explicitly specify it. $\endgroup$
    – Will Chen
    Commented Mar 2, 2021 at 20:47
12
$\begingroup$

You can do this:

item='CAMERA'
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_by_type(type=item)
bpy.ops.object.delete()

where item can take any of the following values according to this documentation:

[‘MESH’, ‘CURVE’, ‘SURFACE’, ‘META’, ‘FONT’, ‘ARMATURE’, ‘LATTICE’, ‘EMPTY’, ‘CAMERA’, ‘LAMP’, ‘SPEAKER’]

The above method works, but is not ideal. The following method works and is ideal. First remove the meshes with the following code:

for obj in bpy.context.scene.objects:
     if obj.type == 'MESH':
         obj.select = True
     else:
         obj.select = False
 bpy.ops.object.delete()

Then you can execute the following code snippet to remove all the unused blocks. This way you don't have to close/open Blender or save/open a .blend file:

for block in bpy.data.meshes:
    if block.users == 0:
        bpy.data.meshes.remove(block)

for block in bpy.data.materials:
    if block.users == 0:
        bpy.data.materials.remove(block)

for block in bpy.data.textures:
    if block.users == 0:
        bpy.data.textures.remove(block)

for block in bpy.data.images:
    if block.users == 0:
        bpy.data.images.remove(block)

IMPORTANT NOTE: It looks like that there is some dependencies between some data blocks such as mesh, texture, image and materials. If you do not remove the data blocks on the highest level of the hierarchy, you will not be able to remove other data blocks or you have to take the risk and remove data blocks with users more than 0. So make sure you use the code above in the following order to remove data blocks. This way you can remove all unlinked (users == 0) data blocks:

remove meshes --> remove materials --> remove textures --> remove images

The followings also have data blocks:

bpy.data.curves
bpy.data.lamps
bpy.data.cameras
$\endgroup$
1
  • $\begingroup$ This is more relevant for preventing memory leakage in Blender $\endgroup$
    – Amir
    Commented Mar 1, 2018 at 4:04
8
$\begingroup$

Just to add up to the great answer above, one must be in OBJECT mode in order to delete objects. In order to do so in Python use the following code:

bpy.ops.object.mode_set(mode='OBJECT')

It also makes sense to return to previous mode, after the operation:

oldMode = bpy.context.mode
bpy.ops.object.mode_set(mode='OBJECT')
 ...Select your object here
bpy.ops.object.delete()
bpy.ops.object.mode_set(mode=oldMode)
$\endgroup$
1
$\begingroup$

To empty an entire scene, I typically include these lines at the start immediately after import bpy

    removeThese = bpy.context.copy()
    removeThese['selected_objects'] = list(bpy.context.scene.objects)
    bpy.ops.object.delete(removeThese)
$\endgroup$

You must log in to answer this question.

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