Blender中bpy.app.timers注册与注销异常问题修复咨询
修复Blender定时器注销时的ValueError问题
问题根源
出现Error: function is not registered,大概率是这俩原因:
- 注销的函数和之前注册的不是同一个引用——比如用了Operator类的实例方法,每次点按钮都会生成新的Operator实例,方法引用自然不一样;或者注册的是局部函数,后续找不到同一个对象。
- 定时器已经自行停止——比如你的定时器函数返回了
False/None,或者抛出了异常,Blender会自动注销它,这时候再调用unregister肯定报错。
具体修复步骤
1. 把定时器函数改成全局独立函数
别用Operator类里的方法当定时器函数,改成全局的,确保注册和注销的是同一个对象。
修改ba_ops.py的示例代码:
import bpy # 全局变量存储定时器状态和更新间隔 _timer_active = False _update_gap = 0.1 def refresh_view3d(): # 这里写你的View3D更新逻辑,比如强制刷新视图 for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw() # 返回间隔时间继续运行,返回None则停止定时器 return _update_gap if _timer_active else None class BA_OT_StartTimer(bpy.types.Operator): bl_idname = "ba.start_timer" bl_label = "启动视图更新定时器" def execute(self, context): global _timer_active if not _timer_active: _timer_active = True bpy.app.timers.register(refresh_view3d) self.report({'INFO'}, "定时器已启动") return {'FINISHED'} class BA_OT_StopTimer(bpy.types.Operator): bl_idname = "ba.stop_timer" bl_label = "停止视图更新定时器" def execute(self, context): global _timer_active if _timer_active: _timer_active = False try: bpy.app.timers.unregister(refresh_view3d) self.report({'INFO'}, "定时器已停止") except ValueError: # 处理定时器已经自动停止的情况 self.report({'WARNING'}, "定时器本来就已经停了") _timer_active = False return {'FINISHED'}
2. 若必须用类方法,需保存绑定引用
如果一定要用Operator类的方法,得把绑定的方法引用存起来,避免每次使用新的实例方法。示例:
import bpy class BA_OT_TimerController(bpy.types.Operator): bl_idname = "ba.timer_controller" bl_label = "定时器控制器" _bound_func = None _is_running = False def update_view(self): # 视图更新逻辑 for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw() return 0.1 if self._is_running else None def execute(self, context): if not self._is_running: self._bound_func = self.update_view bpy.app.timers.register(self._bound_func) self._is_running = True self.report({'INFO'}, "定时器启动") else: try: bpy.app.timers.unregister(self._bound_func) self.report({'INFO'}, "定时器停止") except ValueError: self.report({'WARNING'}, "定时器已停止") self._bound_func = None self._is_running = False return {'FINISHED'}
3. 注销时强制捕获异常
不管用哪种方式,注销定时器时都加try-except块,避免因定时器已自动停止而触发报错。
4. 配合属性和UI管理状态
在ba_props.py中添加全局属性跟踪定时器状态:
import bpy def register(): bpy.types.Scene.ba_timer_running = bpy.props.BoolProperty( name="定时器运行中", default=False, description="标记视图更新定时器是否激活" ) def unregister(): del bpy.types.Scene.ba_timer_running
然后在ba_ui.py中添加面板按钮(记得在Operator的execute方法里同步更新scene.ba_timer_running的值):
import bpy class BA_PT_TimerPanel(bpy.types.Panel): bl_label = "视图更新定时器" bl_idname = "BA_PT_timer_panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "BA工具" def draw(self, context): layout = self.layout scene = context.scene if not scene.ba_timer_running: layout.operator("ba.start_timer") else: layout.operator("ba.stop_timer")
重点提醒
- 定时器函数必须是全局可调用的,不能是
execute内部定义的局部函数,否则Blender无法跟踪到引用。 - 注册和注销必须使用同一个函数对象,别每次注册都生成新的函数(比如用lambda或者每次实例化的方法)。
- 停止定时器时,要么让函数返回
None/False,要么调用unregister,不要同时操作。
内容的提问来源于stack exchange,提问作者aum hren
相关产品推荐
相关产品推荐

