1
$\begingroup$

The script below uses a handler function to show the current frame on the Text_object (every 10 frames) in 3d_view.

Tried assigning current_frame to text object in 2 ways:
1.With context.object.data.body
2.Updating text from Edit mode with editmodetoggle()

Both , Frame and Scene are being updated with their respective handler functions.

However, in both cases the handler function is not entirely getting executed and the Text in 3d_view doesn't update

How to resolve this?

# SCRIPT -->  SHOW CURRENT FRAME (EVERY 10 FRAME'S) IN 3D_VIEW

import bpy 

scene = bpy.context.scene
obj_active = scene.objects.active
obj_ops = bpy.ops.object
font_ops = bpy.ops.font
handlers = bpy.app.handlers

frame_current = scene.frame_current
obj_active = bpy.data.objects["Text"]


from bpy.app.handlers import persistent
@persistent
def frame_change(scene):
    frame_end = 200
    print(frame_current ," = script start")
    
#ATTEMPT 1---------------   UPDATE TEXT.DATA.BODY   --------------
    if frame_current <= frame_end:
        if frame_current % 10 == 0 :    
            obj_active.data.body = str(frame_current)      
            print(frame_current ," = TEXT UPDATED")  #Function not getting executed
    print(frame_current ," = script end")     
 
                 
#ATTEMPT 2----------------     EDIT TEXT FROM EDIT MODE    ------------------ 
#    if frame_current <= frame_end:
#        if frame_current % 10 == 0 :
#            obj_ops.editmode_toggle()
#            font_ops.delete(type='ALL')

#            text_frame = str(frame_current)
#            font_ops.text_insert(text = text_frame)
#            obj_ops.editmode_toggle()
#            print(text_frame," = FRAME CHANGE")  

def register():    
handlers.frame_change_post.clear()   
handlers.frame_change_post.append(frame_change)
bpy.app.handlers.scene_update_post.append(frame_change)
handlers.load_post.append(frame_change)



def unregister():  
    handlers.frame_change_post.pop(frame_change)
    bpy.app.handlers.scene_update_post.pop(frame_change)
 

if __name__ == "__main__":
    register()

##########################---------- EDIT 1 -------##########################

Tried @batFinger's script but Text doesn't render in 3d_View (even if script runs in console )

enter image description here

import bpy 
from bpy.app import handlers    
from bpy.app.handlers import persistent


@persistent
def frame_change(scene):
    frame_current = scene.frame_current
    frame_end = 200
    obj_active = scene.objects.get("Text")
    print(frame_current ," = script start")
    if obj_active is None:
        return # no font object named "Text"
    if frame_current <= frame_end:
        if frame_current % 10 == 0 :    
            obj_active.data.body = str(frame_current)      
            print(frame_current ," = TEXT UPDATED") 
    print(frame_current ," = script end")     
 
def register():    
    handlers.frame_change_post.append(frame_change)
    
#ALSO TRIED  THE BELOW COMMAND'S BUT NO RESULT     
#    handlers.render_post.append(frame_change)
#    handlers.scene_update_post.append(frame_change)    
#    handlers.load_post.append(frame_change)
#    handlers.frame_change_pre.append(frame_change)

def unregister():  
    handlers.frame_change_post.remove(frame_change)
#    handlers.render_post.remove(frame_change)
#    handlers.scene_update_post.remove(frame_change)
#    handlers.load_post.remove(frame_change)
#    handlers.frame_change_pre.remove(frame_change)
    
if __name__ == "__main__":
    register()
$\endgroup$

2 Answers 2

2
$\begingroup$

An issue of scope.

As mentioned in a prior answer. Defining frame current outside the handler sets it to what the current frame was when question script above is run and the handler is registered.

If you want current frame when the handler is run

def frame_change(scene):
    frame_end = 200
    frame_current = scene.frame_current
    print(frame_current ," = script start")

secondly recommend don't scope an object outside the handler, instead within the handler

ob = scene.objects.get("Text")
if ob:
    # it exists do something to it.

Also don't mix context with handlers. Every chance it will not work as expected, especially when rendering.

Related.

Handler-script updates in viewport but not in render

This should work in 2.7

Notice for 2.7x use frame change pre, for 2.9 use frame change post.

Notice the use of the div operator

int(frame_current) // 10

to set on each frame, otherwise will not set properly until a 10th frame using the modulus == 0 operator.

import bpy 
from bpy.app  import handlers


from bpy.app.handlers import persistent
@persistent
def frame_change(scene):
    frame_current = scene.frame_current
    frame_end = 200
    obj_active = scene.objects.get("Text")
    print(frame_current ," = script start")
    if obj_active is None:
        return # no font object named "Text"
    if frame_current <= frame_end:
        obj_active.data.body = str(int(frame_current) // 10)      
        print(frame_current , int(frame_current) // 10) 
    print(frame_current ," = script end")     
 
def register():    
    handlers.frame_change_pre.clear()   # use when testing will burn others
    handlers.frame_change_pre.append(frame_change)


def unregister():  
    handlers.frame_change_pre.remove(frame_change)
 

if __name__ == "__main__":
    register()
$\endgroup$
4
  • $\begingroup$ Thanks for the explanation .Tried your script but Text doesn't render in 3d_View (even if script runs in console ).Question updated and blend file provided. $\endgroup$
    – srt111
    Jul 10, 2020 at 4:06
  • $\begingroup$ Also,if Object Scoping is to be kept local to the Handler Function, then how should a Property be declared while passing it from an operator class like Panel.row.prop to handler function? $\endgroup$
    – srt111
    Jul 10, 2020 at 4:09
  • $\begingroup$ Fired up 2.7 and fixed above. Not at all sure what you mean re "Property be declared while passing it from an operator class like Panel.row.prop to handler function" The handler function (for 2.7x) takes one argument scene. As shown the path to the font object named "Text" from scene is scene.objects["Text"] $\endgroup$
    – batFINGER
    Jul 10, 2020 at 6:34
  • $\begingroup$ Thanks for the answer.... Considering Object "Scoping" of local handler function variables ,how could one access them in external classes & functions? In this example , secs = font["timer"] ,font[timer] list is created in the handler function but accessed "globally" by the Panel class and is_timer() ,Why isn't there a scope error raised,considering font["timer"] is local to handler function .refer $\endgroup$
    – srt111
    Jul 10, 2020 at 11:43
0
$\begingroup$

This seems to work:

bl_info = {
    "name": "Name",
    "author": "Your Name",
    "version": (1, 0),
    "blender": (2, 83, 0),
    "location": "",
    "description": "",
    "warning": "",
    "doc_url": "",
    "category": "",
}

import bpy


@persistent
def the_handler(scene):
    if scene.frame_current % 10 == 0:
        if bpy.context.object not None:
            if bpy.context.object.type == 'FONT':
                bpy.context.object.data.body = str(scene.frame_current)

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

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


if __name__ == "__main__":
    register()
$\endgroup$
2
  • 1
    $\begingroup$ I think it might be helpful to give some explanation as to why this works vs the code in the question. $\endgroup$ Jul 9, 2020 at 15:34
  • $\begingroup$ I did not look into the code. Seemed easier to give a working example. Apparently batFINGER was answering the question at the same time and answered it in full. I am still leaving this in hope it might be an easy copy paste template for whoever finds it if they need an add-on with a handler. $\endgroup$ Jul 9, 2020 at 16:05

You must log in to answer this question.

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