Search code examples
pythonscriptingmaya

Maya - Listen to change in anim curve


I'm looking for a solution to create a script job that lets say print something whenever I make any changes in animation curves inside graph editor. Lets say I modify key by changing value or time or I modify its tangents.

In documentation, script job has an event like 'SelectionChanged' or 'AttributeChanged' so I can run any script when one of those events are true. But I have a problem when I want to listen to anim curve change in graph editor. Do you know any solution to do so without using Maya API. Is it doable in python?

All best, Piotr

Update:

Ok, I have a bit of troubles like always when it comes to details :).

(I hope I'm updating my question in right way but I need to provide a code because I still have an issue)

@Green Cell, I used your code but as a function inside, I want to set keys on other animcurve and because of that, after modyfing 'animCurve_A' I get few seconds lag and after this a "RuntimeError: maximum recursion deph exceeded" and "Warning: Python anim callback failed" (however it works when I use for example setAttr command). To be more specific this is simplified code where I want to show my issue.

import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as OpenMayaAnim

#create empty animation curve and make 2 keyframes
cmds.createNode('animCurveTL', n='animCurve_A') 
cmds.setKeyframe('animCurve_A', t=0, v=0)
cmds.setKeyframe('animCurve_A', t=10, v=10)

#create second empty animation curve
cmds.createNode('animCurveTL', n='animCurve_B') 

#create callback from animCurve_A to drive animCurve_B
def func(mobj_array, client_data):
    for i in range(mobj_array.length()): 
        if OpenMaya.MFnDependencyNode(mobj_array[i]).name() == 'animCurve_A':
            cmds.setKeyframe('animCurve_B', t=0, v=0)

cb = OpenMayaAnim.MAnimMessage.addAnimCurveEditedCallback(func)

Any clues what I'm doing wrong?... :(


Solution

  • The documentation has a similiar example so that you can use scriptJob's attributeChanged parameter:

    import maya.cmds as cmds
    
    def func():
        print "TEST"
    
    sj = cmds.scriptJob(attributeChange=['pSphere1.ty', func])
    

    This will fire func whenever pSphere1's translateY is changed. It will also fire as you edit its curve in the graph editor when moving a key, changing a key's value, and changing a tangent's angle.

    Edit:

    I know you mentioned 'without using Maya API', but there's a way to add a global callback that triggers when any modifications on anim curves are done, using a MAnimMessage. Here's an example:

    import maya.OpenMaya as OpenMaya
    import maya.OpenMayaAnim as OpenMayaAnim
    
    
    # Define a function that the callback will call.
    def func(mobj_array, client_data):
        for i in range(mobj_array.length()):  # Need to loop as it may pass multiple curves that are being edited at the same time.
            print OpenMaya.MFnDependencyNode(mobj_array[i]).name()  # Get the curve's name. At this point you can test to see if this is the curve you need to operate on.
    
    
    cb = OpenMayaAnim.MAnimMessage.addAnimCurveEditedCallback(func)  # Create callback.
    
    # OpenMayaAnim.MAnimMessage.removeCallback(cb)  # Run this to remove the callback.
    

    Inside the function you need to add an if check to see if it's your custom curve. This could be done by name, or just adding a custom attribute on it and checking if it exists. The curve doesn't need to be connected to any attribute and will still trigger on its own. This does trigger on any modification outside of the graph editor, so adding/removing/moving keys, changing values in the attribute editor; but that makes sense and should be expected anyways.