# How to structure modal operator correctly with macro like step by step execution?

I struggle with understanding the modal operator concept. Below is a chain of commands I would like to step by step to execute, while during translate the following steps should wait till translate is done. So a modal operator structure is needed here.

        bpy.ops.object.select_all(action='DESELECT')
bpy.ops.transform.translate('INVOKE_DEFAULT')
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.object.delete()


This is the template I found and understand. I select one object, execute the command which will then get the location and the modal takes over. But I failed to implement my commands into this structure. How can I make the modal aware of that I added the empty and jump into translate.

I understand the confirm and cancel being the states where I can execute the rest after transform or even undo the initial empty generation if need needed.

import bpy

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

def __init__(self):
print("Start")

def __del__(self):
print("End")

def execute(self, context):
# bpy.ops.object.empty_new()
context.object.location.x = self.value / 1000.0
return {'FINISHED'}

def modal(self, context, event):
if event.type == 'MOUSEMOVE':  # Apply
self.value = event.mouse_x
self.execute(context)
elif event.type == 'LEFTMOUSE':  # Confirm
bpy.ops.view3d.snap_cursor_to_selected()
x bpy.ops.object.delete()
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:  # Cancel
bpy.ops.object.delete(use_global=False)
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
self.init_loc_x = context.object.location.x
self.value = event.mouse_x
self.execute(context)

return {'RUNNING_MODAL'}

class ObjectData(bpy.types.Panel):
bl_label = "Object + Mesh Data"
bl_idname = "ObjectData"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = "Modeling Toolbox"

def draw(self, context):
layout = self.layout

obj = context.object

row = layout.row()
self.layout.operator("object.modal_operator")

def register(): bpy.utils.register_module(__name__)
def unregister(): bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
register()

• There is a new Approach which might help you out: Blender for Architecture and Engineering – Samoth Jan 14 '16 at 10:09
• related (snap target) blender.stackexchange.com/questions/1956/… – zeffii Jan 14 '16 at 10:26
• Blender has no ability to move and snap the 3D cursor as an object. So the idea is to make an empty move it, allow the user while moving to snap to any object geometry vertext, and then releasing the mouse set the 3D cursor to the empty position and delete the empty. – Claas Kuhnen Jan 14 '16 at 13:58
• Yes of course that is the only reason I try to figure this out. When you work with complex meshes and booleans you do not want to get into edit mode. The maker of Enhanced 3D Cursor stated that Blender would slow down with my models and his add-on. PivotPro uses macros and I was suggested to look into this as well, but fail to understand how to use it. blender.stackexchange.com/questions/44971/… – Claas Kuhnen Jan 14 '16 at 15:52
• OK. now I get it. It's sometimes easier to provide an answer when the final goal is known and explicitly stated too (not just inferred from the variable names). This changes the question imo. – zeffii Jan 14 '16 at 16:12

Your operator is not a modal operator and you are calling a modal operator inside it , SnapOriginObject will exit and leave transform.translate('INVOKE_DEFAULT') running behind ( it won't wait as you expect ) , you have to create a modal operator ( see the templates ) where you catch the mouse events ( movements, clicks, position ) to move the empty and delete it when you are done.

• can a modal operator be inside the "def execute(self, context):" ? – Claas Kuhnen Jan 14 '16 at 15:53

Here is the code that does what I asked in my question. Thank you Zeffi and Chebhou

import bpy
from bpy_extras import view3d_utils

class ModalOperator(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
count = 0
def __init__(self):
print("Start")

def __del__(self):
print("End")

# get the context arguments

def modal(self, context, event):
self.count += 1

if self.count == 1:
bpy.ops.transform.translate('INVOKE_DEFAULT')

if event.type == 'LEFTMOUSE': # Confirm
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.object.delete()
return {'FINISHED'}

elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel
bpy.ops.object.delete()
return {'CANCELLED'}
return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':

self.scene = context.scene
self.region = context.region
self.rv3d = context.region_data
self.mouse_co = event.mouse_region_x, event.mouse_region_y
self.depth = view3d_utils.region_2d_to_vector_3d(self.region, self.rv3d, self.mouse_co)
self.emp_co = view3d_utils.region_2d_to_location_3d(self.region, self.rv3d, self.mouse_co, self.depth)
bpy.ops.object.select_all(action='DESELECT')