5
$\begingroup$

Is it possible to get if Ctrl key is pressed when I click on my python button?

I would like make different behaviors for my button operator.

Example:
LMB - Select some objects
CtrlLMB - Deselect some objects

I don't use a modal operator / the modal() method. I've been making a simple script.

$\endgroup$
11
$\begingroup$

Yes, it is possible if you use an invoke() method in your operator instead of execute(), because there's a 3rd parameter given to invoke - an event object.

That object has attributes about modifier key states among a few other pieces of input info.

Here's an example script, that adds a button to the 3D View. You can hold e.g. CtrlShift down and click on it to let the operator report Ctrl+Alt+Click in the info area:

import bpy

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

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def invoke(self, context, event):
        ev = []
        if event.ctrl:
            ev.append("Ctrl")
        if event.shift:
            ev.append("Shift")
        if event.alt:
            ev.append("Alt")
        if event.oskey:
            ev.append("OS")
        ev.append("Click")

        self.report({'INFO'}, "+".join(ev))
        return {'FINISHED'}


def draw_func(self, context):
    self.layout.operator(SimpleOperator.bl_idname)


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_HT_header.prepend(draw_func)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_HT_header.remove(draw_func)


if __name__ == "__main__":
    register()

Note that there can also be an execute() method in the operator, but it won't be called unless the execution context is not INVOKE_*, but EXEC_*. The default behavior of the Python Console is, for instance, to execute an operator, not to invoke it.

If there's no execute() and you run bpy.ops.object.simple_operator() in the console, an error will be reported in the System Console and the operator abort with {'PASS_THROUGH'}. You can explicitly invoke it however: bpy.ops.object.simple_operator('INVOKE_DEFAULT')

$\endgroup$
  • $\begingroup$ As always you save me. You are my hero! $\endgroup$ – mifth Jan 30 '15 at 10:26
  • $\begingroup$ You're welcome. On a side note: It's not possible to let the button change (label or icon) based on the held down modifier keys. Actually it is, but only if you monitor their state continuously - which would require a modal operator running in the background all the time. Plus, you would need to tag_redraw() the region. See my prototype over here: blenderartists.org/forum/… $\endgroup$ – CoDEmanX Jan 30 '15 at 10:34
  • $\begingroup$ great answer, you made a touched on few points here that I tripped over for a while before I figured it out. $\endgroup$ – David Jan 30 '15 at 18:46
  • $\begingroup$ @CoDEmanX I've been looking for a solution to a recent problem I encountered. For my problem, I need to click on a node in the node editor to trigger LBM clicking so that the values of the 'Image Node` block get updated. I think a version of your solution will provide me what I want but I do not know how I can adapt your answer. Could you take a look at my question here and let me know if you think I am doing something wrong? $\endgroup$ – Amir Mar 10 '18 at 19:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.