1
$\begingroup$

I have about 300 text objects, of which the texts need to change during the animation (numbers in a graph). I came across this Python method using applicatoin handlers: Keyframe on text body in Python?

My problem is: it works if I do this for one object at a time, but not when i use a for loop:

for p in pricelist:
    pname = p[0]
    plist = p[1:]
        
    text = bpy.data.objects[pname]
    print(pname, text.data.body)
        
    def update(self):
        text = bpy.data.objects[pname]
        frame = bpy.context.scene.frame_current
        text.data.body = f'{plist[scene.frame_current]}'
    
    def register():
        bpy.app.handlers.frame_change_post.append(update)

    def unregister():
        bpy.app.handlers.frame_change_post.remove(update)

    register()

(with pricelist being a list of lists, every row contains the object name (first item) and after that, for every frame a value in a string).

When I run it, nothing happens. Sometimes, I have not figured out when this occurs, I get the error "ReferenceError: StructRNA of type Object has been removed" in the terminal over and over again as the animation plays.

Why does this not work, and how do I get the texts of 300 objects to change in the animation in one go?

Thanks in advance for your thoughts!

$\endgroup$
3
  • $\begingroup$ Hello, can you confirm that this is a typo and you're not registering your handler in the loop ? $\endgroup$
    – Gorgious
    Jun 23 at 14:18
  • $\begingroup$ In any case I would change text.data.body = f'{plist[scene.frame_current]}' for text.data.body = f'{plist[bpy.context.scene.frame_current]}', the scene variable seems to be initialized before the script and might be invaliated at the time of execution of the callback $\endgroup$
    – Gorgious
    Jun 23 at 14:25
  • $\begingroup$ Thanks for looking into this. The register() function is meant to be within the for loop, otherwise it would only register the last defined update in the loop. Just to be sure, I ran the code with register() outside the loop, to no effect. Also changing the scene variable to f'{plist[bpy.context.scene.frame_current]} didn't yield any results $\endgroup$
    – MSDMMM
    Jun 23 at 17:44

1 Answer 1

1
$\begingroup$

It looks like the update function is overridden in forloop in blender, but making a wrapper fixes that.

import bpy

# add 2 text object
bpy.ops.object.text_add(location=(0,0,0))
bpy.ops.object.text_add(location=(0,2,0))
#

list2 = [
    ["Text", [f"Cube: frame={r}"  for r in range(500)]],
    ["Text.001", [f"Cube.001: frame={r}"  for r in range(500)]]
]

objs = bpy.data.objects

def wrap(name, l):
    def update(self):
        print("update start", name)
        frame = bpy.context.scene.frame_current
        objs[name].data.body = f'{l[frame]}'
    return update

for name, l in list2:
    fx = wrap(name, l)
    
    def register():
        print("reg", name)
        bpy.app.handlers.frame_change_post.append(fx)
        print("len", len(bpy.app.handlers.frame_change_post))

    register()

Here is an example that doesn't work

import bpy

# add 2 text object
bpy.ops.object.text_add(location=(0,0,0))
bpy.ops.object.text_add(location=(0,2,0))
#

list2 = [
    ["Text", [f"Cube: frame={r}"  for r in range(500)]],
    ["Text.001", [f"Cube.001: frame={r}"  for r in range(500)]]
]

objs = bpy.data.objects

for name, l in list2:
    def update(self):
        print("update start", name)
        frame = bpy.context.scene.frame_current
        objs[name].data.body = f'{l[frame]}'

    def register():
        print("reg", name)
        bpy.app.handlers.frame_change_post.append(update)
        print("len", len(bpy.app.handlers.frame_change_post))

    register()
$\endgroup$
1
  • $\begingroup$ Thanks a bunch, that totally did the trick! $\endgroup$
    – MSDMMM
    Jun 25 at 11:47

You must log in to answer this question.

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