EU-Utility/core/main_optimized.py

444 lines
16 KiB
Python

"""
EU-Utility - Performance Optimized Main Entry Point
Integrates all performance optimizations:
- Startup profiling
- Optimized LogWatcher
- Optimized OCR Service
- Memory leak detection
- UI rendering at 60+ FPS
"""
import sys
import os
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent.parent
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
# Performance imports
from core.startup_profiler import get_startup_profiler
from core.memory_leak_detector import start_leak_detection, MemoryOptimizer
profiler = get_startup_profiler()
try:
with profiler.profile("pyqt_import"):
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt, QObject, pyqtSignal
PYQT_AVAILABLE = True
except ImportError:
PYQT_AVAILABLE = False
print("Error: PyQt6 is required.")
sys.exit(1)
try:
with profiler.profile("keyboard_import"):
import keyboard
KEYBOARD_AVAILABLE = True
except ImportError:
KEYBOARD_AVAILABLE = False
print("Warning: 'keyboard' library not installed.")
# Core imports with profiling
with profiler.profile("core_imports"):
from core.plugin_manager import PluginManager
from core.overlay_window import OverlayWindow
from core.floating_icon import FloatingIcon
from core.settings import get_settings
from core.overlay_widgets import OverlayManager
from core.plugin_api import get_api, APIType
# Use optimized components
try:
from core.log_watcher_optimized import get_log_watcher
USE_OPTIMIZED_LOG = True
except ImportError:
from core.log_reader import get_log_reader
USE_OPTIMIZED_LOG = False
print("[Performance] Using standard LogReader (optimized version not available)")
try:
from core.ocr_service_optimized_v2 import get_ocr_service
USE_OPTIMIZED_OCR = True
except ImportError:
from core.ocr_service import get_ocr_service
USE_OPTIMIZED_OCR = False
print("[Performance] Using standard OCR (optimized version not available)")
from core.screenshot import get_screenshot_service
from core.notifications import get_notification_manager, NotificationManager
from core.nexus_api import get_nexus_api
from core.http_client import get_http_client
from core.window_manager import get_window_manager
from core.event_bus import get_event_bus
from core.tasks import get_task_manager
from core.audio import get_audio_manager
from core.clipboard import get_clipboard_manager
from core.data_store import get_data_store
# UI optimizations
try:
from core.ui_render_optimized import set_target_fps
USE_OPTIMIZED_UI = True
except ImportError:
USE_OPTIMIZED_UI = False
class HotkeyHandler(QObject):
"""Signal bridge for thread-safe hotkey handling."""
toggle_signal = pyqtSignal()
hide_overlays_signal = pyqtSignal()
class PerformanceOptimizedApp:
"""
Main application controller with performance optimizations.
"""
def __init__(self):
self.app = None
self.overlay = None
self.floating_icon = None
self.plugin_manager = None
self.hotkey_handler = None
self.settings = None
self.overlay_manager = None
self.api = None
self.notification_manager = None
self.leak_detector = None
def run(self):
"""Start the application with profiling."""
profiler = get_startup_profiler()
with profiler.profile("qt_app_creation"):
# Create Qt Application
self.app = QApplication(sys.argv)
self.app.setQuitOnLastWindowClosed(False)
# Enable high DPI scaling
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
self.app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling)
with profiler.profile("plugin_api_init"):
# Initialize Plugin API
print("[Performance] Initializing Plugin API...")
self.api = get_api()
self._setup_api_services()
with profiler.profile("event_bus_init"):
# Initialize Event Bus
print("[Performance] Initializing Event Bus...")
self.event_bus = get_event_bus()
self._print_event_bus_stats()
with profiler.profile("notification_init"):
# Initialize Notification Manager
print("[Performance] Initializing Notification Manager...")
self.notification_manager = get_notification_manager()
self.notification_manager.initialize(self.app)
self.api.register_notification_service(self.notification_manager)
with profiler.profile("settings_load"):
# Load settings
self.settings = get_settings()
with profiler.profile("hotkey_setup"):
# Create hotkey handler (must be in main thread)
self.hotkey_handler = HotkeyHandler()
with profiler.profile("plugin_loading"):
# Initialize plugin manager
print("[Performance] Loading plugins...")
self.plugin_manager = PluginManager(None)
self.plugin_manager.load_all_plugins()
with profiler.profile("ui_creation"):
# Create overlay manager
self.overlay_manager = OverlayManager(self.app)
# Create overlay window
self.overlay = OverlayWindow(self.plugin_manager)
self.plugin_manager.overlay = self.overlay
# Create floating icon
print("[Performance] Creating floating icon...")
self.floating_icon = FloatingIcon()
self.floating_icon.clicked.connect(self._toggle_overlay)
self.floating_icon.show()
# Set target FPS for UI
if USE_OPTIMIZED_UI:
set_target_fps(60.0)
with profiler.profile("connections_setup"):
# Connect hotkey signals
self.hotkey_handler.toggle_signal.connect(self._on_toggle_signal)
# Setup global hotkeys
self._setup_hotkeys()
# Load saved overlay widgets
self._load_overlay_widgets()
with profiler.profile("leak_detection_start"):
# Start memory leak detection
self.leak_detector = start_leak_detection(check_interval=300.0) # 5 minutes
# Print startup report
profiler.print_report()
print("\n" + "=" * 60)
print("EU-Utility Started!")
print("=" * 60)
print("Press Ctrl+Shift+U to toggle overlay")
print("Press Ctrl+Shift+H to hide all overlays")
print("Or double-click the floating icon")
print(f"Loaded {len(self.plugin_manager.get_all_plugins())} plugins")
print(f"Optimizations enabled:")
print(f" - Optimized LogWatcher: {USE_OPTIMIZED_LOG}")
print(f" - Optimized OCR: {USE_OPTIMIZED_OCR}")
print(f" - Optimized UI Rendering: {USE_OPTIMIZED_UI}")
print(f" - Memory Leak Detection: Active")
print("=" * 60 + "\n")
# Show Event Bus stats
self._print_event_bus_stats()
# Run
return self.app.exec()
def _setup_api_services(self):
"""Setup shared API services with performance optimizations."""
# Initialize and start Log Reader (Optimized)
print("[Performance] Initializing Log Reader...")
if USE_OPTIMIZED_LOG:
self.log_reader = get_log_watcher()
else:
self.log_reader = get_log_reader()
if self.log_reader.start():
print(f"[Performance] Log Reader started - watching: {self.log_reader.log_path}")
else:
print("[Performance] Log Reader not available - log file not found")
self.api.register_log_service(self.log_reader.read_lines)
# Initialize Window Manager
print("[Performance] Initializing Window Manager...")
self.window_manager = get_window_manager()
if self.window_manager.is_available():
eu_window = self.window_manager.find_eu_window()
if eu_window:
print(f"[Performance] Found EU window: {eu_window.title} ({eu_window.width}x{eu_window.height})")
else:
print("[Performance] EU window not found - will retry when needed")
self.api.register_window_service(self.window_manager)
else:
print("[Performance] Window Manager not available (Windows only)")
# Screenshot Service - Initialize on startup
print("[Performance] Initializing Screenshot Service...")
self.screenshot_service = get_screenshot_service()
if self.screenshot_service.is_available():
self.api.register_screenshot_service(self.screenshot_service)
backends = self.screenshot_service.get_available_backends()
print(f"[Performance] Screenshot Service ready (backends: {', '.join(backends)})")
else:
print("[Performance] Screenshot Service not available")
# OCR Service - LAZY INITIALIZATION
print("[Performance] OCR Service configured (lazy init)")
self.ocr_service = get_ocr_service(background_init=True)
self.api.register_ocr_service(self._lazy_ocr_handler)
# Initialize Nexus API Service
print("[Performance] Initializing Nexus API Service...")
self.nexus_api = get_nexus_api()
self.api.register_nexus_service(self.nexus_api)
# HTTP Client - Initialize with caching
print("[Performance] Initializing HTTP Client...")
try:
self.http_client = get_http_client(
cache_dir="cache/http",
default_cache_ttl=3600,
rate_limit_delay=0.1,
max_retries=3,
backoff_factor=0.5,
respect_cache_control=True
)
self.api.register_http_service(self.http_client)
print("[Performance] HTTP Client initialized with caching")
except Exception as e:
print(f"[Performance] HTTP Client initialization failed: {e}")
# Initialize Audio Service
print("[Performance] Initializing Audio Service...")
self.audio_manager = get_audio_manager()
if self.audio_manager.is_available():
self.api.register_audio_service(self.audio_manager)
backend = self.audio_manager.get_backend()
volume = int(self.audio_manager.get_volume() * 100)
print(f"[Performance] Audio Service ready (backend: {backend}, volume: {volume}%)")
else:
print("[Performance] Audio Service not available")
# Initialize Task Manager
print("[Performance] Initializing Task Manager...")
try:
self.task_manager = get_task_manager(max_workers=4)
self.task_manager.initialize()
self.api.register_task_service(self.task_manager)
print("[Performance] Task Manager initialized with 4 workers")
except Exception as e:
print(f"[Performance] Task Manager initialization failed: {e}")
# Initialize Clipboard Manager
print("[Performance] Initializing Clipboard Manager...")
self.clipboard_manager = get_clipboard_manager()
if self.clipboard_manager.is_available():
self.api.register_clipboard_service(self.clipboard_manager)
print("[Performance] Clipboard Service ready")
else:
print("[Performance] Clipboard Service not available")
# Initialize Data Store
print("[Performance] Initializing Data Store...")
self.data_store = get_data_store()
self.api.register_data_service(self.data_store)
print("[Performance] Data Store ready")
print("[Performance] All API services registered")
def _lazy_ocr_handler(self, region=None):
"""Lazy OCR handler - triggers init on first use."""
return self.ocr_service.recognize(region=region)
def _print_event_bus_stats(self):
"""Print Event Bus statistics on startup."""
if not hasattr(self, 'event_bus') or not self.event_bus:
return
stats = self.event_bus.get_stats()
print("\n" + "=" * 50)
print("📊 Event Bus Statistics")
print("=" * 50)
print(f" Total Events Published: {stats.get('total_published', 0)}")
print(f" Total Events Delivered: {stats.get('total_delivered', 0)}")
print(f" Active Subscriptions: {stats.get('active_subscriptions', 0)}")
print(f" Events Per Minute: {stats.get('events_per_minute', 0)}")
print(f" Avg Delivery Time: {stats.get('avg_delivery_ms', 0)} ms")
print(f" Errors: {stats.get('errors', 0)}")
top_types = stats.get('top_event_types', {})
if top_types:
print(f"\n Top Event Types:")
for event_type, count in list(top_types.items())[:5]:
print(f"{event_type}: {count}")
print("=" * 50 + "\n")
def _setup_hotkeys(self):
"""Setup global hotkeys."""
if KEYBOARD_AVAILABLE:
try:
keyboard.add_hotkey('ctrl+shift+u', self._on_hotkey_pressed)
keyboard.add_hotkey('ctrl+shift+h', self._on_hide_overlays_pressed)
except Exception as e:
print(f"[Performance] Failed to register hotkey: {e}")
def _on_hotkey_pressed(self):
"""Called when toggle hotkey is pressed."""
if self.hotkey_handler:
self.hotkey_handler.toggle_signal.emit()
def _on_hide_overlays_pressed(self):
"""Called when hide hotkey is pressed."""
if self.overlay_manager:
self.overlay_manager.hide_all()
def _on_toggle_signal(self):
"""Handle toggle signal in main thread."""
self._toggle_overlay()
def _toggle_overlay(self):
"""Toggle overlay visibility."""
if self.overlay:
self.overlay.toggle_overlay()
def _load_overlay_widgets(self):
"""Load saved overlay widgets."""
widget_settings = self.settings.get('overlay_widgets', {})
for widget_name, config in widget_settings.items():
if config.get('enabled', False):
try:
self.overlay_manager.create_widget(
widget_name.replace('_overlay', ''),
widget_name
)
except Exception as e:
print(f"[Performance] Failed to load overlay widget {widget_name}: {e}")
def create_overlay_widget(self, widget_type, name):
"""Create an overlay widget."""
if self.overlay_manager:
return self.overlay_manager.create_widget(widget_type, name)
return None
def quit(self):
"""Quit the application with cleanup."""
print("[Performance] Shutting down...")
# Print final memory report
MemoryOptimizer.print_memory_summary()
# Stop log reader
if hasattr(self, 'log_reader'):
self.log_reader.stop()
# Close all notifications
if self.notification_manager:
self.notification_manager.close_all()
# Shutdown Event Bus
if hasattr(self, 'event_bus') and self.event_bus:
print("[Performance] Shutting down Event Bus...")
self.event_bus.shutdown()
# Shutdown Audio
if hasattr(self, 'audio_manager') and self.audio_manager:
print("[Performance] Shutting down Audio...")
self.audio_manager.shutdown()
# Shutdown Task Manager
if hasattr(self, 'task_manager') and self.task_manager:
print("[Performance] Shutting down Task Manager...")
self.task_manager.shutdown(wait=True, timeout=30.0)
# Shutdown leak detector
if self.leak_detector:
self.leak_detector.stop()
if self.overlay_manager:
self.overlay_manager.hide_all()
if self.plugin_manager:
self.plugin_manager.shutdown_all()
self.app.quit()
def main():
"""Entry point."""
app = PerformanceOptimizedApp()
sys.exit(app.run())
if __name__ == "__main__":
main()