Using Blender's presets in Python
Looking to add support for presets in your addon? Read on to find out how!
There’s a built-in presets system in Blender used for operators and panels. The good news is that it’s Python based and easy to use. Presets are Python files that manually set values. When you select a preset in the menu that particular script is read from disk and run. Adding/removing presets involves creating or deleting py files from the presets folder. This is handled for us, but you might want to keep it mind for other things (like packaging presets). The presets folder is inside your personal Blender scripts folder.
Linux | ~/.config/blender/2.81/scripts/presets/ |
Mac | /Users/{user}/Library/Application/Support/Blender/2.81/scripts/presets/ |
Windows | C:\Documents and Settings\%username%\Application Data\Blender Foundation\Blender\2.81\scripts\presets\ |
Unfortunately there’s no way of importing/exporting presets yet other than manually copying files (or making an operator that does that). Also note that the built-in presets system only includes the menu we often see on top of operators and panels. If you want to have images, thumbnails and other advanced features you will have to roll your own system.
For operators
Adding presets to operators is as simple 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 preset menu to the operator UI for you. It will even create a folder for them in the presets directory! (note that you need to save at least one preset for that)
For panels
Using the presets system on panels only takes a bit more work. It also brings some extra flexibility on the UI.
First we have to decide where to save the presets. This could be your addons name, or a category with your addon as a subfolder. For this example 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 operator to add/remove presets. Let’s bring the AddPresetsBase
mixin 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()
function in this case, since the mixin implements them. If you want to take a peek at the code behind this magic you can find it in [BLENDER_FOLDER]/bin/2.81/scripts/startup/bl_operators/presets.py
We can also use this operator to remove presets by setting remove_active
to True.
The meaty part of this code are the preset_defines
and preset_values
properties. The first list lets you declare variables common to all the preset’s values. This is only for convenience. It saves you from typing bpy.blah.bla over and over. The second list are the actual values that will be saved in the preset. With the operator done and ready, we now have to make a class for the header 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 “official” way of packaging presets with addons. But since they are just files we can easily do this ourselves. The process goes like this: First check if the presets folder exists, if it doesn’t that means this is the first time the addon is enabled. In that case copy the addon files from the addon folder to the presets folder. If it does exist we can assume the presets 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 updated for Blender 2.80+