0

I am learning how to create extensions and I am stuck. In a text overlay I want to update the time every second with a modal operator. The script is running in principle, but no text is displayed on the screen above the 3D viewport. Can you help me?

Blender 3.4.1

import bpy
import os
import time
import blf

class ModalTimerOperator(bpy.types.Operator): bl_idname = "wm.modal_curremt_time_operator" bl_label = "Current Time Operator"

timer = None

font_info = {
    "font_id": 0,
    "handler": None,
}

font_path = bpy.path.abspath('//Zeyada.ttf')

def draw_text(self):
    font_id = self.font_info["font_id"]
    blf.position(font_id, 2, 80, 0)
    blf.size(font_id, 50, 72)
    ido=time.strftime("%H:%M:%S")
    blf.draw(font_id, ido)

def remove_text(self):
    handler = bpy.app.driver_namespace.get('draw_time')
    if handler:
        bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
        del bpy.app.driver_namespace['draw_time']

def cancel(self, context):
    wm = context.window_manager
    wm.event_timer_remove(self._timer)


def modal(self, context, event):
    if event.type in {'RIGHTMOUSE', 'ESC'}:
        self.cancel(context)
        remove_text()
        return {'FINISHED'}

    if event.type == 'TIMER':
        self.draw_text()

    return {'PASS_THROUGH'}

def execute(self, context):        
    if os.path.exists(self.font_path):
        self.font_info["font_id"] = blf.load(self.font_path)
    else:
        self.font_info["font_id"] = 0    

    self.font_info["handler"] = bpy.app.driver_namespace.get('draw_time')
    if not self.font_info["handler"]:
        self.font_info["handler"] = bpy.types.SpaceView3D.draw_handler_add(
            self.draw_text,(),'WINDOW','POST_PIXEL'
        )
        dns=bpy.app.driver_namespace
        dns['draw_time']=self.font_info["handler"]

    wm=context.window_manager
    self.timer=wm.event_timer_add(time_step=1.0,window=context.window)
    wm.modal_handler_add(self)

    return {'RUNNING_MODAL'}

def register(): bpy.utils.register_class(ModalTimerOperator)

def unregister(): bpy.utils.unregister_class(ModalTimerOperator)

if name=="main": register() ```

  • use Application Timers to make a timer: https://docs.blender.org/api/current/bpy.app.timers.html#module-bpy.app.timers – X Y Feb 20 '23 at 15:42
  • I would use bpy.app.handlers.frame_change_pre instead of a time like here How can I make dynamic text in an animation? - Blender Stack Exchange – L0Lock Feb 20 '23 at 16:45
  • The timer is not a good solution because it does not automatically refresh the 3D viewport. Even if the text changes because of the timer, it will only be visible after the user does something in the viewport. The modal operator is the best way to do this. But for some reason it doesn't work. – Ambrus Attila Feb 21 '23 at 10:53
  • Frame_change_pre is called during rendering and playback. What you mean is persistent. But they have the same problem as the timer. It does not update the 3D viewport. And that was designed specifically so that you can't force it to update. There are cheat methods to do it, but it's not the tried and true method, it's the modal operator. So the question is, why doesn't the text plotting in the model operator work? – Ambrus Attila Feb 21 '23 at 11:02

0 Answers0