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:

  1. Backs up the original UI class methods

  2. Replaces them with your custom implementations

  3. Adds polling logic to conditionally execute your override

  4. Provides fallback to original behavior when poll fails

  5. 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 active

  • Method Signature: Override methods receive (self, context, layout) for draw methods

  • Fallback Behavior: When poll returns False, original methods are called

  • Multiple 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:

  1. Wraps the original function with custom logic

  2. Executes pre-function hooks before the original

  3. Calls the original function

  4. Executes post-function hooks after the original

  5. Preserves return values and handles early returns

  6. 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 called

  • Exception 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:#

  1. Always provide poll functions for UIOverride to avoid unnecessary overrides

  2. Keep override logic minimal to maintain performance

  3. Test fallback behavior when poll returns False

  4. Document your overrides clearly for maintenance

  5. 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.