Register Timer#
This document describes the timer registration system in the UVFlow addon_utils
module, which provides a powerful way to create and manage timed callbacks with automatic cleanup and advanced features.
Overview#
The timer registration system allows you to:
Create one-time or recurring timers with flexible intervals
Set timeouts for automatic timer termination
Control persistence across Blender sessions
Automatic cleanup when the addon is disabled
Manual timer control with start/stop functionality
Basic Timer Types#
One-Time Timer with Delay#
Execute a function once after a specified delay:
from ..addon_utils import Register
@Register.TIMER(first_interval=1.0, one_time_only=True, persistent=False)
def delayed_action():
print("This runs once after 1 second")
# Perform one-time action
Features:
Executes exactly once
Configurable delay before execution
Non-persistent (doesn’t survive Blender restart)
Infinite Recurring Timer#
Execute a function repeatedly at regular intervals:
@Register.TIMER(first_interval=0.0, step_interval=0.1, one_time_only=False, persistent=True)
def continuous_monitor():
print("This runs every 0.1 seconds indefinitely")
# Continuous monitoring logic
# Return -1 to stop the timer manually
# if some_condition:
# return -1
Features:
Runs indefinitely until manually stopped
Configurable interval between executions
Can be persistent across Blender sessions
Recurring Timer with Timeout#
Execute a function repeatedly for a limited time:
@Register.TIMER(first_interval=0.1, step_interval=0.1, timeout=5.0, one_time_only=False, persistent=True)
def limited_monitor():
print("This runs every 0.1 seconds for 5 seconds total")
# Limited-time monitoring logic
Features:
Runs repeatedly until timeout is reached
Automatic termination after specified duration
Configurable execution interval
Advanced Timer Features#
Timer with Parameters#
Pass arguments to your timer function:
@Register.TIMER(first_interval=1.0, step_interval=2.0, one_time_only=False,
args=("param1", "param2"), kwargs={"key": "value"})
def parameterized_timer(arg1, arg2, key=None):
print(f"Timer called with: {arg1}, {arg2}, key={key}")
Manual Timer Control#
Create timers that can be controlled programmatically:
# Create a timer handler for manual control
timer_handler = None
def start_monitoring():
global timer_handler
if timer_handler is None:
timer_handler = Register.TIMER.new_timer(
callback=monitor_function,
first_interval=0.0,
step_interval=0.5,
one_time_only=False,
persistent=False
)
def stop_monitoring():
global timer_handler
if timer_handler:
timer_handler.stop()
timer_handler = None
def monitor_function():
print("Monitoring...")
# Return -1 to stop from within the function
# if stop_condition:
# return -1
Conditional Timer Termination#
Stop timers based on conditions:
@Register.TIMER(first_interval=0.0, step_interval=0.1, one_time_only=False)
def conditional_timer():
import bpy
# Check if we should continue
if not bpy.context.scene.objects:
print("No objects in scene, stopping timer")
return -1 # Stop the timer
# Continue monitoring
print(f"Scene has {len(bpy.context.scene.objects)} objects")
# Return None or don't return to continue
Timer Parameters Reference#
Core Parameters#
@Register.TIMER(
first_interval=0.0, # Delay before first execution (seconds)
step_interval=0.1, # Interval between recurring executions (seconds)
timeout=0.0, # Total runtime limit (0 = no timeout)
one_time_only=True, # Execute only once vs. repeatedly
persistent=False, # Survive Blender restart
args=(), # Positional arguments for the callback
kwargs={} # Keyword arguments for the callback
)
def my_timer_function():
pass
Parameter Details#
first_interval: Time to wait before the first execution
step_interval: Time between executions (only for recurring timers)
timeout: Maximum total runtime (only for recurring timers)
one_time_only:
True
for single execution,False
for recurringpersistent:
True
to survive Blender restart,False
for session-onlyargs: Tuple of positional arguments passed to the callback
kwargs: Dictionary of keyword arguments passed to the callback
Examples#
Auto-Save Functionality#
@Register.TIMER(first_interval=300.0, step_interval=300.0, one_time_only=False, persistent=True)
def auto_save_timer():
import bpy
# Only save if there are unsaved changes
if bpy.data.is_dirty:
try:
bpy.ops.wm.save_mainfile()
print("Auto-saved project")
except Exception as e:
print(f"Auto-save failed: {e}")
return -1 # Stop on error
Progress Monitoring#
progress_data = {"current": 0, "total": 100, "active": False}
@Register.TIMER(first_interval=0.1, step_interval=0.1, one_time_only=False)
def progress_monitor():
if not progress_data["active"]:
return -1 # Stop when not active
current = progress_data["current"]
total = progress_data["total"]
percentage = (current / total) * 100
print(f"Progress: {percentage:.1f}% ({current}/{total})")
if current >= total:
print("Operation completed!")
progress_data["active"] = False
return -1 # Stop when complete
# Start monitoring
def start_operation():
progress_data.update({"current": 0, "total": 100, "active": True})
# Update progress from other parts of code
def update_progress(value):
progress_data["current"] = value
Performance Monitoring#
import time
performance_data = {"start_time": None, "frame_count": 0}
@Register.TIMER(first_interval=1.0, step_interval=1.0, one_time_only=False, persistent=False)
def fps_monitor():
import bpy
if performance_data["start_time"] is None:
performance_data["start_time"] = time.time()
performance_data["frame_count"] = 0
return
current_time = time.time()
elapsed = current_time - performance_data["start_time"]
if elapsed >= 1.0: # Report every second
fps = performance_data["frame_count"] / elapsed
print(f"Approximate FPS: {fps:.2f}")
# Reset counters
performance_data["start_time"] = current_time
performance_data["frame_count"] = 0
# Call this from draw handlers or other frequent callbacks
def count_frame():
performance_data["frame_count"] += 1
Delayed Cleanup#
cleanup_queue = []
@Register.TIMER(first_interval=5.0, step_interval=5.0, one_time_only=False)
def cleanup_timer():
global cleanup_queue
if not cleanup_queue:
return # Continue checking
print(f"Processing {len(cleanup_queue)} cleanup items")
for cleanup_func in cleanup_queue:
try:
cleanup_func()
except Exception as e:
print(f"Cleanup error: {e}")
cleanup_queue.clear()
# Add cleanup tasks from anywhere in your code
def schedule_cleanup(cleanup_function):
cleanup_queue.append(cleanup_function)
Temporary UI Updates#
ui_flash_data = {"active": False, "start_time": 0, "duration": 2.0}
@Register.TIMER(first_interval=0.1, step_interval=0.1, one_time_only=False)
def ui_flash_timer():
import bpy
if not ui_flash_data["active"]:
return # Keep checking
elapsed = time.time() - ui_flash_data["start_time"]
if elapsed >= ui_flash_data["duration"]:
# Stop flashing
ui_flash_data["active"] = False
# Reset UI to normal state
for area in bpy.context.screen.areas:
area.tag_redraw()
return
# Continue flashing
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
area.tag_redraw()
def flash_ui(duration=2.0):
ui_flash_data.update({
"active": True,
"start_time": time.time(),
"duration": duration
})
Best Practices#
1. Use Appropriate Timer Types#
# For one-time delayed actions
@Register.TIMER(first_interval=2.0, one_time_only=True)
def delayed_setup():
pass
# For continuous monitoring
@Register.TIMER(first_interval=0.0, step_interval=1.0, one_time_only=False)
def continuous_check():
pass
# For limited-time operations
@Register.TIMER(first_interval=0.0, step_interval=0.1, timeout=10.0, one_time_only=False)
def temporary_monitor():
pass
2. Handle Errors Gracefully#
@Register.TIMER(first_interval=1.0, step_interval=1.0, one_time_only=False)
def robust_timer():
try:
# Your timer logic here
risky_operation()
except Exception as e:
print(f"Timer error: {e}")
return -1 # Stop on error to prevent spam
3. Use Efficient Intervals#
# Good: Reasonable intervals for different purposes
@Register.TIMER(first_interval=0.0, step_interval=0.1, one_time_only=False) # UI updates
def ui_timer():
pass
@Register.TIMER(first_interval=0.0, step_interval=1.0, one_time_only=False) # Status checks
def status_timer():
pass
@Register.TIMER(first_interval=0.0, step_interval=60.0, one_time_only=False) # Periodic saves
def save_timer():
pass
# Avoid: Too frequent intervals that impact performance
# @Register.TIMER(first_interval=0.0, step_interval=0.001, one_time_only=False) # Too fast!
4. Provide Stop Conditions#
@Register.TIMER(first_interval=0.0, step_interval=0.5, one_time_only=False)
def smart_timer():
import bpy
# Stop if addon is being disabled
if not hasattr(bpy.context.scene, 'my_addon_property'):
return -1
# Stop if specific condition is met
if bpy.context.scene.my_addon_property.stop_monitoring:
return -1
# Continue monitoring
monitor_logic()
5. Use Persistence Wisely#
# Persistent: For important background tasks
@Register.TIMER(first_interval=300.0, step_interval=300.0,
one_time_only=False, persistent=True)
def auto_backup():
pass
# Non-persistent: For session-specific tasks
@Register.TIMER(first_interval=1.0, step_interval=1.0,
one_time_only=False, persistent=False)
def session_monitor():
pass
Manual Timer Management#
Creating Timers Programmatically#
from ..addon_utils.register.reg_timer import new_timer
def create_custom_timer():
def my_callback():
print("Custom timer executed")
return -1 # Stop after one execution
timer_handler = new_timer(
callback=my_callback,
first_interval=2.0,
one_time_only=True,
persistent=False
)
return timer_handler
Timer Control Class#
class TimerManager:
def __init__(self):
self.timers = {}
def start_timer(self, name, callback, interval=1.0, recurring=False):
if name in self.timers:
self.stop_timer(name)
self.timers[name] = new_timer(
callback=callback,
first_interval=interval,
step_interval=interval if recurring else 0.1,
one_time_only=not recurring,
persistent=False
)
def stop_timer(self, name):
if name in self.timers and self.timers[name]:
self.timers[name].stop()
del self.timers[name]
def stop_all(self):
for name in list(self.timers.keys()):
self.stop_timer(name)
# Usage
timer_manager = TimerManager()
timer_manager.start_timer("monitor", monitor_function, 0.5, recurring=True)
timer_manager.stop_timer("monitor")
Automatic Cleanup#
The timer registration system automatically handles cleanup:
def register():
# Timers are automatically registered
pass
def unregister():
# All registered timers are automatically stopped and cleaned up
# No manual cleanup required
pass
Performance Considerations#
Avoid very short intervals (< 0.1 seconds) unless necessary
Use timeouts for temporary monitoring to prevent runaway timers
Implement stop conditions to prevent unnecessary processing
Handle exceptions to prevent timer spam in the console
Use persistent timers sparingly to avoid cluttering Blender’s timer system
This timer registration system provides a robust foundation for time-based operations in Blender addons with automatic management and flexible control options.