Reloading your code in Blender

UPDATE 2019: Things are considerably simpler. Just press F8 in Blender 2.79 or older, or press F3 and search for ‘Reload Scripts’ in recent Blenders.

Blender lets you script more or less everything. You can have your code inside Blender (and the .blend file) itself, but I prefer to store it outside the .blend file. This makes it easier to handle changes, and allows you to use any editor you want. The downside is that reloading your code becomes a bit of a hassle. This is a little snippet I wrote to reload my code in Blender with the click of a button.

import bpy
import imp
import os.path
import sys

my_path = bpy.path.abspath('//')

if my_path not in sys.path:
    sys.path.insert(0, my_path)

def load_my_stuff():
    # This imports my main module and registers its contents.
    # Alter to suit your needs.
    import calc_distance
    calc_distance.register()

def reload_my_modules(base_path):
    '''Reloads all my modules.

    "my modules" are those that are stored in the same directory (or subdir)
    of this .blend file.
    '''

    base_path = os.path.normpath(base_path)  # normalize the path
    my_module = sys.modules[__name__]

    print('Reloading from %r' % base_path)
    # Only allow reloading of pure-Python (i.e. not compiled) modules,
    # that have a __file__ and that are not this specific module.
    reloadable = (module for module in sys.modules.values()
                  if hasattr(module, '__file__') and
                     module != my_module and
                     (module.__file__.endswith('.py') or
                      module.__file__.endswith('.pyc')))

    for module in reloadable:
        modpath = os.path.abspath(module.__file__)
        common = os.path.commonprefix([base_path, modpath])
        if os.path.commonprefix([base_path, modpath]).startswith(base_path):
            print('    Reloading %s from %s' % (module, module.__file__))
            imp.reload(module)

    # re-register the reloaded classes.
    load_my_stuff()

class ReloadScriptsOperator(bpy.types.Operator):
    bl_idname = 'script.reload_my_scripts'
    bl_label = 'Reload code'
    bl_description = 'Reloads all distance code.'

    def execute(self, context):
        my_path = bpy.path.abspath('//')
        if not my_path:
            self.report({'ERROR'}, 'Save the Blend file first, before reloading')
            return {'CANCELLED'}

        reload_my_modules(my_path)
        return {'FINISHED'}

print(80 * '*')
print('Registering %s and the rest' % __name__)
bpy.utils.register_module(__name__)

load_my_stuff()

The code makes one assumption that’s important to know: it assumes that the Python files you want to reload are in the same directory as your Blend file (or subdirectory thereof). This means that you must save your Blend file before this works. If you store your code in a different directory, you’ll have to modify the matching against my_path to suit your needs.

In order to have the “Reload code” button in the user interface somewhere, add this line to your UI code:

layout.operator('script.reload_my_scripts')

For completeness, here are the relevant parts of my custom UI panel:

class ReloadExamplePanel(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_category = "Tools"
    bl_context = "objectmode"
    bl_label = "Reload button example"

    def draw(self, context):
        layout = self.layout
        layout.operator('script.reload_my_scripts')

        # Here I draw more stuff, but that's irrelevant if you don't have my code.

def register():
    print('Registering %s' % __name__)
    bpy.utils.register_module(__name__)</pre>
dr. Sybren A. Stüvel
dr. Sybren A. Stüvel
Open Source software developer, photographer, drummer

Related