You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blender中bpy.app.timers注册与注销异常问题修复咨询

修复Blender定时器注销时的ValueError问题

问题根源

出现Error: function is not registered,大概率是这俩原因:

  1. 注销的函数和之前注册的不是同一个引用——比如用了Operator类的实例方法,每次点按钮都会生成新的Operator实例,方法引用自然不一样;或者注册的是局部函数,后续找不到同一个对象。
  2. 定时器已经自行停止——比如你的定时器函数返回了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:13:17