1
$\begingroup$

I want to create an action (Fcurve) before I have any object to attach it to. I know how to create an Fcurve directly for an object: for example, to keyframe the location of an object

object.location = (x, y, z)
object.keyframe_insert(‘location’, frame=1)

I have tried to do the following:

# Create a new action (not attached to anything)
new_action = bpy.data.actions.new('NewAction')
new_action.keyframe_insert('location', index=2, frame=1)

Which produces the following error:

TypeError: bpy_struct.keyframe_insert() property "location" not found

which makes sense -- there is no object selected, so there is no 'location' that can be recorded.

So, is there a way to do it, with no object involved in the initial creation of the Fcurve?

$\endgroup$
1

2 Answers 2

3
$\begingroup$

No changes from 2.79

AFAIK there are no noticeable changes to creating actions, fcurves or inserting keyframes in 2.80.

Some blender API basics

bpy.types.bpy_struct

built-in base class for all classes in bpy.types.

bpy_struct.keyframe_insert(...)

Insert a keyframe on the property given, adding fcurves and animation data when necessary. Strongly recommend you visit and checkout the examples given

hence the keyframe_insert method will belong to all objects in bpy whether they are animatable or not. Was there a slogan? Blender everything** is animatable

obj = bpy.context.object

# set the keyframe at frame 1
obj.location = 3.0, 4.0, 10.0
obj.keyframe_insert(data_path="location", frame=1)

which will, if needed create the animation data, create the action, create the fcurves, and then insert keyframes into action fcurves for the given data path and array index.

Lets do this with an action

>>> action = D.actions.new("dummy")
>>> action.location = 1, 2, 3
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
AttributeError: 'Action' object has no attribute 'location'

oh an error, wait... could this be the reason for action.keyframe_insert("location") throwing

TypeError: bpy_struct.keyframe_insert() property "location" not found

I believe so. Has nothing to do with object selection. It's OOP 101 the object is in this case a new action named "dummy", referenced by variable action.

Ok then what about a property that does belong to an action like frame_range let's animate that so at every frame the frame range of the action changes..

>>> D.actions['dummy'].keyframe_insert("frame_range")
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
TypeError: bpy_struct.keyframe_insert() property "frame_range" not animatable

but it says the property is animatable

>>> D.actions['dummy'].bl_rna.properties['frame_range'].is_animatable
True

in hindsight, perhaps this property is read only since it is basically the minimum / maximum of keyframe frames over all fcurves, and hence can't be animated.

>>> D.actions['dummy'].bl_rna.properties['frame_range'].is_readonly
True

Believe this also answers why option 2 in your answer "doesn't work". Being able to animate fcurve keyframe coordinates would mean the graphs changing when the frame changes.... possibly (probably) not plausible.

The method to make a simple scale action emulates keyframe_insert("scale") to some extent without "being tied to any object" Could be edited to create fcurves with any values for datapath and array index.

Related

Fast keyframe insertion from data

Quite probably a duplicate question.

How do I add keyframes to a newly created action with no associated objects?

**Almost everything

$\endgroup$
0
2
$\begingroup$

There is a related question here (Is it possible to create instance with different scale animation data but the same mesh with python?), but I will expand on it:

Once a new action has been created, and an Fcurve for it, using keyframes_insert does not work (it produces the error pasted in the question). Using keyframes_points.add(count=X), does work, and later the frame number of value can be set with:

keyframe_point.co = (frame_number, value)

For example (taken from the link above):

from random import uniform

def random_scale_action():
    action = bpy.data.actions.new("RandomScaleAction")
    data_path = "scale"
    # (frame, value) for keyframe point
    for axis in [0, 1, 2]:
        # new fcurve
        fc = action.fcurves.new(data_path, index=axis)
        # add a new keyframe point
        # OPTION 1: IT WORKS!
        fc.keyframe_points.add(count=2)
        for kfp in fc.keyframe_points:
            kfp.co = (uniform(1,100), uniform(0, 1))
        # OPTION 2: does NOT work, and produces the error pasted into the question(above)
        fc.keyframe_insert(data_path, index=axis, frame=uniform(1,100))

    return action

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .