58

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?

Jan Rüegg
  • 693
  • 1
  • 5
  • 9
  • 5
    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. – CodeManX Mar 17 '15 at 13:21

5 Answers5

86

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.

p2or
  • 15,860
  • 10
  • 83
  • 143
David
  • 49,291
  • 38
  • 159
  • 317
  • 5
    note that this will work in 2.77 and higher only. prior vers have no do_unlink. :-/ – Mechanic Sep 05 '17 at 04:43
  • 4
    Note that the documentation and behaviour now have do_unlink=True as the default, making it unnecessary to explicitly specify it. – Will Chen Mar 02 '21 at 20:47
73

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

Iaian7
  • 98
  • 7
p2or
  • 15,860
  • 10
  • 83
  • 143
  • 1
    Doesn't work, the camera is still available in bpy.data.cameras – Jan Rüegg Mar 17 '15 at 12:38
  • 2
    Ah, good to know, then thats the information I was missing! – Jan Rüegg Mar 17 '15 at 12:48
  • 1
    if before that you had selected some other object, it will also be selected and then removed... – ntg Aug 25 '16 at 16:11
  • 1
    @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? – p2or Aug 25 '16 at 16:37
  • 1
    @poor: No question, just an observation.... Running bpy.ops.object.select_all(action='DESELECT') beforehand will deselect everything, "avoiding" it. – ntg Aug 26 '16 at 08:13
  • 1
    @ntg You can set the objects select flag to False for certain objects in the scene/context. For more control go with a for loop instead (for ob in bpy.data.objects: ob.select = False), which does basically the same as the operator, but allows for instance to exclude objects in order to "avoid" re-selecting ;) – p2or Aug 26 '16 at 09:39
  • It doesn't seem to work if the object is on another layer. – anatoly techtonik Jan 09 '17 at 13:31
  • @p2or I have some memory leakage issues. While searching, I came across your answer. I wonder, are there better ways (after about two years) to solve memory leakage issues and removing left-out data blocks? Could you please take a look at my question here? I hope you can offer a solution besides saving/opening blender file. Thanks in advance! – Amir Mar 01 '18 at 03:11
  • @JanRüegg I found a very easy solution to the problem :) I updated my answer in this question too – Amir Mar 01 '18 at 04:05
  • Worth noting some objects in bpy.data.objects will never have context (not in context.selected_objects), eg if not a member of the context scene, will not be removed even when select attribute is set to true when using the delete operator. – batFINGER Oct 18 '18 at 09:48
  • FYI I am using Blender 2.8b and Object no longer seems to have the select attribute on it. – Tomáš Hübelbauer Jan 28 '19 at 23:39
  • 1
    @TomášHübelbauer Updated the Answer for 2.8x. – p2or Jan 29 '19 at 11:12
  • Isn't use_global parameter for such cases? @batFINGER – p2or Jan 29 '19 at 11:39
  • 2.8 full copy scene, to test run script RuntimeError: Error: Object 'Camera' not in View Layer 'RenderLayer On the global flag, am of the understanding that it will remove same object from all scenes that it is linked to. Wont delete other objects in other scene or from data if not linked to context scene. – batFINGER Jan 29 '19 at 12:45
  • Yeah right, same here @batFINGER. Thanks for testing, I'll check whether there is some bizarre workaround or mention that. – p2or Jan 29 '19 at 13:35
  • Suggest iterate over scene objects, not bpy.data.objects – batFINGER Jan 29 '19 at 14:23
  • Thanks, iterating over scene objects now :) @batFINGER – p2or Apr 02 '19 at 12:43
12

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
Amir
  • 3,074
  • 2
  • 29
  • 55
8

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)
1

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)
Jay Wilson
  • 37
  • 4