setKeyframe on a locked animation layer?

setKeyframe on a locked animation layer?

RFlannery1
Collaborator Collaborator
1,880 Views
3 Replies
Message 1 of 4

setKeyframe on a locked animation layer?

RFlannery1
Collaborator
Collaborator

I have a script that sets keyframes on an object.  If the base animation layer is locked, the keyframe does not get set.  I tried adding code to unlock the base animation layer, set the keyframe, and then relock it.  This does not work.  However, if I manually unlock the layer and THEN run my script, it works.

 

Reproducing the problem:

Create a new scene.  Add an object (cube, sphere, whatever) to the scene.  Set a couple of keys on the object.  Now create a new animation layer with nothing in it.  Lock the base animation layer.

Select the object, and run the code below.  The keyframe does not get set. 

 

Sample code:

 

def setKeyOnSelected(frameNumber, value):
    selObjs = cmds.ls(sl=True, o=True)
    
    currTime = cmds.currentTime(q=True)
    cmds.currentTime(frameNumber)
    
    baseLayer = cmds.animLayer(q=True, root=True)
    if baseLayer:
        baseLayerLockState = cmds.animLayer(baseLayer, q=True, lock=True)
        cmds.animLayer(baseLayer, edit=True, lock=False)
    
    for obj in selObjs:
        cmds.move(value, 0, 0, obj)
        res = cmds.setKeyframe(obj)
        print res
    
    cmds.currentTime(currTime)
    if baseLayer:
        cmds.animLayer(baseLayer, edit=True, lock=baseLayerLockState)

 

setKeyOnSelected(10, 5)

Is this a bug, or do I have some fundamental misunderstanding about animation layers and setting keyframes?

Accepted solutions (1)
1,881 Views
3 Replies
Replies (3)
Message 2 of 4

RFlannery1
Collaborator
Collaborator

I think I figured something out.  Looking through layerEditor.mel, it seems that when you unlock an animation layer through the GUI, it not only unlocks the layer but also unlocks all animation curves associated with that layer.  So I wrote a function to emulate that behavior, but without all the GUI-specific behavior.

def setAnimLayerLockState(animLayer, lockState):
    allAnimLayers = cmds.ls(type='animLayer')
    if animLayer not in allAnimLayers:
        print '"{0}" is not a valid animation layer'.format(animLayer)
        return
    
    # Lock or unlock the animation layer
    cmds.animLayer(animLayer, edit=True, lock=lockState)
    
    # Now lock or unlock all animation curves in the specified layer
    baseLayer = cmds.animLayer(q=True, root=True)
    if animLayer == baseLayer:
        allAnimCurves = set(cmds.ls(type='animCurve'))
        for animLayerName in allAnimLayers:
            if animLayerName == baseLayer:
                continue
            layerCurves = cmds.animLayer(animLayerName, q=True, animCurves=True)
            if layerCurves:
                allAnimCurves -= set(layerCurves)            
        for animCurve in allAnimCurves:
            cmds.setAttr(animCurve+'.ktv', lock=lockState)
    else:
        layerAnimCurves = cmds.animLayer(animLayer, q=True, animCurves=True)
        for animCurve in layerAnimCurves:
            cmds.setAttr(animCurve+'.ktv', lock=lockState)

 Then, I just use this function where I previously used the line:

cmds.animLayer(baseLayer, edit=True, lock=baseLayerLockState)

 This it not 100% tested, but it seems to work pretty well for simple cases.

Message 3 of 4

RFlannery1
Collaborator
Collaborator
Accepted solution

Slight modification to avoid "'NoneType' object is not iterable" error.

def setAnimLayerLockState(animLayer, lockState):
    allAnimLayers = cmds.ls(type='animLayer')
    if animLayer not in allAnimLayers:
        print '"{0}" is not a valid animation layer'.format(animLayer)
        return
    
    # Lock or unlock the animation layer
    cmds.animLayer(animLayer, edit=True, lock=lockState)
    
    # Now lock or unlock all animation curves in the specified layer
    baseLayer = cmds.animLayer(q=True, root=True)
    if animLayer == baseLayer:
        allAnimCurves = set(cmds.ls(type='animCurve'))
        for animLayerName in allAnimLayers:
            if animLayerName == baseLayer:
                continue
            layerCurves = cmds.animLayer(animLayerName, q=True, animCurves=True) or []
            allAnimCurves -= set(layerCurves)            
        for animCurve in allAnimCurves:
            cmds.setAttr(animCurve+'.ktv', lock=lockState)
    else:
        layerAnimCurves = cmds.animLayer(animLayer, q=True, animCurves=True) or []
        for animCurve in layerAnimCurves:
            cmds.setAttr(animCurve+'.ktv', lock=lockState)

 

Message 4 of 4

victor.maso
Explorer
Explorer

Hi,

 

I've find that this issue still happens in both Maya 2020.4 and Maya 2022.2. Just wanted to know if this bug will be fixed at some point as it makes working with animation layers quite cumbersome. Just to be clear, the issue only happens when trying to set a keyframe on a previously locked base layer that has been unlocked by code. With other layers this does not seem to happen.

 

I've also wanted to report on some findings in case it helps someone else with the same problem or it helps Autodesk solve the issue:

 

  • Using the MEL equivalent command does not solve the issue:

 

 

mel.eval('animLayer -edit -lock 0 layerName')​​

 

 

 

  • Using the animLayer parameters forceUIRefresh(uir) or forceUIRebuild(fur) after unlocking the layer and before setting the keyframe does not work either.

 

 

cmds.animLayer('BaseAnimation', e=True, lock=False)
cmds.animLayer(uir=True)
cmds.animLayer(fur=True)
cmds.setKeyframe('pCube1.tx', t=10, v=5.0, al='BaseAnimation')​​​​

 

 

 

  • Calling setKeyframe two times in a row does set the keyframe, not an ideal solution though. Also, If you execute the animLayer command and then the setKeyframe it works just fine. They need to be executed separately in the script editor one after the other, if executed together it does not work.

 

 

cmds.animLayer('BaseAnimation', e=True, lock=False)
# Calling setKeyframe two times in a row does set the keyframe
cmds.setKeyframe('pCube1.tx', t=10, v=5.0, al='BaseAnimation')
cmds.setKeyframe('pCube1.tx', t=10, v=5.0, al='BaseAnimation')​​

 

 

 

  • By echoing all commands in the Script Editor you can see all the MEL code that's being called under the hood when editing the lock state of an animation layer. Using this MEL call as a way to edit the lock state of a layer solves the issue.

 

 

import maya.mel as mel

anim_layer = 'BaseAnimation'
lock_state = False

mel.eval('''animLayerLockCallBack "{0}" "{1}";
             animLayer -edit -lock {1} {0};
             updateEditorFeedbackAnimLayers("AnimLayerTab");
             onAnimLayersBaseLockChanged("AnimLayerTab");
             doUpdateTangentFeedback;'''
             .format(anim_layer, int(lock_state)))​

cmds.setKeyframe('pCube1.tx', t=10, v=5.0, al='BaseAnimation')

 

 

 

@RFlannery1 solution also works fine for me in Maya 2022.4 and 2022.2. Thanks a lot for posting the code ! 🙂

0 Likes