I'm trying to change a text object on each frame in Blender 2.75a using the code from a previous answer (How can I make dynamic text in an animation?). With my script the text value is coming from the lens property of a camera which is keyframed.

I have added the script to the blender file and changed it to use the camera lens value. When I hit Run script and then move a frame it updates the text object in the 3D viewport as expected. But then when I move another frame it does not update. Nor does it work for Alt+A or if I render a sequence of frames.

As per Ray's suggestion I added a print function which showed frame and lens value. As I moved back the frame back and forth the frame number updated; however the lens value only changed the first time after hitting Run Script and then remained the same in the console. The lens value was changing in the UI.

Code is here: http://www.pasteall.org/61366/python

I've tried code to clear the handlers (frame_change_pre.clear()) and closing down Blender and reopening to reset everything and the same behavior occurs.

Zeffii's suggestion of moving the camera_lens = bpy.data.cameras['Camera'].lens line into the function partially worked.

My understanding of the problem now (thanks, everyone) is that when the script is run for the first time it establishes the handler and sets the variable with the lens value. From that point on when the frame moves the handler only executes the function and since the variable that holds the lens value is outside the function it is never updated. Moving the lens variable assignment to inside the function now means it is updated each time the handler is called.

However, now the update lags one frame behind. i.e:

  • start on frame 1, UI and 3D view = 16.0.
  • Move to frame 2, UI = 16.9, 3D view = 16.0.
  • Move to frame 3, UI = 17.8, 3D view = 16.9.

I'm guessing the python handler is executing before the keyframe value is calculated. Tried changing to frame_change_post but that didn't seem to work either... suggestions?

  • 1
    $\begingroup$ A print statement in the handler is a quick way to test if it is being run, by looking at the system console. If it is being run each time, perhaps the property (camera lens value?) is not updated. $\endgroup$
    – batFINGER
    Sep 27, 2015 at 4:18
  • $\begingroup$ axb2035, please share the smallest amount of code that demonstrates the problem. Something like: gist.github.com/zeffii/3e98014f3ee7b050eb03 $\endgroup$
    – zeffii
    Sep 27, 2015 at 6:40
  • 1
    $\begingroup$ move camera_lens = bpy.data.cameras['Camera'].lens into your function $\endgroup$
    – zeffii
    Sep 27, 2015 at 21:47
  • 1
    $\begingroup$ recommend you edit your question and title to focus on the current problem. (off by one frame in ui vs render) . The problem isn't (and never was) that the event handler only worked once, the value wasn't updated because it was only generated once -- outside of the event handler. $\endgroup$
    – zeffii
    Sep 28, 2015 at 4:51

1 Answer 1


As pointed out by zeffi - camera_lens = bpy.data.cameras['Camera'].lens assigns the current value of the camera lens to the variable. That is camera_lens is set to 16.0 when you run the script to insert the handler, this value is then used to set the text every time the handler is run. Only the code within recalculate_text() is run on frame change, not the code before it. What you want is to hold the camera object in a variable and then read the current camera lens setting each time you update the text contents, or to use the full data path within the function.

The off by one issue is the order that things happen. The frame_change_pre handlers are run before frame_current is changed while frame_change_post handlers are run after. frame_current needs to change so that things like a value controlled by an fcurve can be calculated, which allows you to read the keyed value that applies to the current frame.

import bpy

def recalculate_text(scene):
    text_obj = scene.objects['Lens']
    my_camera = bpy.data.cameras['Camera']
    text_obj.data.body = ('{0:.1f} mm' .format(my_camera.lens))


You must log in to answer this question.

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