Using Blender's presets in Python

Looking to add sup­port for pre­sets in your addon? Read on to find out how!

There’s a built-in pre­sets sys­tem in Blender used for oper­a­tors and pan­els. The good news is that it’s Python based and easy to use. Presets are Python files that man­u­al­ly set val­ues. When you select a pre­set in the menu that par­tic­u­lar script is read from disk and run. Adding/removing pre­sets involves cre­at­ing or delet­ing py files from the pre­sets fold­er. This is han­dled for us, but you might want to keep it mind for oth­er things (like pack­ag­ing pre­sets). The pre­sets fold­er is inside your per­son­al Blender scripts folder. 

Linux~/.config/blender/2.81/scripts/presets/
Mac/Users/{user}/Library/Application/Support/Blender/2.81/scripts/presets/
WindowsC:\Documents and Settings\%username%\Application Data\Blender Foundation\Blender\2.81\scripts\presets\

Unfortunately there’s no way of importing/exporting pre­sets yet oth­er than man­u­al­ly copy­ing files (or mak­ing an oper­a­tor that does that). Also note that the built-in pre­sets sys­tem only includes the menu we often see on top of oper­a­tors and pan­els. If you want to have images, thumb­nails and oth­er advanced fea­tures you will have to roll your own system.

For operators

Adding pre­sets to oper­a­tors is as sim­ple as it gets. Just add the PRESET to the bl_options set and you’re done.

class My_OP(Operator):
    bl_idname = "my.operator"
    bl_label = 'My Cool OP'
    bl_options = {'PRESET'}

Blender will add the pre­set menu to the oper­a­tor UI for you. It will even cre­ate a fold­er for them in the pre­sets direc­to­ry! (note that you need to save at least one pre­set for that)

For panels

Using the pre­sets sys­tem on pan­els only takes a bit more work. It also brings some extra flex­i­bil­i­ty on the UI.

First we have to decide where to save the pre­sets. This could be your addons name, or a cat­e­go­ry with your addon as a sub­fold­er. For this exam­ple let’s use ‘object/my_presets’. The full path would then
be ~/.config/blender/2.81/scripts/presets/object/my_presets/.

Next we need to make an oper­a­tor to add/remove pre­sets. Let’s bring the AddPresetsBase mix­in into the mix (hah!).

from bl_operators.presets import AddPresetBase

class MT_MyPresets(Menu): 
    bl_label = 'My Presets' 
    preset_subdir = 'object/my_presets' 
    preset_operator = 'script.execute_preset' 
    draw = Menu.draw_preset

class OT_AddMyPreset(AddPresetBase, Operator):
    bl_idname = 'my.add_preset'
    bl_label = 'Add A preset'
    preset_menu = 'MT_MyPresets'

    # Common variable used for all preset values
    preset_defines = [
                        'obj = bpy.context.object',
                        'scene = bpy.context.scene'
                     ]

    # Properties to store in the preset
    preset_values = [
                        'obj.show_axis',
                        'obj.show_name',
                        'obj.show_bounds',
                        'scene.world'
                    ]

    # Directory to store the presets
    preset_subdir = 'object/my_presets'

There’s no need to add an execute() or invoke() func­tion in this case, since the mix­in imple­ments them. If you want to take a peek at the code behind this mag­ic you can find it in [BLENDER_FOLDER]/bin/2.81/scripts/startup/bl_operators/presets.py We can also use this oper­a­tor to remove pre­sets by set­ting remove_active to True.

The meaty part of this code are the preset_defines and preset_values prop­er­ties. The first list lets you declare vari­ables com­mon to all the pre­set’s val­ues. This is only for con­ve­nience. It saves you from typ­ing bpy.blah.bla over and over. The sec­ond list are the actu­al val­ues that will be saved in the pre­set. With the oper­a­tor done and ready, we now have to make a class for the head­er and add it to the panel.

from bl_ui.utils import PresetPanel
from bpy.types import Panel, Menu

# [...]

class MY_PT_presets(PresetPanel, Panel):
    bl_label = 'My Presets'
    preset_subdir = 'object/my_presets'
    preset_operator = 'script.execute_preset'
    preset_add_operator = 'my.add_preset'

# Now we need to make our actual panel draw the header from the presets panel

class My_PT_Panel(Panel):
    bl_label = 'My Panel'
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'output'

    def draw_header_preset(self, _context):
        MY_PT_presets.draw_panel_header(self.layout)

    def draw(self, context):    
        # The rest of our panel's code 
        pass

Packaging presets

There’s no “offi­cial” way of pack­ag­ing pre­sets with addons. But since they are just files we can eas­i­ly do this our­selves. The process goes like this: First check if the pre­sets fold­er exists, if it does­n’t that means this is the first time the addon is enabled. In that case copy the addon files from the addon fold­er to the pre­sets fold­er. If it does exist we can assume the pre­sets have already been installed (in which case we don’t do anything).

We can do all this in the register() function.

import os
import shutils

# [...]

my_presets = os.path.join(presets_folder, 'object', 'my_presets')

if not os.path.isdir(my_presets):
    # makedirs() will also create all the parent folders (like "object")
    os.makedirs(my_presets)

    # Get a list of all the files in your bundled presets folder
    files = os.listdir(my_bundled_presets)

    # Copy them
    [shutil.copy2(os.path.join(my_bundled_presets, f), my_presets)
     for f in files]

Updated: The code has been updat­ed for Blender 2.80+

All the posts you can read
TutorialsAddon, Blender, Python25.11.2019