""" Icon Management System ===================== Comprehensive icon system with SVG icons, Material Design icons, and utility functions for the Traffic Monitoring Application. Features: - Material Design icon set - SVG icon generation - Icon theming and colorization - Size variants and scaling - Custom icon registration """ from PySide6.QtGui import QIcon, QPixmap, QPainter, QColor, QBrush, QPen from PySide6.QtCore import Qt, QSize from PySide6.QtSvg import QSvgRenderer from typing import Dict, Optional, Tuple import base64 from io import BytesIO class IconTheme: """Icon theme management""" # Icon colors for dark theme PRIMARY = "#FFFFFF" SECONDARY = "#B0B0B0" ACCENT = "#00BCD4" SUCCESS = "#4CAF50" WARNING = "#FF9800" ERROR = "#F44336" INFO = "#2196F3" class SVGIcons: """Collection of SVG icons as base64 encoded strings""" # Navigation icons HOME = """ """ PLAY = """ """ PAUSE = """ """ STOP = """ """ RECORD = """ """ # Detection and monitoring icons CAMERA = """ """ MONITOR = """ """ TRAFFIC_LIGHT = """ """ VIOLATION = """ """ # Analytics and statistics icons CHART_BAR = """ """ CHART_LINE = """ """ CHART_PIE = """ """ DASHBOARD = """ """ # System and settings icons SETTINGS = """ """ EXPORT = """ """ IMPORT = """ """ SAVE = """ """ # Status and alert icons CHECK_CIRCLE = """ """ WARNING_CIRCLE = """ """ ERROR_CIRCLE = """ """ INFO_CIRCLE = """ """ # Action icons REFRESH = """ """ DELETE = """ """ EDIT = """ """ FILTER = """ """ SEARCH = """ """ class IconManager: """Manages icons for the application""" def __init__(self): self._icon_cache: Dict[str, QIcon] = {} self.theme = IconTheme() def get_icon(self, name: str, color: str = IconTheme.PRIMARY, size: int = 24) -> QIcon: """Get an icon by name with specified color and size""" cache_key = f"{name}_{color}_{size}" if cache_key in self._icon_cache: return self._icon_cache[cache_key] # Get SVG content svg_content = getattr(SVGIcons, name.upper(), None) if not svg_content: return QIcon() # Return empty icon if not found # Replace currentColor with specified color svg_content = svg_content.replace('currentColor', color) # Create icon from SVG icon = self._create_icon_from_svg(svg_content, size) self._icon_cache[cache_key] = icon return icon def _create_icon_from_svg(self, svg_content: str, size: int) -> QIcon: """Create QIcon from SVG content""" # Create QSvgRenderer from SVG content svg_bytes = svg_content.encode('utf-8') renderer = QSvgRenderer(svg_bytes) # Create pixmap pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) # Paint SVG onto pixmap painter = QPainter(pixmap) renderer.render(painter) painter.end() return QIcon(pixmap) def get_status_icon(self, status: str, size: int = 16) -> QIcon: """Get icon for specific status""" status_map = { 'success': ('CHECK_CIRCLE', IconTheme.SUCCESS), 'warning': ('WARNING_CIRCLE', IconTheme.WARNING), 'error': ('ERROR_CIRCLE', IconTheme.ERROR), 'info': ('INFO_CIRCLE', IconTheme.INFO), 'violation': ('VIOLATION', IconTheme.ERROR), 'active': ('PLAY', IconTheme.SUCCESS), 'inactive': ('PAUSE', IconTheme.SECONDARY), 'recording': ('RECORD', IconTheme.ERROR) } icon_name, color = status_map.get(status, ('INFO_CIRCLE', IconTheme.INFO)) return self.get_icon(icon_name, color, size) def get_action_icon(self, action: str, size: int = 20) -> QIcon: """Get icon for specific action""" action_map = { 'play': 'PLAY', 'pause': 'PAUSE', 'stop': 'STOP', 'record': 'RECORD', 'settings': 'SETTINGS', 'export': 'EXPORT', 'import': 'IMPORT', 'save': 'SAVE', 'refresh': 'REFRESH', 'delete': 'DELETE', 'edit': 'EDIT', 'filter': 'FILTER', 'search': 'SEARCH' } icon_name = action_map.get(action, 'INFO_CIRCLE') return self.get_icon(icon_name, IconTheme.PRIMARY, size) def get_navigation_icon(self, view: str, size: int = 24) -> QIcon: """Get icon for navigation views""" nav_map = { 'home': 'HOME', 'detection': 'CAMERA', 'violations': 'VIOLATION', 'analytics': 'DASHBOARD', 'export': 'EXPORT', 'monitor': 'MONITOR', 'chart': 'CHART_BAR' } icon_name = nav_map.get(view, 'HOME') return self.get_icon(icon_name, IconTheme.ACCENT, size) def create_colored_icon(self, base_icon: str, color: str, size: int = 24) -> QIcon: """Create a colored version of an icon""" return self.get_icon(base_icon, color, size) def set_theme_color(self, color: str): """Set the theme accent color""" self.theme.ACCENT = color # Clear cache to regenerate icons with new color self._icon_cache.clear() # Global icon manager instance icon_manager = IconManager() # Convenience functions def get_icon(name: str, color: str = IconTheme.PRIMARY, size: int = 24) -> QIcon: """Get an icon - convenience function""" return icon_manager.get_icon(name, color, size) def get_status_icon(status: str, size: int = 16) -> QIcon: """Get status icon - convenience function""" return icon_manager.get_status_icon(status, size) def get_action_icon(action: str, size: int = 20) -> QIcon: """Get action icon - convenience function""" return icon_manager.get_action_icon(action, size) def get_navigation_icon(view: str, size: int = 24) -> QIcon: """Get navigation icon - convenience function""" return icon_manager.get_navigation_icon(view, size) # Common icon sets for easy access class CommonIcons: """Commonly used icon combinations""" @staticmethod def toolbar_icons() -> Dict[str, QIcon]: """Get all toolbar icons""" return { 'play': get_action_icon('play'), 'pause': get_action_icon('pause'), 'stop': get_action_icon('stop'), 'record': get_action_icon('record'), 'settings': get_action_icon('settings'), 'export': get_action_icon('export'), 'refresh': get_action_icon('refresh') } @staticmethod def status_icons() -> Dict[str, QIcon]: """Get all status icons""" return { 'success': get_status_icon('success'), 'warning': get_status_icon('warning'), 'error': get_status_icon('error'), 'info': get_status_icon('info'), 'violation': get_status_icon('violation'), 'active': get_status_icon('active'), 'inactive': get_status_icon('inactive'), 'recording': get_status_icon('recording') } @staticmethod def navigation_icons() -> Dict[str, QIcon]: """Get all navigation icons""" return { 'detection': get_navigation_icon('detection'), 'violations': get_navigation_icon('violations'), 'analytics': get_navigation_icon('analytics'), 'export': get_navigation_icon('export'), 'monitor': get_navigation_icon('monitor') } # Traffic light specific icons def create_traffic_light_icon(red_on: bool = False, yellow_on: bool = False, green_on: bool = False, size: int = 32) -> QIcon: """Create a traffic light icon with specific lights on/off""" svg_template = f""" """ svg_bytes = svg_template.encode('utf-8') renderer = QSvgRenderer(svg_bytes) pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) renderer.render(painter) painter.end() return QIcon(pixmap) # New FinaleIcons class to wrap the existing functionality class FinaleIcons: """ Wrapper class for icon management to maintain compatibility with existing code that references FinaleIcons.get_icon() etc. """ @staticmethod def get_icon(name: str, color: str = IconTheme.PRIMARY, size: int = 24) -> QIcon: """Get an icon by name""" return get_icon(name, color, size) @staticmethod def get_status_icon(status: str, size: int = 16) -> QIcon: """Get a status icon""" return get_status_icon(status, size) @staticmethod def get_action_icon(action: str, size: int = 20) -> QIcon: """Get an action icon""" return get_action_icon(action, size) @staticmethod def get_navigation_icon(view: str, size: int = 24) -> QIcon: """Get a navigation icon""" return get_navigation_icon(view, size) @staticmethod def create_colored_icon(base_icon: str, color: str, size: int = 24) -> QIcon: """Create a colored version of an icon""" return get_icon(base_icon, color, size) @staticmethod def traffic_light_icon(red_on: bool = False, yellow_on: bool = False, green_on: bool = False, size: int = 32) -> QIcon: """Create a traffic light icon with specific lights on/off""" return create_traffic_light_icon(red_on, yellow_on, green_on, size)