Finding out keymap bindings at register in 2.80

How do you retrieve keymap bindings in an add-on’s register function? For example, in Blender 2.79 you could do this inside register:

    keymaps = bpy.context.window_manager.keyconfigs.active.keymaps
    print(keymaps['Object Mode'].keymap_items['object.hide_view_set'].type)

In 2.80, this is no longer possible. Using the same code at register results in this error:
KeyError: 'bpy_prop_collection[key]: key "object.hide_view_set" not found'

I tried replacing keyconfigs.active with keyconfigs.default and had the same error. In 2.80 the keymaps values have nothing assigned to the when register is called when Blender loads. Inserting this in an add-on’s register function prints out an empty list:

    keymaps = bpy.context.window_manager.keyconfigs.active.keymaps
    print(keymaps['Object Mode'].keymap_items.keys())

This type of operation was useful for setting an add-on’s keymaps to match with existing Blender settings.

This annoyed me as well. I disable a lot of defaults using an addon instead of dealing with dirty exported keyconfigs.

As much as I hate to admit, I keep going back to using a timer every time I’m brickwalled by RestrictContext, RestrictData, and of course blender loading addons before the default keymaps.

Curiously enough, even with a very low interval the timer seems to always succeed on the second attempt.

def register():

    ...

    register_keymaps()


def register_keymaps():
    kc = bpy.context.window_manager.keyconfigs
    areas = 'Window', 'Text', 'Object Mode', '3D View'

    if not all(i in kc.active.keymaps for i in areas):
        bpy.app.timers.register(register_keys, first_interval=0.1)

    else:
        # can now proceed with checking default kmis
        pass

1 Like

same. I find myself having to hack around this and other issues, such that I hope at some point in the near future a discussion can be started on revamping the entire keymap/keyconfig system to be a bit more forward thinking.

Wild, this looks buggy. Not so much the workaround you listed, but that a workaround like this is needed. I thought the loss of keymap data was an unintended change with the move from 2.79 to 2.80 or some new interface I overlooked, but are you saying this change in behavior was intentional?

I think there was a small spelling mistake in your code, I was getting a “register_keys not found” error. Replacing “register_keys” with “register_keymaps” worked:

def register():

    ...

    register_keymaps()


def register_keymaps():
    kc = bpy.context.window_manager.keyconfigs
    areas = 'Window', 'Text', 'Object Mode', '3D View'

    if not all(i in kc.active.keymaps for i in areas):
        bpy.app.timers.register(register_keymaps, first_interval=0.1)

    else:
        # can now proceed with checking default kmis
        print(kc.active.keymaps['Object Mode'].keymap_items['object.hide_view_set'].type)
        pass

Since 2.80 the order of loading keymaps has changed. I’ve made a patch that makes add-ons load after the keymap, I’ll talk to Brecht about this since we both worked on this area.

diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index e212df17f60..daaf9afddf9 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -23,6 +23,7 @@ __all__ = (
     "modules",
     "check",
     "enable",
+    "enable_all",
     "disable",
     "disable_all",
     "reset_all",
@@ -43,6 +44,9 @@ def _initialize():
     path_list = paths()
     for path in path_list:
         _bpy.utils._sys_path_ensure_append(path)
+
+
+def enable_all():
     for addon in _preferences.addons:
         enable(addon.module)
 
diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 04aaa7bd69d..4cb2377112b 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -275,7 +275,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
     # deal with addons separately
     _initialize = getattr(_addon_utils, "_initialize", None)
     if _initialize is not None:
-        # first time, use fast-path
+        # first time, initialize add-on paths (doesn't load add-ons).
         _initialize()
         del _addon_utils._initialize
     else:
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 3632eb9eb9a..d9b7da4e210 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -59,6 +59,8 @@
 #include "BKE_image.h"
 #include "BKE_particle.h"
 
+#include "BPY_extern.h"
+
 #include "DEG_depsgraph.h"
 
 #include "IMB_imbuf.h" /* for IMB_init */
@@ -445,6 +447,10 @@ int main(int argc,
   CTX_py_init_set(C, true);
   WM_keyconfig_init(C);
 
+#ifdef WITH_PYTHON
+  BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.enable_all()");
+#endif
+
 #ifdef WITH_FREESTYLE
   /* initialize Freestyle */
   FRS_initialize();
3 Likes

Note that checking the add-ons on register seems unreliable since users may change the key-map at run-time.

Wouldn’t it be better to have handlers:

  • bpy.app.handlers.keyconfig_load_pre
  • bpy.app.handlers.keyconfig_load_post