3

I have a custom panel in which I have buttons that can use modifier keys, IE Clicking, Ctrl+Clicking, Alt+Clicking etc all do slightly different things and it works as expected. I want to hotkey the UI button but dont know how to differentiate if its called via the UI (and take modifiers into account) or via a hotkey (and not take the modifiers into account).

class dummyButton(bpy.types.Operator):
bl_idname = "view3d.fd_dummybutton"
bl_label = "Dummy Button"
bl_options = {'REGISTER', 'UNDO'}

modeEnum : bpy.props.EnumProperty( items = { ("1", "Stuff", ""), ("2", "Other stuff", ""), ("3", "Even more stuff", ""), ("4", "Final stuff", "") }, name = "Mode", default = "1" )

def invoke(self, context, event):

if event.ctrl:
    self.modeEnum = "2"
elif event.shift:
    self.modeEnum = "3"
elif event.alt:
    self.modeEnum = "4"

return self.execute(context)

def execute(self, context):

if self.modeEnum == "1":
    print("Do stuff")
elif self.modeEnum == "2":
    print("Do other stuff")
else:
    print("Stuff")

return {'FINISHED'}

I'd prefer to use an enum since then I can choose the function in the hotkey menuEnum dropdown to pick what the hotkey does I want to be able to use Ctrl+9 as a hotkey without it forcing the modeEnum to be set to "2"

Am I going about this in the wrong way?

pyCod3R
  • 1,926
  • 4
  • 15
CybranM
  • 413
  • 2
  • 13

2 Answers2

3

You can catch the left-click event when the button is pressed to differentiate between UI and hotkey call, using event.type == 'LEFTMOUSE': https://docs.blender.org/api/current/bpy.types.Event.html#bpy.types.Event.type

import bpy

https://blender.stackexchange.com/a/24002/

class OBJECT_OT_CustomOp(bpy.types.Operator): """Tooltip""" bl_idname = "object.simple_operator" bl_label = "Simple Object Operator"

def invoke(self, context, event):
    #is_property_set
    ev = []
    if event.ctrl:
        ev.append("Ctrl")
    if event.shift:
        ev.append("Shift")
    if event.type == 'LEFTMOUSE':
        ev.append("Click")
    self.report({'INFO'}, "+".join(ev))
    return {'FINISHED'}

class OBJECT_PT_CustomPanel(bpy.types.Panel): bl_label = "Panel" bl_idname = "OBJECT_PT_custom_panel" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Tools" bl_context = "objectmode"

def draw(self, context):
    layout = self.layout
    layout.operator(OBJECT_OT_CustomOp.bl_idname)
    layout.separator()

addon_keymaps = []

def register(): bpy.utils.register_class(OBJECT_OT_CustomOp) bpy.utils.register_class(OBJECT_PT_CustomPanel)

# https://blender.stackexchange.com/a/196518/
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
    km = wm.keyconfigs.addon.keymaps.new(name='3D View', space_type='VIEW_3D')
    kmi = km.keymap_items.new(OBJECT_OT_CustomOp.bl_idname, type='Q', value='PRESS', ctrl=True)
    addon_keymaps.append((km, kmi))


def unregister(): bpy.utils.unregister_class(OBJECT_PT_CustomPanel) bpy.utils.unregister_class(OBJECT_OT_CustomOp)

for km, kmi in addon_keymaps:
    km.keymap_items.remove(kmi)
addon_keymaps.clear()


if name == "main": register()

# test call
#bpy.ops.object.simple_operator()

Python: Ctrl+Click for buttons (capture invocation event)

Create keyboard shortcut for an operator using python?

pyCod3R
  • 1,926
  • 4
  • 15
  • 2
    This solution is both clean and simple, seems so obvious now in hindsight. Thanks for taking the time to help out! – CybranM Jan 12 '22 at 16:21
2

You can also add a (hidden) property, IE Bool or Enum to the operator (as you already do) which will be set when the button is pressed. https://docs.blender.org/api/current/bpy.types.KeyMapItem.html#bpy.types.KeyMapItem.properties

import bpy

https://blender.stackexchange.com/a/24002/

class OBJECT_OT_CustomOp(bpy.types.Operator): """Tooltip""" bl_idname = "object.simple_operator" bl_label = "Simple Object Operator" bl_options = {'REGISTER', 'UNDO'}

# https://blender.stackexchange.com/a/21108/
hotkey_call: bpy.props.BoolProperty(options={'HIDDEN', 'SKIP_SAVE'})

def invoke(self, context, event):
    ev = []
    if event.ctrl:
        ev.append("Ctrl")
    if event.shift:
        ev.append("Shift")
    if self.hotkey_call:
        ev = ["Hotkey Call"]
    self.report({'INFO'}, "+".join(ev))
    return {'FINISHED'}

class OBJECT_PT_CustomPanel(bpy.types.Panel): bl_label = "Panel" bl_idname = "OBJECT_PT_custom_panel" bl_space_type = "VIEW_3D"
bl_region_type = "UI" bl_category = "Tools" bl_context = "objectmode"

def draw(self, context):
    layout = self.layout
    layout.operator(OBJECT_OT_CustomOp.bl_idname)
    layout.separator()

addon_keymaps = []

def register(): bpy.utils.register_class(OBJECT_OT_CustomOp) bpy.utils.register_class(OBJECT_PT_CustomPanel)

# https://blender.stackexchange.com/a/196518/
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
    km = wm.keyconfigs.addon.keymaps.new(name='3D View', space_type='VIEW_3D')
    kmi = km.keymap_items.new(OBJECT_OT_CustomOp.bl_idname, type='Q', value='PRESS', ctrl=True)
    kmi.properties.hotkey_call = True
    addon_keymaps.append((km, kmi))


def unregister(): bpy.utils.unregister_class(OBJECT_PT_CustomPanel) bpy.utils.unregister_class(OBJECT_OT_CustomOp) for km, kmi in addon_keymaps: km.keymap_items.remove(kmi) addon_keymaps.clear()

if name == "main": register()

# test call
#bpy.ops.object.simple_operator()

Option flags for custom properties (bpy.props)

pyCod3R
  • 1,926
  • 4
  • 15