2
$\begingroup$

I'm trying to detect fcurves that are "errors" (the ones with the red underlines):

fcurves with and without errors

I've tried code like this:

import bpy

valid = 0
invalid = 0

for action in bpy.data.actions:
    for f in action.fcurves:
        if f.is_valid:
            valid += 1
        else:
            invalid += 1

print(valid)
print(invalid)

However, certain fcurves that are invalid are only registered as "invalid" in this code if I open an action from the GUI and it goes ahead and evaluates them to determine that they're errors.

How can I detect these from my code without needing to manipulate the GUI so I can process these fcurves?

$\endgroup$
1
  • $\begingroup$ Probably not the loop to use (action in bpy.data.actions) Need to know for what object(s) the action is for. For example all pose bone fcurves will be invalid for the Cube. If the object is an armature it will require the named pose bone. blender.stackexchange.com/questions/214049/… $\endgroup$ – batFINGER May 11 at 1:57
1
$\begingroup$

ID.path_resolve

Probably not the loop to use (action in bpy.data.actions) Need to know for what object(s) the action is for. For example all pose bone fcurves will be invalid for the Cube. If the object is an armature it will require the named pose bone.

Similarly to Evaluate FCurve without keyframes won't work can use Object.path_resolve(FCurve.data_path) in a try / except clause to search for what will be seen as invalid fcurves.

Have left out the "obvious" test of looking to see which action is assigned to objects animation data

Is the object that is associated with an action selected?

action is ob.animation_data.action

or searching for it in the Strips of NLA Tracks. Something like

any(s.action is action for t in ob.animation_data.nla_tracks for s in t.strips) 

Also look at the Action.id_root which is set when adding a new action to a particular data type using the UI. No suitably rooted paths error when trying to set action on object

Test script, checks all actions against all objects, meshes, shape keys and materials and counts the fcurves valid for each.

import bpy
from itertools import product
context = bpy.context
prev = None
for action, ob in product(
        bpy.data.actions, 
        sum(
                (
                bpy.data.objects[:], 
                bpy.data.meshes[:],
                bpy.data.materials[:],
                bpy.data.shape_keys[:],
                ),
                []
            )
        ):
    if action is not prev:
        print("-" * 44)
        print(f"Action : {action.name}")
        n = len(action.fcurves)
        print()
        print(f"\tID {action.id_root}")
        print(f"\tFcurves : {n}")        
        prev = action
    def is_valid(fc):
        
        try:
            ob.path_resolve(fc.data_path)
            return 1
        except:
            return 0
    v = sum(is_valid(fc) for fc in action.fcurves)
    print(f"\t{ob.bl_rna.identifier} : {ob.name}  Valid : {v} ({100 * v / n}%)")
print()   

Test run, quickly added some keyframes to some objects, a mesh, an armature object a material and a shape key. The "CubeAction" on the cube action was LocRotScale, which will be valid on any object.

--------------------------------------------
Action : ArmatureAction

    ID OBJECT
    Fcurves : 10
    Object : Armature  Valid : 10 (100.0%)
    Object : Camera  Valid : 0 (0.0%)
    Object : Cube  Valid : 0 (0.0%)
    Mesh : Cube  Valid : 0 (0.0%)
    Material : Material  Valid : 0 (0.0%)
    Key : Key  Valid : 0 (0.0%)
--------------------------------------------
Action : CameraAction

    ID OBJECT
    Fcurves : 3
    Object : Armature  Valid : 3 (100.0%)
    Object : Camera  Valid : 3 (100.0%)
    Object : Cube  Valid : 3 (100.0%)
    Mesh : Cube  Valid : 0 (0.0%)
    Material : Material  Valid : 0 (0.0%)
    Key : Key  Valid : 0 (0.0%)
--------------------------------------------
Action : CubeAction

    ID OBJECT
    Fcurves : 6
    Object : Armature  Valid : 6 (100.0%)
    Object : Camera  Valid : 6 (100.0%)
    Object : Cube  Valid : 6 (100.0%)
    Mesh : Cube  Valid : 0 (0.0%)
    Material : Material  Valid : 0 (0.0%)
    Key : Key  Valid : 0 (0.0%)
--------------------------------------------
Action : CubeAction.001

    ID MESH
    Fcurves : 1
    Object : Armature  Valid : 0 (0.0%)
    Object : Camera  Valid : 0 (0.0%)
    Object : Cube  Valid : 0 (0.0%)
    Mesh : Cube  Valid : 1 (100.0%)
    Material : Material  Valid : 0 (0.0%)
    Key : Key  Valid : 0 (0.0%)
--------------------------------------------
Action : KeyAction

    ID KEY
    Fcurves : 1
    Object : Armature  Valid : 0 (0.0%)
    Object : Camera  Valid : 0 (0.0%)
    Object : Cube  Valid : 0 (0.0%)
    Mesh : Cube  Valid : 0 (0.0%)
    Material : Material  Valid : 0 (0.0%)
    Key : Key  Valid : 1 (100.0%)
--------------------------------------------
Action : MaterialAction

    ID MATERIAL
    Fcurves : 2
    Object : Armature  Valid : 0 (0.0%)
    Object : Camera  Valid : 0 (0.0%)
    Object : Cube  Valid : 0 (0.0%)
    Mesh : Cube  Valid : 0 (0.0%)
    Material : Material  Valid : 2 (100.0%)
    Key : Key  Valid : 0 (0.0%)
$\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.