Register Properties#

This document describes the property registration system in the UVFlow addon_utils module, which provides a clean way to add custom properties to any Blender type with automatic cleanup and type safety.

Overview#

The property registration system allows you to:

  1. Add properties to any bpy.types class (Object, Scene, WindowManager, etc.)

  2. Batch register multiple properties at once

  3. Automatic cleanup when the addon is disabled

  4. Type safety with comprehensive property types

  5. Integration with operators and UI systems

Basic Property Registration#

Single Property#

Add a single property to any Blender type:

from bpy.types import Object
from ..addon_utils import Register, Property

Register.PROP(Object, 'test_bool', Property.BOOL(name="Test", default=True))

Usage in code:

# Access the property
obj = context.active_object
print(obj.test_bool)  # True
obj.test_bool = False

Multiple Properties (Batch)#

Add several properties at once to the same type:

from bpy.types import Object, WindowManager
from ..addon_utils import Register, Property

Register.PROP_BATCH(WindowManager,
    test_bool = Property.BOOL(),
    test_rgba = Property.COLOR_RGBA(default=(1, 1, 0, 1)),
    test_vector3 = Property.VECTOR_3D(default=(0, 0, 0)),
    test_filepath = Property.FILEPATH(),
    test_pointer_object = Property.POINTER(Object)
)

Comprehensive Property Types#

Basic Types#

from bpy.types import Scene
from ..addon_utils import Register, Property

Register.PROP_BATCH(Scene,
    # Boolean
    enable_feature = Property.BOOL(
        name="Enable Feature",
        description="Enable this awesome feature",
        default=True
    ),
    
    # Integer
    iteration_count = Property.INT(
        name="Iterations",
        description="Number of iterations to perform",
        default=10,
        min=1,
        max=100
    ),
    
    # Float
    scale_factor = Property.FLOAT(
        name="Scale Factor",
        description="Scale factor for the operation",
        default=1.0,
        min=0.1,
        max=10.0,
        precision=2
    ),
    
    # String
    custom_name = Property.STRING(
        name="Custom Name",
        description="Enter a custom name",
        default="Default Name",
        maxlen=64
    )
)

Color Properties#

Register.PROP_BATCH(Scene,
    # RGB Color
    primary_color = Property.COLOR_RGB(
        name="Primary Color",
        description="Primary color for the operation",
        default=(1.0, 0.0, 0.0),  # Red
        subtype='COLOR'
    ),
    
    # RGBA Color (with alpha)
    secondary_color = Property.COLOR_RGBA(
        name="Secondary Color",
        description="Secondary color with transparency",
        default=(0.0, 1.0, 0.0, 0.5),  # Semi-transparent green
        subtype='COLOR'
    )
)

Vector Properties#

Register.PROP_BATCH(Scene,
    # 2D Vector
    uv_offset = Property.VECTOR_2D(
        name="UV Offset",
        description="UV coordinate offset",
        default=(0.0, 0.0),
        subtype='TRANSLATION'
    ),
    
    # 3D Vector
    world_position = Property.VECTOR_3D(
        name="World Position",
        description="Position in world space",
        default=(0.0, 0.0, 0.0),
        subtype='TRANSLATION'
    ),
    
    # Euler angles
    rotation = Property.VECTOR_3D(
        name="Rotation",
        description="Rotation in Euler angles",
        default=(0.0, 0.0, 0.0),
        subtype='EULER'
    )
)

File and Directory Properties#

Register.PROP_BATCH(Scene,
    # File path
    export_filepath = Property.FILEPATH(
        name="Export File",
        description="Path to export file",
        default="",
        subtype='FILE_PATH'
    ),
    
    # Directory path
    asset_directory = Property.DIRPATH(
        name="Asset Directory",
        description="Directory containing assets",
        default="",
        subtype='DIR_PATH'
    )
)

Enum Properties#

Register.PROP_BATCH(Scene,
    # Simple enum
    operation_mode = Property.ENUM(
        name="Operation Mode",
        description="Choose the operation mode",
        items=[
            ('AUTO', 'Automatic', 'Use automatic settings'),
            ('MANUAL', 'Manual', 'Use manual settings'),
            ('ADVANCED', 'Advanced', 'Use advanced settings'),
        ],
        default='AUTO'
    ),
    
    # Enum with icons
    tool_type = Property.ENUM(
        name="Tool Type",
        description="Select the tool type",
        items=[
            ('SELECT', 'Select', 'Selection tool', 'RESTRICT_SELECT_OFF', 0),
            ('MOVE', 'Move', 'Move tool', 'ORIENTATION_GLOBAL', 1),
            ('ROTATE', 'Rotate', 'Rotation tool', 'ORIENTATION_GIMBAL', 2),
            ('SCALE', 'Scale', 'Scale tool', 'FULLSCREEN_EXIT', 3),
        ],
        default='SELECT'
    )
)

Pointer Properties#

from bpy.types import Object, Material, Image

Register.PROP_BATCH(Scene,
    # Object pointer
    target_object = Property.POINTER(
        type=Object,
        name="Target Object",
        description="Object to operate on"
    ),
    
    # Material pointer
    active_material = Property.POINTER(
        type=Material,
        name="Active Material",
        description="Material to use"
    ),
    
    # Image pointer
    reference_image = Property.POINTER(
        type=Image,
        name="Reference Image",
        description="Reference image for the operation"
    )
)

Collection Properties#

from bpy.types import PropertyGroup

# First, define a property group
@Register.PROP_GROUP
class CustomItem(PropertyGroup):
    name: Property.STRING(default="Item")
    value: Property.FLOAT(default=0.0)
    enabled: Property.BOOL(default=True)

# Then use it in a collection
Register.PROP_BATCH(Scene,
    custom_items = Property.COLLECTION(
        type=CustomItem,
        name="Custom Items",
        description="Collection of custom items"
    ),
    
    # Index for the collection
    custom_items_index = Property.INT(
        name="Active Item Index",
        default=0,
        min=0
    )
)

Common Target Types#

Scene Properties#

Global settings that persist with the blend file:

from bpy.types import Scene

Register.PROP_BATCH(Scene,
    global_scale = Property.FLOAT(default=1.0),
    export_format = Property.ENUM(
        items=[('OBJ', 'OBJ', ''), ('FBX', 'FBX', '')],
        default='OBJ'
    )
)

# Access: bpy.context.scene.global_scale

Object Properties#

Properties specific to individual objects:

from bpy.types import Object

Register.PROP_BATCH(Object,
    custom_id = Property.STRING(default=""),
    is_processed = Property.BOOL(default=False),
    processing_data = Property.VECTOR_3D(default=(0, 0, 0))
)

# Access: obj.custom_id, obj.is_processed, obj.processing_data

WindowManager Properties#

Temporary properties that don’t save with the file:

from bpy.types import WindowManager

Register.PROP_BATCH(WindowManager,
    temp_value = Property.FLOAT(default=0.0),
    ui_state = Property.ENUM(
        items=[('NORMAL', 'Normal', ''), ('ADVANCED', 'Advanced', '')],
        default='NORMAL'
    )
)

# Access: bpy.context.window_manager.temp_value

Mesh Properties#

Properties on mesh data:

from bpy.types import Mesh

Register.PROP_BATCH(Mesh,
    uv_scale = Property.FLOAT(default=1.0),
    seam_data = Property.STRING(default="")
)

# Access: obj.data.uv_scale (where obj.type == 'MESH')

Property Integration with Operators#

Using Properties in Operators#

@Register.OPS.INVOKE_PROPS
class ProcessWithSceneSettings:
    # Operator properties
    override_scale: Property.BOOL(
        name="Override Scale",
        description="Override scene scale setting",
        default=False
    )
    
    custom_scale: Property.FLOAT(
        name="Custom Scale",
        description="Custom scale value",
        default=1.0
    )
    
    def action(self, context):
        # Access scene properties
        scene_scale = context.scene.global_scale
        
        # Use operator properties
        if self.override_scale:
            scale = self.custom_scale
        else:
            scale = scene_scale
        
        # Process with the scale value
        return {'FINISHED'}

Property-Driven UI#

@Register.UI.PANEL.VIEW3D
class CustomPropertiesPanel:
    label = "Custom Properties"
    
    def draw_ui(self, context, layout):
        scene = context.scene
        
        # Draw scene properties
        layout.prop(scene, "global_scale")
        layout.prop(scene, "export_format")
        
        # Conditional UI based on properties
        if scene.export_format == 'FBX':
            layout.prop(scene, "fbx_specific_setting")
        
        # Object properties (if object selected)
        obj = context.active_object
        if obj:
            box = layout.box()
            box.label(text="Object Properties:")
            box.prop(obj, "custom_id")
            box.prop(obj, "is_processed")

Best Practices#

1. Use Descriptive Names#

# Good
Register.PROP(Scene, 'uv_unwrap_angle_limit', Property.FLOAT(default=66.0))

# Avoid
Register.PROP(Scene, 'val1', Property.FLOAT(default=66.0))

2. Provide Clear Descriptions#

Register.PROP(Scene, 'export_scale',
    Property.FLOAT(
        name="Export Scale",
        description="Scale factor applied during export (1.0 = no scaling)",
        default=1.0,
        min=0.001,
        max=1000.0
    )
)

3. Set Appropriate Limits#

# For percentages
Register.PROP(Scene, 'opacity',
    Property.FLOAT(
        default=1.0,
        min=0.0,
        max=1.0,
        subtype='FACTOR'  # Shows as percentage in UI
    )
)

# For angles
Register.PROP(Scene, 'rotation_angle',
    Property.FLOAT(
        default=0.0,
        subtype='ANGLE'  # Shows with degree symbol
    )
)

4. Choose Appropriate Target Types#

# Persistent settings → Scene
Register.PROP(Scene, 'project_settings', Property.STRING())

# Temporary UI state → WindowManager  
Register.PROP(WindowManager, 'ui_mode', Property.ENUM(...))

# Object-specific data → Object
Register.PROP(Object, 'object_id', Property.STRING())

5. Use Property Groups for Complex Data#

@Register.PROP_GROUP
class ExportSettings(PropertyGroup):
    format: Property.ENUM(items=[...])
    scale: Property.FLOAT(default=1.0)
    apply_modifiers: Property.BOOL(default=True)

Register.PROP(Scene, 'export_settings', Property.POINTER(type=ExportSettings))

# Access: context.scene.export_settings.format

Automatic Cleanup#

The property registration system automatically handles cleanup:

def register():
    # Properties are automatically registered
    pass

def unregister():
    # Properties are automatically removed from target types
    # No manual cleanup required
    pass

This property registration system provides a robust foundation for adding custom data to Blender types with minimal code and automatic management.