Addon shortcuts?

I’m trying to register a new shortcut from an addon but I can’t figure out how to do it.

Here’s my current, non working, code:

bl_info = {
	'name': 'Shortcut Test',
	'version': (0, 1, 0),
	'blender': (2, 79, 0),
	'description':
		'',
	'category': 'Test'
}

import bpy

class OT_Shortcut_Test(bpy.types.Operator):
	bl_idname = "wm.shortcut_test"
	bl_label = "Shortcut Test"

	def execute(self, context):
		self.report({'INFO'}, 'SHORTCUT TEST')
		return {'FINISHED'}

classes=[OT_Shortcut_Test]
addon_keymaps = []

def register():
	for cls in classes:
		bpy.utils.register_class(cls)

	wm = bpy.context.window_manager
	if wm.keyconfigs.addon:
		km = wm.keyconfigs.addon.keymaps.new(name='Shortcut Test', space_type='EMPTY')
		kmi = km.keymap_items.new('wm.shortcut_test', 'A', 'PRESS')
		addon_keymaps.append((km, kmi))

def unregister():
	for cls in classes:
		bpy.utils.unregister_class(cls)

	wm = bpy.context.window_manager
	kc = wm.keyconfigs.addon
	if kc:
		for km, kmi in addon_keymaps:
			km.keymap_items.remove(kmi)
	addon_keymaps.clear()

if __name__ == "__main__":
	register()

Normally you should not create a new keymap, but rather add an item to the appropriate existing keymap depending in which editor or mode you want the shortcut to work.

1 Like

I see, thank you @brecht.
So it should be just:

km = wm.keyconfigs.active.keymaps["Window"]
kmi = km.keymap_items.new('wm.shortcut_test', 'A', 'PRESS')

So there’s no built-in way to handle conflicting shortcuts, right?
I should just search and disable them?

Right, there is no automatic way to detect conflicting shortcuts.

However Blender gives first priority to user defined shortcuts, then addon shortcuts, and then builtin ones. So the overriding is automatic in most cases.

2 Likes

However Blender gives first priority to user defined shortcuts, then addon shortcuts, and then builtin ones

Really?
Then I must be doing something wrong cause my snippet code doesn’t work unless I change the shortcut to an unused one (like “F7”) or disable any conflicting shortcut.

Ah right, it’s a bit more complicated. That priority works within one keymap. But there is also a priority between keymaps, where for example the 3D view keymap overrides the window keymap.

The Select All operators bound to A are defined per editor keymap, so you would need to add your shortcuts to those keymaps to override them.

1 Like

Ok, now I got it.

I was missing two important things.

One is that you actually have to create a new keymap inside keyconfig addons, so instead of

km = wm.keyconfigs.active.keymaps["Window"]

It should be

km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY')

Otherwise it will only work as long as the addon loads on the Blender startup.

The second important thing I was missing is that the keymap must have the name of an already existing keymap.

I was taking as a reference the built-in addons and I was under the impression that they were defining new keymap categories (like ‘3D View Generic’, for example), but nope, they are all already defined in bpy_extras\keyconfig_utils.py.

And after that you have to take into account the priorities between different keymaps too.

So the final, actually working snippet looks like this:

bl_info = {
	'name': 'Shortcut Test',
	'version': (1, 0, 0),
	'blender': (2, 79, 0),
	'category': 'Test'
}

import bpy

class OT_Shortcut_Test(bpy.types.Operator):
	bl_idname = "wm.shortcut_test"
	bl_label = "Shortcut Test"

	def execute(self, context):
		self.report({'INFO'}, 'SHORTCUT TEST')
		return {'FINISHED'}

classes=[OT_Shortcut_Test]
addon_keymaps = []

def register():
	for cls in classes:
		bpy.utils.register_class(cls)

	wm = bpy.context.window_manager
	if wm.keyconfigs.addon:
		km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY')
		kmi = km.keymap_items.new('wm.shortcut_test', 'SPACE', 'PRESS')
		addon_keymaps.append((km, kmi))

def unregister():
	for cls in classes:
		bpy.utils.unregister_class(cls)

	wm = bpy.context.window_manager
	kc = wm.keyconfigs.addon
	if kc:
		for km, kmi in addon_keymaps:
			km.keymap_items.remove(kmi)
	addon_keymaps.clear()

if __name__ == "__main__":
	register()
2 Likes

@brecht Sorry to raise this here, but… can you tell me why this doesn’t work when run from a text window in Blender, this is the end of the code that registers the bits:

# Registration
addon_keymaps = []

def draw_menu(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(precision_draw_tools_cm.bl_idname,text="Precision Drawing Tools",icon='EMPTY_AXIS')

def register():
    bpy.utils.register_class(precision_draw_tools_cm)
    bpy.types.VIEW3D_MT_edit_mesh_vertices.append(draw_menu)
    # handle the keymap
    wm = bpy.context.window_manager
    # Note that in background mode (no GUI available), keyconfigs are not available either,
    # so we have to check this to avoid nasty errors in background case.
    kc = wm.keyconfigs.addon
    if kc:
        km = wm.keyconfigs.addon.keymaps.new(name='Edit Mode', space_type='EMPTY')
        kmi = km.keymap_items.new(precision_draw_tools_cm.bl_idname, 'P', 'PRESS', ctrl=False, shift=True)
        addon_keymaps.append((km, kmi))

def unregister():
    # handle the keymap
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()
    bpy.utils.unregister_class(precision_draw_tools_cm)
    bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(draw_menu)

if __name__ == "__main__":
    register()

I can’t see whats wrong with this… thanks!

At a glance, I’d say it’s because there’s no such keymap as ‘Edit Mode’. Perhaps you want ‘Mesh’?

Thank you! I tried “Window”, which worked, but I can see this will lead to problems in other than 3D windows…

Cheers, Clock.

EDIT:

I used “Mesh” … works a treat

is there no category for meshes?