If the duration of the scene is known, simply use bpy.context.scene.frame_end
and assign a custom duration value to it - this will overwrite the current playback/rendering range:
import bpy
# get the current scene
scn = bpy.context.scene
# assign new duration value
scn.frame_end = 880
In order to adjust the scene duration dynamically (based on the included animations) you'll need to iterate through all the relevant objects in the scene and read their keyframes by using either Animation Data or Actions.
Keyframes via Animation Data
Use animation_data
to get the keyframes of an object. Following example reads the keyframes of the selected objects and prints the first and the last keyframe to the console.
import bpy
import math
# get keyframes of object list
def get_keyframes(obj_list):
keyframes = []
for obj in obj_list:
anim = obj.animation_data
if anim is not None and anim.action is not None:
for fcu in anim.action.fcurves:
for keyframe in fcu.keyframe_points:
x, y = keyframe.co
if x not in keyframes:
keyframes.append((math.ceil(x)))
return keyframes
# get all selected objects
selection = bpy.context.selected_objects
# check if selection is not empty
if selection:
# get all frames with assigned keyframes
keys = get_keyframes(selection)
# print all keyframes
print (keys)
# print first and last keyframe
print ("{} {}".format("first keyframe:", keys[0]))
print ("{} {}".format("last keyframe:", keys[-1]))
else:
print ('nothing selected')
Keyframes via Actions
Use bpy.data.actions
to get a list of actions in the scene, but actions are not associated with objects, scenes or nodes. Following example reads the frame range of all F-Curves within the actions of the file and prints the first and the last keyframe to the console.
import bpy
# check if actions is empty
if bpy.data.actions:
# get all actions
action_list = [action.frame_range for action in bpy.data.actions]
# sort, remove doubles and create a set
keys = (sorted(set([item for sublist in action_list for item in sublist])))
# print all keyframes
print (keys)
# print first and last keyframe
print ("{} {}".format("first keyframe:", keys[0]))
print ("{} {}".format("last keyframe:", keys[-1]))
else:
print ("no actions")
Test scene
Console Output of both methods:
[1, 5, 7, 18, 21, 30, 47, 49, 67, 69, 75, 84, 100]
first keyframe: 1
last keyframe: 100
Speed comparison
- Animation Data method takes 0.000446319580078125 sec.
- Action method takes 0.00011038780212402344 sec.
Adjusting the timeline
As mentioned above, in order to set the beginning and end of the scene, you can assign the calculated values to bpy.context.scene.frame_start
and bpy.context.scene.frame_end
:
# get the current scene
scn = bpy.context.scene
# assign new starting frame
scn.frame_start = keys[0]
# assign new end frame
scn.frame_end = keys[-1]
Final Note
It really depends on what you want to achieve. Using animation_data
is slower, but using action.frame_range
can be dangerous, because if you have to delete an object during the process, the actions of the objects remain until the blend file is saved again. See: https://blender.stackexchange.com/a/27235/3710