UI Append Utility#
This document describes the UI append utility that allows you to add custom draw functions to existing Blender UI classes (Menus, Panels, Headers) without overriding them completely.
Overview#
The Register.UI.APPEND
decorator provides a clean way to extend existing Blender UI elements by appending or prepending custom draw functions. This is particularly useful when you want to add menu items, buttons, or other UI elements to built-in Blender menus and panels.
How it Works#
The UI append system:
Registers your draw function to be called when the target UI class draws
Appends or prepends your function to the existing draw calls
Preserves all original UI functionality
Automatically manages registration and cleanup
Handles function signature conversion for Blender’s UI system
Basic Usage#
Real-world Example from UVFlow#
Here’s how UVFlow uses the append utility to add a submenu to the 3D View object menu:
import bpy
from bpy.types import UILayout, Context
from ..addon_utils import Register
from ..operators.op_checker import ToggleUvCheckerMaterial
from ..operators.op_material import MaterialSelectObjects, MaterialSelectFaces
from ..operators.op_unwrap import UVUnwrap
from ..operators.op_pack import UVPack
from ..operators.op_texel_density import UVSetScale
# First, create a custom menu class
@Register.UI.MENU
class VIEW3D_MT_object_uvflow:
label = 'UV Flow'
def draw_ui(self, context: Context, layout: UILayout) -> None:
UVUnwrap.draw_in_layout(layout, label="Unwrap UVs")
if bpy.app.version > (3, 6, 0):
UVPack.draw_in_layout(layout, label="Pack UVs")
UVSetScale.draw_in_layout(layout)
layout.separator()
ToggleUvCheckerMaterial.draw_in_layout(layout, label='Enable UV Checkers',
op_props={'enable': True, 'auto': False})
ToggleUvCheckerMaterial.draw_in_layout(layout, label='Disable UV Checkers',
op_props={'enable': False, 'auto': False})
layout.separator()
MaterialSelectObjects.draw_in_layout(layout, label='Select Material Objects')
MaterialSelectFaces.draw_in_layout(layout, label='Select Material Faces')
# Then, append the submenu to the existing object menu
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object)
def draw_uvflow_menu(context: Context, layout: UILayout) -> None:
layout.separator()
VIEW3D_MT_object_uvflow.draw_in_layout(layout, label="UVFlow")
Function Signature#
Your append function must follow this signature:
def your_draw_function(context: Context, layout: UILayout) -> None:
# Your UI drawing code here
pass
Parameters:#
context: The current Blender context
layout: The UI layout object to draw into
Return: Must return
None
Common Use Cases#
Adding to Panels#
@Register.UI.APPEND(bpy.types.VIEW3D_PT_tools_active)
def draw_panel_extension(context: Context, layout: UILayout) -> None:
if context.mode == 'EDIT_MESH':
box = layout.box()
box.label(text="Custom Tools")
box.operator("mesh.custom_operator")
Append vs Prepend#
Append (Default)#
Adds your content after the original UI content
Most common use case
Good for adding supplementary tools
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object) # Appends by default
def draw_at_bottom(context: Context, layout: UILayout) -> None:
layout.operator("my_addon.tool")
Prepend#
Adds your content before the original UI content
Useful for priority tools or important actions
Use sparingly to avoid disrupting user expectations
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object, prepend=True)
def draw_at_top(context: Context, layout: UILayout) -> None:
layout.operator("my_addon.priority_tool")
layout.separator()
Best Practices#
1. Use Separators Appropriately#
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object)
def draw_with_separator(context: Context, layout: UILayout) -> None:
layout.separator() # Add visual separation
layout.operator("my_addon.tool")
3. Use Context-Sensitive Drawing#
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object)
def draw_context_sensitive(context: Context, layout: UILayout) -> None:
obj = context.active_object
if not obj:
return
layout.separator()
if obj.type == 'MESH':
layout.operator("my_addon.mesh_tool")
elif obj.type == 'CURVE':
layout.operator("my_addon.curve_tool")
4. Provide Clear Labels and Icons#
@Register.UI.APPEND(bpy.types.VIEW3D_MT_object)
def draw_with_icons(context: Context, layout: UILayout) -> None:
layout.separator()
layout.operator("my_addon.unwrap", text="Smart Unwrap", icon='MOD_UVPROJECT')
layout.operator("my_addon.pack", text="Pack UVs", icon='PACKAGE')
Common Target UI Classes#
Panels#
bpy.types.VIEW3D_PT_tools_active
- Active tool panelbpy.types.PROPERTIES_PT_context_object
- Object properties panel
Automatic Cleanup#
The append system automatically handles registration and cleanup:
def register():
# UI appends are automatically registered
pass
def unregister():
# UI appends are automatically unregistered
pass
The system maintains a list of all appended functions and properly removes them when the addon is disabled, ensuring no leftover UI elements remain.
Error Handling#
The append system is robust and handles common issues:
Missing target class: Gracefully skips if the target UI class doesn’t exist
Function errors: Isolates your function errors from breaking the original UI
Cleanup: Ensures proper removal even if errors occur during registration
Limitations#
Function signature: Must match the expected
(context, layout)
signatureOrder dependency: Multiple appends to the same target are processed in registration order
This append utility provides a clean, maintainable way to extend Blender’s UI without the complexity of full UI overrides, making it perfect for adding addon-specific tools and menus to existing Blender interfaces.