Proper way to unregister a tool?

This is my first foray into tools- typically I deal strictly with operators, but I have some artists at my studio who are requesting some toolbar tools to facilitate their workflow. My first stop when messing with anything new in blender is the built-in templates to get a basic idea of how a concept works, but the one that ships with Blender 2.93 LTS (ui_tool_simple.py) seems a bit broken? the unregister function is never called, so if you modify the template and re-run it you will immediately get an exception thrown because a tool called ā€˜my_template.my_circle_select’ already exists. I can’t manually unregister a tool because they aren’t registered like a normal class and thus do not appear in bpy.types like operators/panels/menus/literally everything else.

OK, so that’s just an issue with working with a template in the blender text editor- so I set up a bare-bones addon version of the template and treat the tool class exactly like an operator class (caching the object during initialization so it can be referenced during unregister)- but when it’s time to unregister I get an AttributeError exception thrown while calling bpy.utils.unregister_tool saying _bl_tool did not exist (I’m not aware of such an attribute?)

Traceback (most recent call last):
  File "\Text", line 43, in <module>
  File "\Text", line 34, in register
  File "\Text", line 39, in unregister
  File "e:\blender-2.93.0\2.93\scripts\modules\bpy\utils\__init__.py", line 951, in unregister_tool
    tool_def = tool_cls._bl_tool
AttributeError: type object 'MyTool' has no attribute '_bl_tool'

So yeah, anybody have any idea what the proper way to unregister a tool is? Clearly I’m doing something wrong because there are plenty of addons available that use tools.

1 Like

ok for anyone who finds themselves here in the future- I made this little helper function to facilitate removing any tool you like, including built-in tools.

def unregister_tool(idname, space_type, context_mode):
    from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
    cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
    tools = cls._tools[context_mode]
            
    for i, tool_group in enumerate(tools):        
        if isinstance(tool_group, tuple):
            for t in tool_group:
                if 'ToolDef' in str(type(t)) and t.idname == idname:
                    if len(tools[i]) == 1:
                        # it's a group with a single item, just remove it from the tools list.
                        tools.pop(i)
                    else:
                        tools[i] = tuple(x for x in tool_group if x.idname != idname)
                    break
        elif tool_group is not None:
            if tool_group.idname == idname:
                tools.pop(i)
                break
    
    # cleanup any doubled up separators left over after removing a tool
    for i, p in enumerate(reversed(tools)):
        if i < len(tools)-2 and tools[i] is None and tools[i+1] is None:
            tools.pop(i)

usage example, this would remove the built in box select from the edit mode toolbar. if the idname is not found, it just continues silently- so it’s safe to ā€˜unregister’ a tool right before you register it to make sure it’s not there.

unregister_tool('built-in.select_box', 'VIEW_3D', 'EDIT_MESH')
1 Like

hi , your script didnt work with builtin tools.

if __name__ == "__main__":
   unregister_tool('builtin_rotate', 'VIEW_3D' , 'OBJECT')

ā€œbuiltin_rotateā€ is not a tool