Beyond the Horizon

Blog of Spencer Magnusson

Blender Add-on Keyboard Shortcuts

Guide, tips, and quirks to managing your own

September 15, 2026 | 7 minutes


Table of Contents ↑

User Experience

What You'll Need

The Basics

Finding References

Modal Operators

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.

Keyboard, stock photo by Aedrian Salazar

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:

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...
  1. addon_keymaps tracks keymap items you’ve added. Makes it easier for unregister() to find and delete them. I’m not one for global variables, but this is a minimal exception.

  2. Blender has multiple key configs:

    • default, Blender’s default built-in key bindings
    • active, key bindings based on your chosen preset (like “2.7x” or “Industry Standard”), stacks on top of default
    • addon
    • user, which stacks addon on top of active, along with a user’s own changes

    In most cases, only write to the addon keyconfig. Updating addon ensures you don’t accidentally overwrite a user’s preferred shortcuts, and makes your shortcuts easier to find and cleanup. When reading shortcuts, use user to account for the user’s changes.

  3. if wm.keyconfigs.addon will be False when 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.

  4. 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.

  5. 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.

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.