# Is it possible to create a modal operator with a modal keymap in Python?

Creating a modal operator is not an issue, but hard-coding the user tools, interactions, and other events is quite cumbersome and not flexible for the user.

Is there a way to check against modal keymap properties within a modal operator through Python?

For example, in my modal I let users select elements with one of the mouse buttons. This is fine for most users but it becomes problematic if a user changes their selection mouse defaults (as an example).

I know how to create a modal keymap, like so:

kc = bpy.context.window_manager.keyconfigs.addon

# Create the mode switch menu hotkey
km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
kmi = km.keymap_items.new('wm.call_menu', 'V', 'PRESS', ctrl=True, shift=True)
kmi.active = True

km = kc.keymaps.new('Addon Modal Keymap', space_type='EMPTY', region_type='WINDOW', modal=True)

kmi = km.keymap_items.new_modal('SELECTION', 'LEFTMOUSE', 'PRESS', any=True)
kmi = km.keymap_items.new_modal('REMOVE', 'X', 'PRESS')


But how do I check against these keymaps in the event system? Normally I would just hard-code it by checking for the event type, but this is not flexible.

I don't know how to use modal keymaps, but it is quite easy to write such an event system on your own.

Here is how I would do it now (I just wrote this the first time, so maybe I forgot something):

at first we need 2 classes to store the keymap information:

class EventCollection:
def __init__(self):
self.events = {}

def new_event(self, name, event_settings):
self.events[name] = event_settings

# assumes that only one event can happen at the same time
def get_matching_event_name(self, event):
names = self.get_matching_event_names(event)
if len(names) > 0:
return names[0]
return None

def get_matching_event_names(self, event):
return [name for name, description in self.events.items() if description.fits_event(event)]

class EventSettings:
def __init__(self, event_type, event_value, shift = False, ctrl = False, alt = False):
self.type = event_type
self.value = event_value
self.shift = shift
self.ctrl = ctrl
self.alt = alt

def fits_event(self, event):
return event.type == self.type and \
event.value == self.value and \
event.shift == self.shift and \
event.ctrl == self.ctrl and \
event.alt == self.alt


Here is how you can use these two classes in a modal operator

class TestKeymap(bpy.types.Operator):
bl_idname = "my.modal_operator"
bl_label = "label"
bl_description = ""
bl_options = {"REGISTER"}

@classmethod
def poll(cls, context):
return True

def modal(self, context, event):

name = self.events.get_matching_event_name(event)
if name == "my event":
print("hey")
if name == "select":
print("select")

if event.type == "ESC":
return {"CANCELLED"}

return {"RUNNING_MODAL"}

def invoke(self, context, event):

e1 = EventSettings("A", "PRESS", ctrl = True)

select_type = context.user_preferences.inputs.select_mouse
if select_type == "RIGHT": event_type = "RIGHTMOUSE"
else: event_type = "LEFTMOUSE"
e2 = EventSettings(event_type, "PRESS")

self.events = EventCollection()
self.events.new_event("my event", e1)
self.events.new_event("select", e2)

return {"RUNNING_MODAL"}


I think this is mostly self-explanatory, so I won't go over it in detail. Basicly you create an EventCollection when you invoke the operator. This makes sure that it is always up to date to latest changes in userprefs.

In this example I create two events (like KeyMapItems in normal keymaps). The one is very simple and the second one shows how to mimic the selection method. It checks wether you normally select with left or right and creates a corresponding EventSettings instance. You may rename these classes

In the modal-function you can always check if the current event matches one of the predefined ones.

This makes the system very flexible and easy to extend/change.

Is that a bit like what you wanted?