1
$\begingroup$

You can assign different hotkeys to one operator.

But when it is executed, how can it be determined with which hotkey it was called?

import bpy

class MyOperator(bpy.types.Operator):
    bl_idname = "node.my_op"
    bl_label = "My Operator"
    myProp:  bpy.props.StringProperty(name="Aaa", default="1")
    def execute(self, context):
        kmUser = bpy.context.window_manager.keyconfigs.user.keymaps['Node Editor']
        #next: ???
        print("Hi, you called this operator using `type=idk`, `shift=idk` and `alt=idk`")
        #print(kmUser.keymap_items['node.my_op'], self.myProp)
        return {'FINISHED'}

def register():
    bpy.utils.register_class(MyOperator)
    km = bpy.context.window_manager.keyconfigs.addon.keymaps.new(name="Node Editor", space_type='NODE_EDITOR')
    km.keymap_items.new(idname=MyOperator.bl_idname, type='H', value='PRESS', shift=True)
    kmi = km.keymap_items.new(idname=MyOperator.bl_idname, type='D', value='PRESS', alt=True)
    kmi.properties.myProp = "2"

if __name__=="__main__":
    register()
$\endgroup$

2 Answers 2

1
$\begingroup$

bpy.context.window_manager.keyconfigs.user.keymaps['Node Editor'].keymap_items.keys() are not Unique. So kmUser.keymap_items['node.my_op'] returns the first item with that name. However, each item has unique id but different for keyconfigs.user.keymaps and keyconfigs.addon.keymaps.

I'd suggest find the index of first and then incrementing the index to get the other:

kmUser = bpy.context.window_manager.keyconfigs.user.keymaps['Node Editor'].keymap_items
i = kmUser.find(MyOperator.bl_idname)
altD = kmUser[i]
shftH = kmUser[i+1]

Note: if you don't set kmi.properties.myProp for shift + H keymap, value of myProp will be unpredictable.

Full code:

import bpy

class MyOperator(bpy.types.Operator):
    bl_idname = "node.my_op"
    bl_label = "My Operator"
    km_index:  bpy.props.IntProperty(name="Index", default=-1)
    def execute(self, context):
        kmUser = bpy.context.window_manager.keyconfigs.user.keymaps['Node Editor'].keymap_items
        i = kmUser.find(MyOperator.bl_idname)
        kmi = kmUser[i + self.km_index]

        print(kmi.to_string())
        # print(f"Hi, you called this operator using `type={kmi.type}`, `shift={kmi.shift}` and `alt={kmi.alt}`")
        
        return {'FINISHED'}

def register():
    bpy.utils.register_class(MyOperator)
    keymaps = (
        {'type': 'H', 'value': 'PRESS', 'shift': True},
        {'type': 'D', 'value': 'PRESS', 'alt': True},
    )
    km = bpy.context.window_manager.keyconfigs.addon.keymaps.new(name="Node Editor", space_type='NODE_EDITOR')

    for i, v in enumerate(keymaps):
        kmi = km.keymap_items.new(idname=MyOperator.bl_idname, **v)
        kmi.properties.km_index = len(keymaps) - i - 1

if __name__=="__main__":
    register()
$\endgroup$
0
1
$\begingroup$

This looks like a XY problem to me. I think an operator doesn't need to be able to know how it was called, the only things it needs to know is the current context it was called with and its properties. IMO it is a design problem to tie logic with specific inputs. What if you want to use your addon with a VR headset and you have another set of inputs ?

Anyways to answer your question I suggest you add eg a string or enum property to the operator and fill that in the keymap entry. Something like

class MyOperator(bpy.types.Operator):
    bl_idname = "node.my_op"
    bl_label = "My Operator"
    i_was_called_with: bpy.props.EnumProperty(
        items=(
            ("SHIFT",)*3,
            ("ALT",)*3,
            ("CTRL",)*3,
        ),
        options={"ENUM_FLAG"},
    )
    def execute(self, context):
        print(self.i_was_called_with)  # outputs is a set like {"SHIFT", "ALT"}
        ...
        return {"FINISHED"}

Link to EnumProperty docs

However the way I would do it :

class MyOperator(bpy.types.Operator):
    bl_idname = "node.my_op"
    bl_label = "My Operator"
    execute_mode: bpy.props.EnumProperty(
        items=(
            ("ISOLATE",)*3,
            ("HIDE",)*3,
            ("DELETE",)*3,
        ),  # Random ideas
    )
    def execute(self, context):
        print(self.execute_mode)
        ...
        return {"FINISHED"}
$\endgroup$
1
  • $\begingroup$ @Gorious, you said: "This looks like a XY problem to me." Well, depends on the use case. For example I am currently trying to get the actual key mapping used to call the considered operator to display it in its tooltip (using the custom "description" classmethod). $\endgroup$
    – Werwack
    Commented Feb 7 at 17:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .