Add unified font control system with auto-regeneration
- Add open file button for direct audio file selection - Implement clustered FontControlWidget with font selector, size slider, and refresh controls - Add auto-regeneration of matplotlib plots when fonts change - Enhance FontManager with system font discovery and startup selection logic - Replace individual font selector with comprehensive font control interface - Support both custom fonts and system font candidates with proper prioritization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
Font selector widget providing GUI interface to font management system.
|
||||
Self-contained widget that can be placed anywhere in the layout.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Optional
|
||||
from PyQt5.QtWidgets import QWidget, QComboBox, QVBoxLayout, QLabel
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from font_manager import get_font_manager
|
||||
|
||||
|
||||
class FontSelectorWidget(QWidget):
|
||||
"""
|
||||
Self-contained font selector widget.
|
||||
|
||||
Provides a dropdown interface to select fonts from available
|
||||
custom fonts and system fonts. Integrates with the FontManager
|
||||
backend for font discovery and application.
|
||||
"""
|
||||
|
||||
# Signal emitted when font selection changes
|
||||
fontChanged = pyqtSignal(str, str) # (font_name, font_type)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""Initialize the font selector widget."""
|
||||
super().__init__(parent)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.font_manager = get_font_manager()
|
||||
self.available_fonts: Dict[str, str] = {} # display_name -> actual_font_name
|
||||
|
||||
self.initUI()
|
||||
self.refresh_font_list()
|
||||
|
||||
def initUI(self):
|
||||
"""Initialize the user interface."""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0) # Minimal margins for embedding
|
||||
|
||||
# Label
|
||||
self.label = QLabel("Font:")
|
||||
layout.addWidget(self.label)
|
||||
|
||||
# Font selector dropdown
|
||||
self.font_combo = QComboBox()
|
||||
self.font_combo.currentTextChanged.connect(self.on_font_changed)
|
||||
layout.addWidget(self.font_combo)
|
||||
|
||||
def refresh_font_list(self):
|
||||
"""Refresh the list of available fonts."""
|
||||
self.logger.debug("Refreshing font list...")
|
||||
self.available_fonts.clear()
|
||||
|
||||
try:
|
||||
# Get custom fonts from font manager
|
||||
custom_fonts = self.font_manager.loaded_fonts
|
||||
|
||||
# Get system font candidates
|
||||
system_fonts = self._get_system_font_candidates()
|
||||
|
||||
# Clear combo box
|
||||
self.font_combo.clear()
|
||||
|
||||
# Add custom fonts first (highest priority)
|
||||
if custom_fonts:
|
||||
for font_family, font_path in custom_fonts.items():
|
||||
display_name = f"{font_family} (Custom)"
|
||||
self.available_fonts[display_name] = font_family
|
||||
self.font_combo.addItem(display_name)
|
||||
self.logger.debug(f"Added custom font: {display_name}")
|
||||
|
||||
# Add system fonts
|
||||
for font_name in system_fonts:
|
||||
display_name = f"{font_name} (System)"
|
||||
self.available_fonts[display_name] = font_name
|
||||
self.font_combo.addItem(display_name)
|
||||
self.logger.debug(f"Added system font: {display_name}")
|
||||
|
||||
# Add default option with actual system font name
|
||||
system_font_name = self.font_manager.get_default_system_font_name()
|
||||
default_name = f"Default ({system_font_name})"
|
||||
self.available_fonts[default_name] = "default"
|
||||
self.font_combo.addItem(default_name)
|
||||
|
||||
# Select startup font
|
||||
self._select_startup_font()
|
||||
|
||||
self.logger.info(f"Font list refreshed: {len(self.available_fonts)} fonts available")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error refreshing font list: {e}")
|
||||
# Fallback: add default option only
|
||||
self.font_combo.clear()
|
||||
self.font_combo.addItem("Default (System)")
|
||||
self.available_fonts = {"Default (System)": "default"}
|
||||
|
||||
def _get_system_font_candidates(self) -> List[str]:
|
||||
"""
|
||||
Get list of available system fonts that are good candidates.
|
||||
|
||||
Returns:
|
||||
List[str]: List of available system font names
|
||||
"""
|
||||
# Use font manager's enhanced system font discovery
|
||||
return self.font_manager.get_available_system_fonts()[:10] # Limit to reasonable number
|
||||
|
||||
def _select_startup_font(self):
|
||||
"""Select the appropriate font on startup."""
|
||||
# Use font manager's startup font selection logic
|
||||
startup_font_name, startup_font_type = self.font_manager.select_startup_font()
|
||||
|
||||
# Find the corresponding display name in our combo box
|
||||
target_display_name = None
|
||||
for display_name, actual_name in self.available_fonts.items():
|
||||
if actual_name == startup_font_name:
|
||||
target_display_name = display_name
|
||||
break
|
||||
|
||||
# If we found the font, select it
|
||||
if target_display_name:
|
||||
index = self.font_combo.findText(target_display_name)
|
||||
if index >= 0:
|
||||
self.font_combo.setCurrentIndex(index)
|
||||
self.logger.info(f"Startup font selected: {target_display_name}")
|
||||
return
|
||||
|
||||
# Fallback to default if we couldn't find the startup font
|
||||
# Find any item that starts with "Default ("
|
||||
for i in range(self.font_combo.count()):
|
||||
item_text = self.font_combo.itemText(i)
|
||||
if item_text.startswith("Default ("):
|
||||
self.font_combo.setCurrentIndex(i)
|
||||
self.logger.info(f"Startup font fallback: {item_text}")
|
||||
return
|
||||
|
||||
def on_font_changed(self, display_name: str):
|
||||
"""Handle font selection change."""
|
||||
if not display_name or display_name not in self.available_fonts:
|
||||
return
|
||||
|
||||
actual_font_name = self.available_fonts[display_name]
|
||||
|
||||
# Determine font type
|
||||
if "(Custom)" in display_name:
|
||||
font_type = "custom"
|
||||
elif "(System)" in display_name:
|
||||
font_type = "system"
|
||||
else:
|
||||
font_type = "default"
|
||||
|
||||
self.logger.info(f"Font changed: {display_name} -> {actual_font_name} ({font_type})")
|
||||
|
||||
# Apply the font change
|
||||
self._apply_font_change(actual_font_name, font_type)
|
||||
|
||||
# Emit signal for any external listeners
|
||||
self.fontChanged.emit(actual_font_name, font_type)
|
||||
|
||||
def _apply_font_change(self, font_name: str, font_type: str):
|
||||
"""Apply the font change to the application."""
|
||||
try:
|
||||
# Use font manager's centralized font application
|
||||
success = self.font_manager.apply_font_selection(font_name, font_type)
|
||||
if not success:
|
||||
self.logger.warning(f"Font application may have failed: {font_name}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error applying font change: {e}")
|
||||
|
||||
def get_current_font(self) -> tuple[str, str]:
|
||||
"""
|
||||
Get currently selected font.
|
||||
|
||||
Returns:
|
||||
tuple: (font_name, font_type)
|
||||
"""
|
||||
display_name = self.font_combo.currentText()
|
||||
if display_name in self.available_fonts:
|
||||
actual_font_name = self.available_fonts[display_name]
|
||||
|
||||
if "(Custom)" in display_name:
|
||||
font_type = "custom"
|
||||
elif "(System)" in display_name:
|
||||
font_type = "system"
|
||||
else:
|
||||
font_type = "default"
|
||||
|
||||
return actual_font_name, font_type
|
||||
|
||||
return "default", "default"
|
||||
|
||||
def set_font(self, font_name: str):
|
||||
"""
|
||||
Programmatically set the font selection.
|
||||
|
||||
Args:
|
||||
font_name: Name of font to select
|
||||
"""
|
||||
# Find matching display name
|
||||
for display_name, actual_name in self.available_fonts.items():
|
||||
if actual_name == font_name:
|
||||
index = self.font_combo.findText(display_name)
|
||||
if index >= 0:
|
||||
self.font_combo.setCurrentIndex(index)
|
||||
return
|
||||
|
||||
self.logger.warning(f"Font not found in selector: {font_name}")
|
||||
Reference in New Issue
Block a user