Search code examples
event-handlingworkspacebpy

how to run bpy callback on workspace tools change


How to add a pre-draw hook to current context workspace.tools change?

I attempted to get there using bpy.types.SpaceView3D.draw_handler_add(...) which as it runs on every draw, checks if workspace.tools changed, and if it changed, run my callback, but my callback wants to add its own SpaceView3D.draw_handler_add and doing it this way adds it a frame-too-late, leaving the view port undrawn until a user event repaints the screen.


Solution

  • I found this post online https://devtalk.blender.org/t/update-property-when-active-tool-changes/11467/12

    summary: maybe there is a mainline callback new https://developer.blender.org/D10635

    AFWS Jan '20

    @kaio

    This seem like a better solution. It’s kind of a mystery code, cause I couldn’t figure out where you got that code info ,but then started looking at the space_toolsystem_common.py file. kaio AFWS Jan '20

    Just realized there might be a cleaner way of getting callbacks for active tools using the msgbus. Since workspace tools aren’t rna properties themselves, figured it’s possible to monitor the bpy_prop_collection which changes with the tool.

    The handle is the workspace itself, so shouldn’t have to worry about keeping a reference. The subscription lasts until a new file is loaded, so add a load_post callback which reapplies it.

    Note this doesn’t proactively subscribe to workspaces added afterwards. Might need a separate callback for that :joy:

    import bpy
    
    def rna_callback(workspace):
        idname = workspace.tools[-1].idname
        print(idname)
    
    def subscribe(workspace):
        bpy.msgbus.subscribe_rna(
            key=ws.path_resolve("tools", False),
            owner=workspace,
            args=(workspace,),
            notify=rna_callback)
    
    if __name__ == "__main__":
        ws = bpy.context.workspace
        subscribe(bpy.context.workspace)
    
    # Subscribe to all workspaces: if 0:
        for ws in bpy.data.workspaces:
            subscribe(bpy.context.workspace)
    
    # Clear all workspace subscriptions if 0:
        for ws in bpy.data.workspaces:
            bpy.msgbus.clear_by_owner(ws)