UI Override Methods#
This document describes two powerful methods for overriding Blender’s built-in UI classes and functions using the UVFlow addon utilities.
Method 1: UIOverride for UI Classes#
The UIOverride
system allows you to override methods from built-in Blender UI classes (like Panels, Menus, Headers) with conditional logic. This is accessible through the Register.UI.OVERRIDE
decorator.
How it Works#
The UIOverride system:
Backs up the original UI class methods
Replaces them with your custom implementations
Adds polling logic to conditionally execute your override
Provides fallback to original behavior when poll fails
Restores original methods on addon unregister
Basic Usage#
from bpy.types import Context, UILayout
from bl_ui.space_statusbar import STATUSBAR_HT_header
from ...addon_utils import Register
from ...tool.polling import poll_uvflow_tool
@Register.UI.OVERRIDE(STATUSBAR_HT_header, poll=poll_uvflow_tool)
class UVFLOW_STATUSBAR_HT_header:
def draw(self, context: Context, layout: UILayout):
# Your custom draw implementation
layout.label(text='UVFlow Tool is active')
layout.separator_spacer()
# You can still call original UI elements
layout.template_reports_banner()
layout.template_running_jobs()
layout.separator_spacer()
row = layout.row()
row.alignment = 'RIGHT'
layout.template_status_info()
Key Features#
Conditional Override: The
poll
function determines when your override is activeMethod Signature: Override methods receive
(self, context, layout)
for draw methodsFallback Behavior: When poll returns
False
, original methods are calledMultiple Methods: You can override multiple methods in the same class
Automatic Cleanup: Original methods are restored when the addon unregisters
Advanced Example#
from bpy.types import Context, UILayout, Menu
from bl_ui.space_view3d import VIEW3D_MT_mesh
from ...addon_utils import Register
def poll_edit_mode(context):
return context.mode == 'EDIT_MESH'
@Register.UI.OVERRIDE(VIEW3D_MT_mesh, poll=poll_edit_mode)
class UVFLOW_VIEW3D_MT_mesh:
def draw(self, context: Context, layout: UILayout):
# Add custom menu items at the top
layout.operator("uvflow.custom_operator")
layout.separator()
# Call original draw to preserve existing menu items
# Note: This would need to be implemented in the override system
# Currently, you need to manually recreate desired original content
@classmethod
def poll(cls, context: Context):
# Override the poll method as well
return context.active_object and context.active_object.type == 'MESH'
Method 2: Function Wrapping with wrap_function#
The wrap_function
utility provides a more granular approach to override any Blender API function with pre/post execution hooks.
How it Works#
The wrap_function system:
Wraps the original function with custom logic
Executes pre-function hooks before the original
Calls the original function
Executes post-function hooks after the original
Preserves return values and handles early returns
Restores original function on cleanup
Basic Usage#
from bl_ui.space_toolsystem_common import activate_by_id
from ..utils.override_helpers import wrap_function
def on_tool_switch_post(context, space_type, item_idname, *, as_fallback=False):
"""Called after tool activation"""
print(f"Tool switched to: {item_idname}")
# Your post-activation logic here
def register():
# Wrap the activate_by_id function with a post-execution hook
wrap_function(activate_by_id, post_func=on_tool_switch_post)
Function Signature Requirements#
Pre-function: Must accept the same arguments as the original function
Post-function: Must accept the same arguments as the original function
Return Handling: If original function returns
False
, post-function is not calledException Safety: Wrapper preserves original function behavior
Real-world Example from UVFlow#
def on_tool_switch_post(context: Context, space_type: str | None, item_idname: str | None, *, as_fallback=False) -> None:
"""Handle tool switching logic"""
if space_type != 'VIEW_3D':
return
act_ob = context.active_object
if not act_ob or act_ob.type != 'MESH' or act_ob.mode != 'EDIT':
return
global last_active_tool
if item_idname == UVFlowTool.bl_idname:
# Tool activated - enable overlays
last_active_tool = item_idname
toggle_overlays(context, enable=True)
elif last_active_tool == UVFlowTool.bl_idname:
# Tool deactivated - disable overlays
toggle_overlays(context, enable=False)
last_active_tool = None
def register():
wrap_function(activate_by_id, post_func=on_tool_switch_post)
When to Use Each Method#
Use UIOverride When:#
Overriding UI class methods (draw, poll, etc.)
Need conditional override based on context
Want to modify existing UI panels, menus, or headers
Need to preserve original UI behavior as fallback
Use wrap_function When:#
Overriding any Blender API function
Need to add pre/post execution logic
Want to monitor function calls
Need to modify behavior of internal functions
Cleanup and Best Practices#
Both systems automatically handle cleanup:
def unregister():
# UIOverride cleanup is handled automatically by reg_ui.py
# wrap_function cleanup is handled by override_helpers.py
pass
Best Practices:#
Always provide poll functions for UIOverride to avoid unnecessary overrides
Keep override logic minimal to maintain performance
Test fallback behavior when poll returns False
Document your overrides clearly for maintenance
Use specific polls to avoid potential conflicts with other addons
Error Handling#
Both systems include error handling:
UIOverride: Falls back to original methods if override fails
wrap_function: Preserves original function behavior and exceptions
Cleanup: Both systems restore original state on addon disable
This ensures your addon doesn’t break Blender’s core functionality even if something goes wrong with your overrides.