Blender Add-on Keyboard Shortcuts
Guide, tips, and quirks to managing your own
September 15, 2026 | 7 minutes
This has been a tutorial idea in the back of my mind for several months. However, it’s much more technical. Most coders would rather copy the code and skim a written breakdown instead of watching me type it out. So here we are.
If you’re interested in learning how to make your own add-ons, feel free to check out my course to script your first add-on, both on CG Cookie via subscription, or as a standalone purchase on Superhive Market. And I’m working on a sequel course as well!
Without further ado, let’s dive in.
User Experience
Keyboard shortcuts are vital to Blender. Once you learn shortcuts, workflows flow fast (now say that five times, fast).
Blender developed their UX in recent years to make operations more accessible and discoverable outside of shortcuts. In general, ensure your operations do the same. Add them to a menu, panel, or tool. This is how Blender adds operators to its search anyways. Most Blender artists who aren’t power users don’t bother making shortcuts. And if they really need a shortcut, they will use Quick Shortcuts instead.
Ponder this question: should I create a default shortcut for my operator?
In my opinion, keyboard shortcuts can easily confuse more than help, especially when they are initially unknown to the user. So when in doubt, I’d say don’t bother. The user can always add one themselves.
If your answer is a firm yes, then think about common conventions. For example, a video editing add-on should use shortcuts consistent to other video editors. If you’re overriding existing operations, don’t hardcode the key binding; not everyone uses the default key bindings.
Once you’ve thought of possible key bindings, search Blender’s keymaps. Open Blender in factory default settings, so you don’t get your own changes. See which are unused and convenient. And be open to feedback from your add-on’s users.
If your new operator has an additional prompt, like
properties to adjust or an “Are you sure?” — add a
separate shortcut and operator setting that can bypass
it. In the case of deleting objects, the X
key deletes objects with the prompt. Delete
skips the prompt.
With these design principles in mind, you’re ready to code a keyboard shortcut!
What You’ll Need
But first, a few important components required to add a shortcut:
- UI
space type - which UI space the shortcut
will be used in.
VIEW_3D?OUTLINER? If it can be used just about anywhere, default toEMPTY. - Keymap - Blender splits keyboard
shortcuts into separate keymaps, often based on the UI
space as well as context modes
(e.g.
Object Mode). You need to know which keymap your operator should belong in. - UI
region type - usually this defaults to
WINDOW, but there’s a few other region types for specific use cases, likeUIfor the panel shelf. - Operator ID name - You know how
operator ID names have a category, like
objectinbpy.ops.object.delete()? Fun fact: which one you pick actually matters here. Because Blender expectsobjectoperators to be available for, well, object mode. Blender subtly enforces different categories for different keymaps. So for your operator’s ID, pick the right category. If the added shortcut weirdly doesn’t work, that’s often why.
The Basics
Here’s a basic registration of a keyboard shortcut:
addon_keymaps = []
def register():
# register the rest of your add-on...
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
km = wm.keyconfigs.addon.keymaps.new(
name='Pose',
space_type='EMPTY',
region_type='WINDOW'
)
kmi = km.keymap_items.new(
'object.my_pose_operator',
'P', 'PRESS',
shift=True,
)
addon_keymaps.append((km, kmi))
def unregister():
wm = bpy.context.window_manager
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
# unregister your add-on...addon_keymapstracks keymap items you’ve added. Makes it easier forunregister()to find and delete them. I’m not one for global variables, but this is a minimal exception.Blender has multiple key configs:
default, Blender’s default built-in key bindingsactive, key bindings based on your chosen preset (like “2.7x” or “Industry Standard”), stacks on top ofdefaultaddonuser, which stacksaddonon top ofactive, along with a user’s own changes
In most cases, only write to the
addonkeyconfig. Updatingaddonensures you don’t accidentally overwrite a user’s preferred shortcuts, and makes your shortcuts easier to find and cleanup. When reading shortcuts, useuserto account for the user’s changes.if wm.keyconfigs.addonwill beFalsewhen running Blender in the terminal without a UI. No need to register keyboard shortcuts without a UI. Also, your add-on will throw an error otherwise.Adding a new keymap may seem strange. But remember, this key config is for add-ons only. It likely will only have your shortcuts inside it anyway. There is a chance another add-on may add this exact same keymap, but
new()will return an existing one for you.Add the keymap item, tagging the operator ID, its key binding, and any other modifier keys. You can even set default operator properties here too.
Finding References
Now you know how the components fit, but how do you know which values to use? How could you possibly know which keymap to pick for your operator?
Luckily, I made a GitHub gist that can give you reference from existing operators with keyboard shortcuts. It prints a template to create a keymap with the matching space type, keymap name, region type, and operator category. I’ll attach it below:
import inspect
import bpy
wm = bpy.context.window_manager
# FIND YOUR BPY.OPS OPERATOR HERE #
to_find = 'object.parent_set'
matching = {
(kc, km)
for km in wm.keyconfigs.user.keymaps
for kmi in km.keymap_items
if kmi.idname == to_find
}
results = [
{
'name': km.name,
'space_type': km.space_type,
'region_type': km.region_type,
}
for kc, km in matching
]
unique_results = {r['name']: r for r in results}.values()
for result in unique_results:
print(
'{} found, '
'with the keymap name \"{}\", '
'space_type \"{}\", '
'and region_type \"{}\"'.format(
to_find,
result['name'],
result['space_type'],
result['region_type']
))
prefix = to_find.split('.')[0]
print(inspect.cleandoc("""
If you wanted to override this with your own operator,
you can code this:
```
addon_keymaps = []
def register():
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
km = wm.keyconfigs.addon.keymaps.new(
name='{}',
space_type='{}',
region_type='{}'
)
kmi = km.keymap_items.new(
'{}.my_custom_operator',
'ADD_KEY_TYPE_HERE',
'PRESS'
)
addon_keymaps.append((km, kmi))
def unregister():
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
```
""".format(
result['name'],
result['space_type'],
result['region_type'],
prefix
)))Use this as a guide to find operators similar to yours. Especially if you are going to override existing shortcuts for your own version of an operation.
Modal Operators
What about modal operators, like the famous bevel and knife tools? Surely, we can make keymaps for our own modal operations. Just like them, right? Right?
Sadly, it’s not supported in the Python API. Apparently, they added it long ago, but it wasn’t really working, so they disabled it and haven’t added it back since.
So any modal keymaps you see for add-ons are all workarounds. While there’s no Blender-endorsed way, I’ll at least explain mine, based on Light Painter. I’m sure there’s a better way (and if you know it, share it and I’ll update it here!).
Firstly, in register(), I create
pseudo-modal keymap items. I set operators (never used)
that has a name property, set its
name with the modal command name and a
prefix (say, “LIGHTPAINTER_”), set the key bindings,
then disable it to prevent accidental usage.
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
global kmi_added
km = kc.keymaps.new(
name='3D View Generic',
space_type='VIEW_3D',
region_type='WINDOW'
)
kmi = km.keymap_items
for default_keymap in keymap.UNIVERSAL_KEYMAP:
km_copy = dict(default_keymap)
kmi = kmi.new('wm.call_menu', **km_copy)
kmi.properties.name = 'LIGHTPAINTER_command_name'
kmi.active = False
kmi_added.append(kmi)Second, to check Blender bpy.types.Event
in my modal operator, I match the event with my
pseudo-keymap items. I check the user key config, and
filter based on my name prefix. Then, with the Blender
event, I compare the values with my own function.
user_keyconfig = context.window_manager.keyconfigs.user
user_keymap = user_keyconfig.keymaps['3D View Generic']
LIGHTPAINTER_KEYMAP_ITEMS = tuple(
item
for item in user_keymap.keymap_items
# match my pseudo-keymap items
if (
item.idname == 'wm.call_menu' and
item.properties and
hasattr(item.properties, 'name') and
item.properties.name.startswith('LIGHTPAINTER')
)
)
def compare_kmi_to_event(item, event,):
"""Checks if Blender keymap item matches a UI event.
Builds a string for both keymap events and compares them.
"""
data = item.type
event_data = event.type
data += item.value
event_data += event.value
data += str(
item.shift * 1 |
item.ctrl * 2 |
item.alt * 4 |
item.oskey * 8
)
event_data += str(
event.shift * 1 |
event.ctrl * 2 |
event.alt * 4 |
event.oskey * 8
)
return data == event_data
def get_matching_event(context, event) -> str | None:
"""Check if user event matches a modal command."""
return next(
(
item.properties.name.replace(PREFIX, '')
for item in LIGHTPAINTER_KEYMAP_ITEMS
if compare_kmi_to_event(
item, event
)
),
None,
)The reason I can’t use
keymap_items.match_event(event) is because
I disabled the keymap item to prevent accidental usage
outside the modal. And — outside this simplified script
example — but for Light Painter, I need to also check
for matches ignoring some modifier keys. But in
non-modal operations, absolutely use
keymap_items.match_event(event).
Then I draw the keymap within my add-on preferences. I omit the meaningless operator name call, just showing the key binding settings with the command names. I mostly just copied Blender’s internal code for drawing the keymap items and tweaked it from there.