diff --git a/scripts/modules/bpy_extras/anim_utils.py b/scripts/modules/bpy_extras/anim_utils.py index c6aa64df7ab..19f565568dd 100644 --- a/scripts/modules/bpy_extras/anim_utils.py +++ b/scripts/modules/bpy_extras/anim_utils.py @@ -75,12 +75,19 @@ class BakeOptions: """Bake custom properties.""" -def action_get_channelbag_for_slot(action: Action, slot: ActionSlot) -> ActionChannelbag | None: +def action_get_channelbag_for_slot(action: Action | None, slot: ActionSlot | None) -> ActionChannelbag | None: """ Returns the first channelbag found for the slot. In case there are multiple layers or strips they are iterated until a channelbag for that slot is found. In case no matching channelbag is found, returns None. """ + if not action or not slot: + # This is just for convenience so that you can call + # action_get_channelbag_for_slot(adt.action, adt.action_slot) and check + # the return value for None, without having to also check the action and + # the slot for None. + return None + for layer in action.layers: for strip in layer.strips: channelbag = strip.channelbag(slot) diff --git a/scripts/modules/keyingsets_utils.py b/scripts/modules/keyingsets_utils.py index e12d9253a31..af871311c3c 100644 --- a/scripts/modules/keyingsets_utils.py +++ b/scripts/modules/keyingsets_utils.py @@ -25,6 +25,8 @@ __all__ = ( import bpy +from bpy_extras import anim_utils + ########################### # General Utilities @@ -117,7 +119,11 @@ def RKS_GEN_available(_ksi, _context, ks, data): # for each F-Curve, include a path to key it # NOTE: we don't need to set the group settings here - for fcu in adt.action.fcurves: + cbag = anim_utils.action_get_channelbag_for_slot(adt.action, adt.action_slot) + if not cbag: + return + + for fcu in cbag.fcurves: if basePath: if basePath in fcu.data_path: ks.paths.add(id_block, fcu.data_path, index=fcu.array_index) diff --git a/scripts/startup/keyingsets_builtins.py b/scripts/startup/keyingsets_builtins.py index 05b6d361155..db72bf4519c 100644 --- a/scripts/startup/keyingsets_builtins.py +++ b/scripts/startup/keyingsets_builtins.py @@ -17,6 +17,7 @@ are supported. import bpy import keyingsets_utils from bpy.types import KeyingSetInfo +from bpy_extras import anim_utils ############################### # Built-In KeyingSets @@ -355,7 +356,13 @@ class BUILTIN_KSI_Available(KeyingSetInfo): ob = context.active_object if ob: # TODO: this fails if one animation-less object is active, but many others are selected - return ob.animation_data and ob.animation_data.action + adt = ob.animation_data + if not adt: + return False + cbag = anim_utils.action_get_channelbag_for_slot(adt.action, adt.action_slot) + if not cbag: + return False + return bool(cbag.fcurves) else: return bool(context.selected_objects) diff --git a/source/blender/makesrna/intern/rna_action.cc b/source/blender/makesrna/intern/rna_action.cc index 38611d00bba..03e94c65d89 100644 --- a/source/blender/makesrna/intern/rna_action.cc +++ b/source/blender/makesrna/intern/rna_action.cc @@ -745,9 +745,15 @@ static void rna_Channelbag_group_remove(ActionChannelbag *dna_channelbag, static ActionChannelbag *rna_ActionStrip_channelbag(ID *dna_action_id, ActionStrip *self, + ReportList *reports, const ActionSlot *dna_slot, const bool ensure) { + if (!dna_slot) { + BKE_report(reports, RPT_ERROR, "Cannot return channelbag when slot is None"); + return nullptr; + } + animrig::Action &action = reinterpret_cast(dna_action_id)->wrap(); animrig::StripKeyframeData &strip_data = self->wrap().data(action); const animrig::Slot &slot = dna_slot->wrap(); @@ -2377,7 +2383,7 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna) /* Strip.channelbag(...). */ func = RNA_def_function(srna, "channelbag", "rna_ActionStrip_channelbag"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Find the ActionChannelbag for a specific Slot"); parm = RNA_def_pointer( func, "slot", "ActionSlot", "Slot", "The slot for which to find the channelbag");