# How to define double shortcut like GG

I would like to define a shortcut that allows to call a custom translate function while translating. Like G for Vertex move and GG for vertex slide, my shortcut should allow G for Blender's translate and GG would call my custom translate function.

I tried with a modal operator calling blender's translate function and an elif event in ('G'): my_translate but it hitting G while translating just doesn't do anything (no error in console output).

GG is not actually a keybinding, G starts the grab operator and that reacts on certain keytrokes while it's running modally. It uses a modal map (keymapping) for this ability.

You can edit modal maps in User preferences > Input, but not call arbitrary operators (in fact, only sub-operations defined by the operator in C, like Select or Deselect in Border Select op).

For python-scripted operators, there's an event argument passed to modal() and you can check for keystrokes (event.type, event.value etc.) and react in whatever way.

Detect G keystroke in a modal operator:

import bpy
from bpy.props import IntProperty, FloatProperty

class ModalOperator(bpy.types.Operator):
"""Move an object with the mouse, example"""
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"

first_mouse_x = IntProperty()
first_value = FloatProperty()

def modal(self, context, event):
if event.type == 'MOUSEMOVE':
delta = self.first_mouse_x - event.mouse_x
context.object.location.x = self.first_value + delta * 0.01

elif event.type == 'G':
self.report({'INFO'}, "G")
context.object.location.x = self.first_value
return {'CANCELLED'}

elif event.type == 'LEFTMOUSE':
return {'FINISHED'}

elif event.type in {'RIGHTMOUSE', 'ESC'}:
context.object.location.x = self.first_value
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.object:
self.first_mouse_x = event.mouse_x
self.first_value = context.object.location.x

return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}

def register():
bpy.utils.register_class(ModalOperator)

def unregister():
bpy.utils.unregister_class(ModalOperator)

if __name__ == "__main__":
register()


Transform Modal Map - what (sub-)operations a possible during the modally running operators:

• Hi CoDEmanX, I tried with the event.type in ('G') as noted in the question, but it doesn't work, maybe I don't do it well. Could you provide a working piece of code ? – matali Oct 31 '13 at 18:00
• I added a quick example above, based on the Operator Modal template. – CodeManX Oct 31 '13 at 23:07
• Thanks CoDEmanX, but as mentioned above, I would like the first 'G' press to use Blender's translate operator and only the second 'G' press to use a custom translate like the one you wrote. Is it possible ? Or can I only switch between 2 custom translate functions ? – matali Nov 1 '13 at 8:21
• Ok, I guess I wasn't clear enoug: it's not possible. Transform operators can only run a hardcoded set of (sub-)operations. See screenshot above. – CodeManX Nov 1 '13 at 9:55
• I found a way, never say it's impossible ;) see my answer for details. By the way, I see it was a good idea to point you to Blender's Stackexchange website, you already earned 1000 points :D – matali Nov 1 '13 at 19:30

After some extensive search it is possible.

Example script:

import bpy
from bpy.props import IntProperty, FloatProperty

class ModalOperator(bpy.types.Operator):
"""Allow GG operator"""
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"

count=0

def modal(self, context, event):
self.count +=1
if self.count ==1:
bpy.ops.transform.translate('INVOKE_DEFAULT')

elif event.type == 'G':
bpy.ops.transform.rotate('INVOKE_DEFAULT')
return {'CANCELLED'}

elif event.type == 'LEFTMOUSE':
return {'FINISHED'}

elif event.type in {'RIGHTMOUSE', 'ESC'}:
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.object:

return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}

def register():
bpy.utils.register_class(ModalOperator)

def unregister():
bpy.utils.unregister_class(ModalOperator)

if __name__ == "__main__":
register()


For the key you chose (here G) go to the modal part of the key-binding (here in transform.translate) and assign "cancel" to the G key. Activate the Add-on and add a hotkey to your modal operator (for the above given script : object.modal_operator for G). It will cancel blender's translate and the add-on will launch your custom operator just after :)

The given script calls blender's official rotation, but you can replace it by a custom written operator.

Example for this script :

• Did you see my comment above? – CodeManX Nov 4 '13 at 23:12
• Sorry, I had a lot to do last days. I didn't test it on 2.67 but it works in 2.69 for sure. I hit G once, it starts blender's grab, G a second time, it cancels Blender's Grab and switch to my operator. I can't ensure I'll have time to test on 2.67 as I'm really busy ATM. – matali Nov 7 '13 at 19:45
• Just tried official 2.69, and it's the same. It won't invoke the rotation :( – CodeManX Nov 7 '13 at 20:48
• I modify my answer to make it clearer – matali Nov 8 '13 at 15:50
• I tested it on both Linux and Windows, it works for me with official 2.69. Hope the solution is now clearer :) – matali Nov 8 '13 at 16:03