Anim: add keytype argument to keyframe_insert() RNA function

Add an optional keyword argument `keytype` to the
`rna_struct.keyframe_insert()` function.

This makes it possible to set the new key's type. The code for this was
almost all in place, the only thing that was missing was the RNA
wrapper, which is what this commit adds.

Example: `bpy.context.object.keyframe_insert("location",
keytype='JITTER')`

There is no backward compatibility issue here, because the argument is
optional and defaults to the previously hardcoded value of `KEYFRAME`.

Pull Request: https://projects.blender.org/blender/blender/pulls/120578
This commit is contained in:
Sybren A. Stüvel 2024-04-12 16:27:30 +02:00
parent d8d6925bc7
commit 1315991ecb
2 changed files with 44 additions and 8 deletions

@ -228,10 +228,13 @@ static int pyrna_struct_keyframe_parse(PointerRNA *ptr,
int *r_index,
float *r_cfra,
const char **r_group_name,
int *r_options)
int *r_options,
eBezTriple_KeyframeType *r_keytype)
{
static const char *kwlist[] = {"data_path", "index", "frame", "group", "options", nullptr};
static const char *kwlist[] = {
"data_path", "index", "frame", "group", "options", "keytype", nullptr};
PyObject *pyoptions = nullptr;
char *keytype_name = nullptr;
const char *path;
/* NOTE: `parse_str` MUST start with `s|ifsO!`. */
@ -244,7 +247,8 @@ static int pyrna_struct_keyframe_parse(PointerRNA *ptr,
r_cfra,
r_group_name,
&PySet_Type,
&pyoptions))
&pyoptions,
&keytype_name))
{
return -1;
}
@ -269,12 +273,24 @@ static int pyrna_struct_keyframe_parse(PointerRNA *ptr,
*r_options |= INSERTKEY_NO_USERPREF;
}
if (r_keytype) {
int keytype_as_int = 0;
if (keytype_name && pyrna_enum_value_from_id(rna_enum_beztriple_keyframe_type_items,
keytype_name,
&keytype_as_int,
error_prefix) == -1)
{
return -1;
}
*r_keytype = eBezTriple_KeyframeType(keytype_as_int);
}
return 0; /* success */
}
char pyrna_struct_keyframe_insert_doc[] =
".. method:: keyframe_insert(data_path, index=-1, frame=bpy.context.scene.frame_current, "
"group=\"\", options=set())\n"
"group=\"\", options=set(), keytype='KEYFRAME')\n"
"\n"
" Insert a keyframe on the property given, adding fcurves and animation data when "
"necessary.\n"
@ -304,6 +320,9 @@ char pyrna_struct_keyframe_insert_doc[] =
" - ``INSERTKEY_CYCLE_AWARE`` Take cyclic extrapolation into account "
"(Cycle-Aware Keying option).\n"
" :type flag: set\n"
" :arg keytype: Type of the key: 'KEYFRAME', 'BREAKDOWN', 'MOVING_HOLD', 'EXTREME', "
"'JITTER', or 'GENERATED'\n"
" :type keytype: string\n"
" :return: Success of keyframe insertion.\n"
" :rtype: boolean\n";
PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyObject *kw)
@ -313,7 +332,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
int index = -1;
float cfra = FLT_MAX;
const char *group_name = nullptr;
const char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */
eBezTriple_KeyframeType keytype = BEZT_KEYTYPE_KEYFRAME;
int options = 0;
PYRNA_STRUCT_CHECK_OBJ(self);
@ -321,13 +340,14 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
"s|$ifsO!:bpy_struct.keyframe_insert()",
"s|$ifsO!s:bpy_struct.keyframe_insert()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
&cfra,
&group_name,
&options) == -1)
&options,
&keytype) == -1)
{
return nullptr;
}
@ -440,12 +460,13 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
"s|$ifsO!:bpy_struct.keyframe_delete()",
"s|$ifsOs!:bpy_struct.keyframe_delete()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
&cfra,
&group_name,
nullptr,
nullptr) == -1)
{
return nullptr;

@ -188,6 +188,21 @@ class KeyframeInsertTest(AbstractAnimationTest, unittest.TestCase):
bpy.ops.object.delete(use_global=False)
def test_keyframe_insert_keytype(self):
key_object = bpy.context.active_object
# Inserting a key with a specific type should work.
key_object.keyframe_insert("location", keytype='GENERATED')
# Unsupported/unknown types should be rejected.
with self.assertRaises(ValueError):
key_object.keyframe_insert("rotation_euler", keytype='UNSUPPORTED')
# Only a single key should have been inserted.
keys = key_object.animation_data.action.fcurves[0].keyframe_points
self.assertEqual(len(keys), 1)
self.assertEqual(keys[0].type, 'GENERATED')
def test_keyframe_insertion_high_frame_number(self):
bpy.ops.mesh.primitive_monkey_add()
key_count = 100