11
$\begingroup$

I'm implementing a Blender Python script to do batch conversions. It works like this:

for each model file found in a directory:

  • Import the model
  • Process it
  • Export in other format

The problem is, that after each iteration, the imported models, textures/materials and everything are still contained in the (scene?) blender instance.


How do i completely clear everything that is loaded in Blender in each iteration?

$\endgroup$

4 Answers 4

15
$\begingroup$

2.79 or newer

Note, to avoid having to remove this extra data, you can start a new file with blank scene, objects..., eg:

bpy.ops.wm.read_factory_settings(use_empty=True)

Or...

bpy.ops.wm.read_homefile(use_empty=True)

Old Answer (useful for partially clearing)

This can be done by running 'Reset Factory Settings' operator (then clear the data).

The advantage with doing this is we only need to clear the default Cube, Camera & Lamp. Since 'Reset Factory Settings' will clear everything else each time (images, packed data, curves.. etc).

import bpy

def reset_blend():
    bpy.ops.wm.read_factory_settings()

    for scene in bpy.data.scenes:
        for obj in scene.objects:
            scene.objects.unlink(obj)

    # only worry about data in the startup scene
    for bpy_data_iter in (
            bpy.data.objects,
            bpy.data.meshes,
            bpy.data.lamps,
            bpy.data.cameras,
    ):
        for id_data in bpy_data_iter:
            bpy_data_iter.remove(id_data)

reset_blend()

If you wanted to you could skip calling bpy.ops.wm.read_factory_settings(), but theres a possibility some other data was loaded (curves or lattice for eg) that you don't know about. The clearing function could be extended to test for all possible data, but loading factory settings is an easier way to ensure you didn't miss some.

$\endgroup$
4
  • 1
    $\begingroup$ When I bpy.ops.wm.read_homefile(use_empty=True), I get the warning DAG zero... not allowed to happen! Then later in my script, I get Operator bpy.ops.script.python_file_run.poll() failed, context is incorrect I know that's not the most detailed report, but any ideas how read_homefile() might leave things in a strange state? $\endgroup$ Mar 19, 2019 at 19:39
  • $\begingroup$ for 2.78 reset_blend() is the only one that removed everything for me $\endgroup$
    – j314erre
    Apr 5, 2019 at 3:17
  • $\begingroup$ @j314erre what data is kept w/ bpy.ops.wm.read_homefile ? $\endgroup$
    – ideasman42
    Apr 6, 2019 at 7:23
  • $\begingroup$ @ideasman42 I'm rendering 100's of images in a loop and the rendering time would shoot up from less than 1sec to over 1min per image. Using bpy.ops.wm.open_mainfile() helped the bpy.ops.wm.memory_statistics() not grow, but the rendering time keep going up steadily. Your reset_blend() function solved the problem for me, keeping rendering time fast for 1000's of images. $\endgroup$
    – j314erre
    Apr 7, 2019 at 19:20
5
$\begingroup$

ideasman42's idea to reset everything to factory settings is pretty interesting. However, if you want to keep the current settings you can remove the unlinked data blocks, if you are willing to use Blender's Python API. This way you can make sure there will never be any memory leakage what so ever. You can execute the following code after you load each object or every couple of times (say every 10 objects).

First remove the meshes/objects 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()

where you can check type against any of the following values according to [this documentation][1]:

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

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$
2
  • $\begingroup$ This is also relevant to how you can prevent memory leakage in Blender $\endgroup$
    – Amir
    Mar 6, 2018 at 15:50
  • $\begingroup$ No need to select anything. You can override the context: blender.stackexchange.com/a/27235/31447 $\endgroup$
    – brockmann
    May 8, 2020 at 10:19
1
$\begingroup$

You can just reopen your blend file with open_mainfile:

bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)

That will clear-up all data created after file been saved.

$\endgroup$
3
  • $\begingroup$ This assumes you will be distributing a blend file with your batch processing tools, while it can work it seems an unnecessary step - especially when you only use for clearing contents. $\endgroup$
    – ideasman42
    Mar 26, 2017 at 21:35
  • $\begingroup$ @ideasman42 question does not specify requirements for working conditions, and for a quick production solution my answer is still valid. $\endgroup$ Mar 28, 2017 at 4:45
  • $\begingroup$ Beware that this will clear your unsaved Python script upon execution. Just found out the hard way. :-) $\endgroup$ Jan 28, 2019 at 22:28
1
$\begingroup$

Echo to @Amir's answer, and based on blender 2.8 update, selecting mesh should be done according to the API changes:

2.7x:

bpy.data.objects['Cube'].select = True

2.8x:

bpy.data.objects['Cube'].select_set(True)

This answer comes from:

Python: Selecting object by name in 2.8

$\endgroup$
3
  • $\begingroup$ While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review $\endgroup$ Aug 22, 2019 at 16:26
  • $\begingroup$ @RayMairlot Thanks for the comment. Added. $\endgroup$
    – X.Arthur
    Aug 22, 2019 at 22:52
  • $\begingroup$ No need to select anything. You can override the context: blender.stackexchange.com/a/27235/31447 $\endgroup$
    – brockmann
    May 8, 2020 at 10:19

You must log in to answer this question.

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