Icons System#
This is the developers guide to cover the UVFlow icons system architecture, implementation details, and how to extend it.
Overview#
The UVFlow icons system provides automatic icon loading, code generation, and easy access to icons for UI and GPU drawing. It’s built on top of Blender’s preview system with additional GPU texture support for custom rendering.
Architecture Overview#
Core Components#
addon_utils/auto_code/ # Auto-code module
├── icons.py # Core icon system implementation
└── __init__.py
---
├── icons.py # Auto-generated icon access classes
└── assets/icons/ # Icon source directory
├── icons_data # Cache database (auto-generated)
└── [category]/ # Category folders
Automatic System Flow#
Icon Discovery: Scans
assets/icons/
directory (or the specified - relative to the addon source path - directory) for image files, including.png
,.jpg
,.jpeg
files.Metadata Processing: Creates
IconData
objects with smart namingCode Generation: Uses templates to generate
icons.py
(or the specified filename) with enum classesLazy Loading: Icons loaded only when accessed (example, user opens an addon panel that uses an icon)
Caching: Preview collections and GPU textures cached for performance
Class Hierarchy#
IconsViewer # Base class for icon preview functionality
├── Icons # Auto-generated main class
├── MAIN(IconsEnum) # Root directory icons
├── TOOLS(IconsEnum) # tools/ directory icons
└── [CATEGORY](IconsEnum) # Custom category directories
IconsEnum # Base enum for icon collections
├── collection property # Collection name
├── filepath property # Full path to icon file
├── icon_id property # Blender preview icon ID
├── gputex property # GPU texture for custom drawing
└── draw_in_layout() method # UI drawing helper
IconData # Internal metadata class
├── Smart name processing # Converts filenames to identifiers
├── Modification tracking # Detects file changes
└── Path management # Handles file paths
Adding New Icons to the Addon#
Basic Icon Addition#
Place icon files in the appropriate directory:
assets/icons/
├── tools/
│ └── my_new_tool.png # Add your icon here
├── ui/
│ └── custom_button.jpg # Or create new categories
└── logo.png # Root directory for main icons
Regenerate icons code:
Nothing to worry about! When reloading the addon, the icons code will be regenerated automatically as it will detect the changes in the icons directory.
Use the new icon:
from uvflow.icons import Icons
# Icon is automatically available
icon_id = Icons.TOOLS.MY_NEW_TOOL.icon_id
Icons.UI.CUSTOM_BUTTON.draw_in_layout(layout)
Supported File Formats#
PNG (recommended for UI icons)
JPG/JPEG
File size: depends on the use case. If for UI, Recommended 64x64 to 256x256 pixels. If for GPU drawing, Recommended 100x100 to 512x512 pixels.
Naming: Use descriptive names, special characters will be cleaned automatically. Check naming convention below.
Smart Naming Convention#
The system automatically converts filenames to valid Python identifiers:
# Filename → Generated Identifier
"[prefix] My Icon.png" → MY_ICON
"(category) tool-name.jpg" → TOOL_NAME
"icon.with.dots.png" → ICON_WITH_DOTS
"special chars!@#.png" → SPECIAL_CHARS
Recommended convention:
# Use lowercase letters and underscores between words!
"my_icon.png" → MY_ICON
"my_icon.jpg" → MY_ICON
"my_icon.jpeg" → MY_ICON
Custom Icon Categories and Organization#
Creating New Categories#
Categories are automatically created based on folder structure, as easy as that!
assets/icons/
├── tools/ # → Icons.TOOLS
├── ui_elements/ # → Icons.UI_ELEMENTS
├── materials/ # → Icons.MATERIALS
├── custom_category/ # → Icons.CUSTOM_CATEGORY
└── main_icons.png # → Icons.MAIN
Nested Categories#
The system ONLY supports ONE level of nesting:
assets/icons/
├── tools/
│ ├── modeling/ # Files here go to Icons.TOOLS
│ │ └── tool.png # → Icons.TOOLS.TOOL (with path: tools/modeling/tool.png)
│ └── sculpting/ # Files here go to Icons.TOOLS
│ └── brush.png # → Icons.TOOLS.BRUSH (with path: tools/sculpting/brush.png)
Category Best Practices#
1. Logical Organization#
# Good: Clear category purposes
assets/icons/
├── tools/ # Tool-specific icons
├── ui/ # UI element icons
├── materials/ # Material preview icons
└── status/ # Status indicator icons
# Avoid: Mixed purposes
assets/icons/
├── misc/ # Too generic
├── stuff/ # Unclear purpose
2. Consistent Naming#
# Good: Consistent naming within categories
tools/
├── cut_uv.png
├── unwrap_tool.png
├── pack_islands.png
# Avoid: Inconsistent naming
tools/
├── CutUV.png
├── unwrap-tool.jpg
├── PackIslands.PNG
3. Icon Sizing#
# Recommended sizes for different uses
UI_ICONS = (32, 32) # Small UI elements
TOOL_ICONS = (64, 64) # Tool buttons
PREVIEW_ICONS = (128, 128) # Material/object previews
GPU_ICONS = (256, 256) # Custom GPU drawing
API Documentation#
IconsEnum Class#
Base enum class for all icon collections.
class IconsEnum(Enum):
"""Base enum class for icon collections with automatic loading and caching."""
Properties#
collection: str
#
@property
def collection(self) -> str:
"""Get the collection name (class name)."""
return self.__class__.__name__
filepath: str | None
#
@property
def filepath(self) -> str | None:
"""Get the full file path to the icon."""
path: Path = GLOBALS.ADDON_SOURCE_PATH / self.value
if path.exists() and path.is_file():
return str(path)
return None
identifier: str
#
@property
def identifier(self) -> str:
"""Get the unique identifier for this icon."""
return self.name
filename: str
#
@property
def filename(self) -> str:
"""Get the filename of the icon."""
return os.path.basename(self.filepath)
icon_id: int
#
@property
def icon_id(self) -> int:
"""Get the Blender preview icon ID for UI usage."""
collection: previews.ImagePreviewCollection = icon_previews.get(self.collection, None)
if collection is None:
collection: previews.ImagePreviewCollection = previews.new()
icon_previews[self.collection] = collection
elif preview := collection.get(self.identifier, None):
return preview.icon_id
filepath = self.filepath
if filepath is None:
return 0
return collection.load(self.identifier, filepath, 'IMAGE', force_reload=True).icon_id
gputex: GPUTexture
#
@property
def gputex(self) -> GPUTexture:
"""Get the GPU texture for custom drawing operations."""
# Loads and caches GPU texture at GPUTEX_ICON_SIZE (100x100)
# Returns cached texture if already loaded
# Creates new texture from image data if not cached
Methods#
draw_in_layout(layout, scale=2.0)
#
def draw_in_layout(self, layout: UILayout, scale: float = 2.0):
"""Draw the icon in a Blender UI layout.
Args:
layout (UILayout): Blender UI layout to draw in
scale (float): Icon scale factor (default: 2.0)
"""
if icon_id := self.icon_id:
layout.template_icon(icon_id, scale=scale)
__call__() -> tuple[str, str]
#
def __call__(self) -> tuple[str, str]:
"""Get identifier and filename as tuple."""
return self.identifier, self.filename
IconsViewer Class#
Base class for icon preview functionality.
class IconsViewer:
"""Base class providing icon preview and organization functionality."""
Class Methods#
draw_icons_in_layout(layout)
#
@classmethod
def draw_icons_in_layout(cls, layout: UILayout):
"""Draw all icon collections in a layout for preview/debugging.
Args:
layout (UILayout): Blender UI layout to draw in
Creates organized boxes showing all available icons grouped by collection.
"""
box = layout.box()
box.label(text="I c o n s P r e v i e w s :")
for icons_enum in cls.__dict__.values():
if inspect.isclass(icons_enum):
collection = box.column(align=True)
collection.box().row(align=True).label(text="Collection '%s'" % icons_enum.__name__)
grid_layout = collection.box().column(align=True).grid_flow(columns=0)
for icon in icons_enum:
icon.draw_in_layout(grid_layout)
IconData Class#
Internal class for icon metadata management.
class IconData:
"""Internal class for managing icon metadata and processing."""
def __init__(self, icon_path: Path):
"""Initialize icon data from file path.
Args:
icon_path (Path): Path to the icon file
"""
Properties#
idname: str
#
Smart-processed identifier name with special character cleanup.
name: str
#
Original filename.
ext: str
#
File extension.
date: float
#
File modification timestamp for cache invalidation.
filepath: str
#
Full path to the icon file.
Code Generation Functions#
generate_icons_py(filename='icons.py', _icons_path=None)
#
def generate_icons_py(filename: str = 'icons.py', _icons_path: Optional[str] = None):
"""Generate the icons.py file with automatic icon discovery.
Args:
filename (str): Output filename (default: 'icons.py')
_icons_path (str, optional): Custom icons directory path
Features:
- Scans icons directory for supported formats
- Creates category-based enum classes
- Tracks file modifications for incremental updates
- Generates clean, type-safe Python code
"""
Usage Patterns#
Basic Icon Usage#
from uvflow.icons import Icons
# Get icon for UI elements
@Register.UI.PANEL.VIEW3D
class MyPanel:
def draw_ui(self, context, layout):
# Use icon in operator
layout.operator("uvflow.my_tool",
icon_value=Icons.TOOLS.MY_TOOL.icon_id)
# Draw icon directly
Icons.TOOLS.MY_TOOL.draw_in_layout(layout, scale=1.5)
Custom GPU Drawing#
import gpu
from gpu_extras.batch import batch_for_shader
def draw_custom_overlay():
"""Example of using icons in custom GPU drawing."""
texture = Icons.UI.OVERLAY_ICON.gputex
# Create shader and batch
shader = gpu.shader.from_builtin('2D_IMAGE')
vertices = [(0, 0), (100, 0), (100, 100), (0, 100)]
uvs = [(0, 0), (1, 0), (1, 1), (0, 1)]
batch = batch_for_shader(shader, 'TRI_FAN', {
"pos": vertices,
"texCoord": uvs
})
# Draw with icon texture
shader.bind()
shader.uniform_sampler("image", texture)
batch.draw(shader)
Icon Browser Panel#
@Register.UI.PANEL.VIEW3D
class IconBrowserPanel:
label = "Icon Browser"
def draw_ui(self, context, layout):
# Draw all available icons for debugging/preview
Icons.draw_icons_in_layout(layout)
Performance Considerations#
Caching Strategy#
Preview Collections: Cached per category, loaded on first access
GPU Textures: Cached individually, created on demand
File Modification: Tracked to invalidate cache when files change
Memory Management: Automatic cleanup on addon disable
Optimization Tips#
# Good: Access icon_id once and reuse
icon_id = Icons.TOOLS.MY_TOOL.icon_id
for i in range(100):
layout.operator("my.op", icon_value=icon_id)
# Avoid: Repeated property access
for i in range(100):
layout.operator("my.op", icon_value=Icons.TOOLS.MY_TOOL.icon_id)
Handling Issues#
Potential Issues#
Invalid File Formats#
# Only PNG, JPG, JPEG are supported!
# Other formats will be ignored during generation
Permission Issues#
# Ensure write permissions for:
# - assets/icons/ directory
# - icons.py file
# - icons_data cache file
Debugging#
# Enable debug output
from uvflow.addon_utils.debug import print_debug
# Check icon loading
print_debug("Icon filepath:", Icons.TOOLS.MY_TOOL.filepath)
print_debug("Icon ID:", Icons.TOOLS.MY_TOOL.icon_id)
# Preview all icons
Icons.draw_icons_in_layout(layout) # Shows all loaded icons
This icons system provides a robust foundation for managing addon icons with minimal setup and maximum flexibility. The automatic code generation and caching ensure good performance while the enum-based interface provides type safety and ease of use.