如何在Workspace Tools变更时运行bpy回调并添加预绘制钩子?
解决Workspace Tools变更的预绘制钩子延迟问题
核心思路:用消息订阅替代绘制时轮询
放弃在SpaceView3D.draw_handler_add里每次绘制都检查工具变更的方式,改用Blender内置的bpy.msgbus机制直接监听工具切换事件,能在工具变更的第一时间触发回调,彻底解决帧延迟和依赖用户重绘的问题。
具体实现步骤
1. 定义监听的属性路径
Workspace当前激活工具的存储路径是bpy.context.workspace.tools.active,我们直接监听这个属性的变更:
import bpy from bpy.types import Context # 设定要监听的属性 subscribe_target = (bpy.types.WorkSpaceTools, "active")
2. 编写工具变更回调函数
回调里可以直接处理工具切换后的逻辑,包括添加自定义绘制处理器,因为是实时触发,不会有延迟:
def tool_switch_callback(context: Context): # 先清理之前的绘制处理器,避免重复添加 if hasattr(bpy.types.SpaceView3D, "_custom_draw_handler"): bpy.types.SpaceView3D.draw_handler_remove( bpy.types.SpaceView3D._custom_draw_handler, 'WINDOW' ) active_tool = context.workspace.tools.active # 这里可以添加你的业务逻辑,比如根据当前工具加载对应的绘制逻辑 print(f"已切换工具: {active_tool.name}") # 定义自定义绘制逻辑 def custom_draw(): # 示例:在视口左上角绘制当前工具名称 bpy.ops.view3d.text( text=f"当前工具: {active_tool.name}", location=(10, 30), size=12 ) # 添加新的绘制处理器 bpy.types.SpaceView3D._custom_draw_handler = bpy.types.SpaceView3D.draw_handler_add( custom_draw, (), 'WINDOW', 'POST_PIXEL' ) # 主动标记视口重绘,确保绘制内容立即显示 for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw()
3. 注册与取消消息订阅
在插件注册(或脚本运行)时绑定订阅,退出时清理:
def register(): # 注册消息订阅,绑定回调 bpy.msgbus.subscribe_rna( key=subscribe_target, owner=bpy.types.WorkSpaceTools, args=(bpy.context,), notify=tool_switch_callback, ) def unregister(): # 取消订阅 bpy.msgbus.clear_by_owner(bpy.types.WorkSpaceTools) # 清理遗留的绘制处理器 if hasattr(bpy.types.SpaceView3D, "_custom_draw_handler"): bpy.types.SpaceView3D.draw_handler_remove( bpy.types.SpaceView3D._custom_draw_handler, 'WINDOW' ) if __name__ == "__main__": register()
关键优势
- 无延迟触发:工具切换瞬间就调用回调,不需要等待下一帧绘制
- 性能更优:避免了每次绘制都执行检查逻辑的额外开销
- 主动重绘:回调末尾手动触发视口重绘,确保自定义内容立即显示
内容的提问来源于stack exchange,提问作者ThorSummoner
相关产品推荐
相关产品推荐

