Alternate between user input and automation in one-step script I would like my script to do the following :


Save selected object's name and add an object (automation)
Let user move the added object where he wants (user input, stopped with leftclick)
Move the 3D cursor to where the object was moved, delete that object and reselect the original object (automation)
Let user move the original object where he wants (user input, stopped with leftclick) and terminate the script.


I tried to modify the template "simple modal operator", but without success. I looked the doc, googled, watched other scripts which are modal... nothing. The best I did get was to reach step 3, but then the script seems to ignore the line where I change the active object. The same line works in the python console...

I don't specifically want the solution for my case, but at least if someone could explain how to properly alternate many times between user input and automated steps, it would be nice.
Macro Operators are a way to achieve this (see script below, this does pretty much what you describe). They allow you to concatenate modal and non-modal operators.

Note that adding an object and deleting it right away repeatedly can lead to a lot of 0-user objects accumulating in bpy.data.

import bpy
from bpy.types import Operator, Macro
from bpy.props import *

class TranslateCursorMacro(Macro):
    """Overall macro for combining move and cursor operators"""
    bl_idname = "object.translate_cursor_macro"
    bl_label = "Translate Cursor"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        if not context.active_object:
            return False
        return True


def _find_prev_object(ptr):
    for ob in bpy.data.objects:
        if str(ob.as_pointer()) == ptr:
            return ob

class RememberObject(Operator):
    """Store active object"""
    bl_idname = "object.remember_object"
    bl_label = "Remember Object"

    def execute(self, context):
        # XXX This is not super nice, but we don't have real
        # properties shared between macro component operators.
        # In an addon this might be stored as a module-level
        # python variable. Store as string because integer is
        # only 32 bit and may truncate 64 bit pointer.
        context.scene["_prev_object"] = str(context.active_object.as_pointer())
        return {'FINISHED'}

class DeleteNoQuestionsObject(Operator):
    """Delete object without asking"""
    bl_idname = "object.delete_no_questions"
    bl_label = "Delete Object"

    def execute(self, context):
        # XXX The usual bpy.ops operator opens a popup
        # when invoke. Use this wrapper to avoid that.
        bpy.ops.object.delete('EXEC_DEFAULT')
        return {'FINISHED'}

class SelectPreviousObject(Operator):
    """Select previous object"""
    bl_idname = "object.select_previous_object"
    bl_label = "Select Previous Object"

    def execute(self, context):
        ptr = context.scene.get("_prev_object", None)
        if ptr is None:
            return {'CANCELLED'}
        del context.scene["_prev_object"] # cleanup
        ob = _find_prev_object(ptr)
        if not ob:
            self.report({"WARNING"}, "Previous object not found")
            return {'CANCELLED'}

        for tob in bpy.data.objects:
            tob.select = (tob == ob)
        context.scene.objects.active = ob
        return {'FINISHED'}


bpy.utils.register_class(TranslateCursorMacro)
bpy.utils.register_class(RememberObject)
bpy.utils.register_class(DeleteNoQuestionsObject)
bpy.utils.register_class(SelectPreviousObject)

TranslateCursorMacro.define("OBJECT_OT_remember_object")
TranslateCursorMacro.define("MESH_OT_primitive_uv_sphere_add")
TranslateCursorMacro.define("TRANSFORM_OT_translate")
TranslateCursorMacro.define("VIEW3D_OT_snap_cursor_to_active")
TranslateCursorMacro.define("OBJECT_OT_delete_no_questions")
TranslateCursorMacro.define("OBJECT_OT_select_previous_object")