pythonblenderbpy

Execute script after Blender is fully loaded


How do I automatically execute a python script after Blender has fully loaded?

Context

My script generates a scene based on a seed. I want to creat a couple thousand images but since Blender leaks memory after a hundred generations or so everything becomes significantly slower and eventually crashes. I want to migitate the problem by creating only x images per session and completely restart Blender after each session.

Problem

If I load the blend file manually and click the play button in the script editor, everything works as expected. When I try to call the script after startup, it crashes in add_curve_spirals.pyline 184, since context.space_data is None.

Since manually starting the script works fine, the problem is, that Blender is in some sort of wrong state. Starting it with or without GUI (--background) does not affect this.

Failed solutions

These are all the ways that I found to automatically execute a script. In every case the script seems to be executed too early / in the wrong state and all fail in the same way.

I want to stress that the script is not the issue here. Even if I could work around the particular line, many similar problems might follow and I don't want to rewrite my whole script. So what is the best way to automatically invoke it in the right state?


Solution

  • It turns out, that the problem was the execution context. This became clear after invoking the timed event manually, e.g. after the scene was loaded completely the timed event was still executed in a wrong context.

    Since the crash happened in the add_curve_spirals addon, the solution was to provide a context override the the operator invokation. The rest of my script was not equally sensitive to the context and worked just fine.

    It was not clear to me, how exactly I should override the context, but this works for now (collected from other parts of the internet, so I don't understand all details):

    def get_context():
        # create a context that works when blender is executed from the command line.
        idx = bpy.context.window_manager.windows[:].index(bpy.context.window)
        window = bpy.context.window_manager.windows[idx]
        screen = window.screen
        views_3d = sorted(
                [a for a in screen.areas if a.type == 'VIEW_3D'],
                key=lambda a: (a.width * a.height))
        
        a = views_3d[0]
        # override
        o = {"window" : window,
             "screen" : screen,
             "area" : a,
             "space_data": a.spaces.active,
             "region" : a.regions[-1]
        }
        return o
    

    Final invocation: bpy.ops.curve.spirals(get_context(), spiral_type='ARCH', radius = radius, turns = turns, dif_z = dif_z, ...