1

I want to animate any number of text objects using Python.

For example, imagine 10 text objects, changing their values to different strings, on any frame.

In short, I want full control to animate the text.data.body property.

Searching on the web, I found a solution for simple cases -> Keyframe on text body in Python?

And then I wrote my own code. However, I get TypeError: 'NoneType' object is not callable from my console, because I am trying to call my update_text function with different parameters.

Apparently, a function must be given to the 'register' without being called. Does anybody have any idea? I see that this is yet an unsolved problem...

Thanks in advanced!


def update_text(text_obj, old_text, new_text, key_frame):
    cur_frame = bpy.context.scene.frame_current
    if cur_frame <= key_frame:
        text_obj.data.body = old_text
    elif cur_frame > key_frame:
        text_obj.data.body = new_text

def register(update): bpy.app.handlers.frame_change_post.append(update)

update text content

register(update_text(my_text_obj, "old", "new", 10))

hrk
  • 57
  • 7

1 Answers1

5

Return a callable

Please read the handler docs https://docs.blender.org/api/current/bpy.app.handlers.html A handler method takes two arguments the scene and the depsgraph. You append a function to handlers. In above update is a callable function whereas update(foo) is the result of calling that function, which without a return, returns None by default. Explaining error message.

That covered, using a method similar to answer here

Handler-script updates in viewport but not in render

Recommend passing the name rather than the object , otherwise object removal may cause null reference issues. The object is fetched from the scene objects collection by name in the handler.

Q code

import bpy

def update_text(name, old_text, new_text, key_frame): def handler(scene, depsgraph): text_ob = scene.objects.get(name) if not text_ob: return f = scene.frame_current text_ob.data.body = new_text if f >= key_frame else old_text return handler

def register(update): bpy.app.handlers.frame_change_post.append(update)

while testing to avoid buildup

bpy.app.handlers.frame_change_post.clear()

update text content

register(update_text("Text", "old", "new", 10))

Am currently using 2.90alpha and this is updating both in viewport and when rendered. Will find some issues with earlier versions of 2.8 not updating in render.

Check out the answer in link above re using a text data block as input using line numbers as frames. Instead of replacing body on each line could append or hold forward if a line is empty. (eg for example "old" on line 1, and "new" on line 10.

enter image description here

import bpy

pass arguments to build a handler

def text_body_handler(text): lines = len(text.lines) # the handler method def text_body(scene): f = scene.frame_current text_ob = scene.objects.get("Text") if text_ob and f <= lines: body = text.lines[f-1].body if body: text_ob.data.body = body

return text_body

bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(text_body_handler(bpy.data.texts["Text"]))

Related

Updating text object in Blender 2.81 using python

Note also: Avoid mixing context with handlers. It may be the case that the scene being "handled" is not the context scene. (bpy.context.scene) perhaps given a bit of a bum steer by answer in Q link..

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • 1
    Your solution is working perfectly! Deffing a function inside a function? Prodigious. I am using official 2.83 release and it is working there. Thank you very very very much! – hrk Jun 23 '20 at 19:37
  • I would like share how I used your answer in my code, there is only a little bit of change. I want a certain text to be visible during a frame period, to achive this, I simply modified the if statement as if f >= keyframe - 30 and f < keyframe: text_obj.data.body = new_text - Thanks again for your help! – hrk Jun 23 '20 at 19:42
  • 1
    Thankyou, appreciated. Happy to help. Remember pondering whilst writing earlier answer that could name text blocks and text objects the same, search for matches and change bodies accordingly. Seeing new comment is going to be much easier to put in a file. The conditional could be if f in range(start, end) Make sure f is an int. – batFINGER Jun 23 '20 at 19:44
  • Was having a similar issue, where I wanted to pass an argument to function after render is completed. This solved my problem. Thanks a lot. Upvoted. – Neeraj Lagwankar Dec 12 '21 at 07:37
  • There is no information about handlers argumetns (Scene, Depsgraph) in blender docs Where did you get it? – Bicukow Dec 05 '23 at 17:53