1
$\begingroup$

enter image description here

bpy.types.SpaceDopeSheetEditor.auto_snap is an enum of different modes describing how transformed keyframes should behave. However, it does not seem to auto-apply the behavior when one makes a custom operator.

# Try running this with any auto-snap mode set in GUI.
# It always behaves as if 'No Auto-Snap' was selected
import bpy
from bpy.props import IntProperty, FloatProperty


class AutoSnappingDoesNotHappen(bpy.types.Operator):
    """wheres my auto auto snap?"""
    bl_idname = "scene.auto_snap_modal_test"
    bl_label = "Auto Snap Modal Test"

    total_offset: FloatProperty(default=0)

    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':
            delta = (event.mouse_x - event.mouse_prev_x) * 0.01
            self.total_offset += delta
                    
            action = context.active_object.animation_data.action
            for fcurve in bpy.data.actions[action.name].fcurves:
                fcurve.update()
                for keyframe in fcurve.keyframe_points:
                    if keyframe.select_control_point:
                        keyframe.co_ui.x += delta
                fcurve.update()

        elif event.type == 'LEFTMOUSE':
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            action = context.active_object.animation_data.action
            for fcurve in bpy.data.actions[action.name].fcurves:
                fcurve.update()
                for keyframe in fcurve.keyframe_points:
                    if keyframe.select_control_point:
                        keyframe.co_ui.x -= self.total_offset
                fcurve.update()
            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(AutoSnappingDoesNotHappen)


def unregister():
    bpy.utils.unregister_class(AutoSnappingDoesNotHappen)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.scene.auto_snap_modal_test('INVOKE_DEFAULT')

Do I have to reimplement each auto-snapping mode's logic for my operator, or is there some function I can call to apply the selected auto_snap mode's behavior?

(I see bpy.ops.action.snap() exists, but the options don't seem to match up perfectly with auto_snap's.)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.