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$
9
$\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$
  • $\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 '13 at 18:00
  • $\begingroup$ I added a quick example above, based on the Operator Modal template. $\endgroup$ – CoDEmanX Oct 31 '13 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 '13 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 '13 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 '13 at 19:30
6
$\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$
  • $\begingroup$ Did you see my comment above? $\endgroup$ – CoDEmanX Nov 4 '13 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 '13 at 19:45
  • $\begingroup$ Just tried official 2.69, and it's the same. It won't invoke the rotation :( $\endgroup$ – CoDEmanX Nov 7 '13 at 20:48
  • $\begingroup$ I modify my answer to make it clearer $\endgroup$ – matali Nov 8 '13 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 '13 at 16:03

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.