2
$\begingroup$

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.object.empty_add()       
        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)

        context.window_manager.modal_handler_add(self)
        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()
$\endgroup$
  • $\begingroup$ There is a new Approach which might help you out: Blender for Architecture and Engineering $\endgroup$ – Samoth Jan 14 '16 at 10:09
  • 1
    $\begingroup$ related (snap target) blender.stackexchange.com/questions/1956/… $\endgroup$ – zeffii Jan 14 '16 at 10:26
  • $\begingroup$ 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. $\endgroup$ – Claas Kuhnen Jan 14 '16 at 13:58
  • $\begingroup$ 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/… $\endgroup$ – Claas Kuhnen Jan 14 '16 at 15:52
  • $\begingroup$ 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. $\endgroup$ – zeffii Jan 14 '16 at 16:12
2
$\begingroup$

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.

| improve this answer | |
$\endgroup$
  • $\begingroup$ can a modal operator be inside the "def execute(self, context):" ? $\endgroup$ – Claas Kuhnen Jan 14 '16 at 15:53
2
$\begingroup$

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')
                bpy.ops.object.empty_add()
                context.object.location = self.emp_co

                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
            else:
                self.report({'WARNING'}, "Active space must be a View3d")  
                return {'CANCELLED'}
| improve this answer | |
$\endgroup$

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.