11
$\begingroup$

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).

$\endgroup$

2 Answers 2

10
$\begingroup$

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

            context.window_manager.modal_handler_add(self)
            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:

Blender User Preferences Input Transform Modal Map

$\endgroup$
6
  • $\begingroup$ 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 ? $\endgroup$
    – matali
    Oct 31, 2013 at 18:00
  • $\begingroup$ I added a quick example above, based on the Operator Modal template. $\endgroup$
    – CodeManX
    Oct 31, 2013 at 23:07
  • $\begingroup$ 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 ? $\endgroup$
    – matali
    Nov 1, 2013 at 8:21
  • $\begingroup$ 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. $\endgroup$
    – CodeManX
    Nov 1, 2013 at 9:55
  • $\begingroup$ 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 $\endgroup$
    – matali
    Nov 1, 2013 at 19:30
7
$\begingroup$

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:

            context.window_manager.modal_handler_add(self)
            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 : key config GG in transform.translate mode

$\endgroup$
7
  • $\begingroup$ Did you see my comment above? $\endgroup$
    – CodeManX
    Nov 4, 2013 at 23:12
  • $\begingroup$ 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. $\endgroup$
    – matali
    Nov 7, 2013 at 19:45
  • $\begingroup$ Just tried official 2.69, and it's the same. It won't invoke the rotation :( $\endgroup$
    – CodeManX
    Nov 7, 2013 at 20:48
  • $\begingroup$ I modify my answer to make it clearer $\endgroup$
    – matali
    Nov 8, 2013 at 15:50
  • $\begingroup$ I tested it on both Linux and Windows, it works for me with official 2.69. Hope the solution is now clearer :) $\endgroup$
    – matali
    Nov 8, 2013 at 16:03

You must log in to answer this question.

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