from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSlider, QCheckBox, QPushButton, QGroupBox, QFormLayout, QSpinBox, QDoubleSpinBox, QTabWidget, QLineEdit, QFileDialog, QSpacerItem, QSizePolicy, QScrollArea, QFrame ) from PySide6.QtCore import Qt, Signal, Slot from PySide6.QtGui import QFont # Import VLM insights widget from ui.vlm_insights_widget import VLMInsightsWidget class ModernConfigPanel(QWidget): def get_vlm_widget(self): """Return the VLMInsightsWidget instance for controller integration.""" return getattr(self, 'vlm_insights_widget', None) def vlm_set_current_frame(self, frame): if hasattr(self, 'vlm_insights_widget'): self.vlm_insights_widget.set_current_frame(frame) def vlm_set_detection_data(self, detection_data): if hasattr(self, 'vlm_insights_widget'): self.vlm_insights_widget.set_detection_data(detection_data) def vlm_on_video_paused(self, is_paused): if hasattr(self, 'vlm_insights_widget'): self.vlm_insights_widget.on_video_paused(is_paused) def vlm_on_analysis_result(self, result): if hasattr(self, 'vlm_insights_widget'): self.vlm_insights_widget.on_analysis_result(result) """Enhanced side panel with modern dark theme and pill-style tabs.""" config_changed = Signal(dict) # Emitted when configuration changes are applied theme_toggled = Signal(bool) # Emitted when theme toggle button is clicked (True = dark) device_switch_requested = Signal(str) def __init__(self): super().__init__() self.setObjectName("ModernConfigPanel") self.setStyleSheet(self._get_modern_style()) # Set minimum and preferred size for the panel self.setMinimumSize(380, 600) self.setMaximumWidth(500) self.initUI() self.dark_theme = True # Start with dark theme def _get_modern_style(self): """Modern dark theme with compact layout""" return """ #ModernConfigPanel { background: #121212; border: none; min-width: 320px; max-width: 400px; } /* Tab Widget Styling */ QTabWidget::pane { background: #1E1E1E; border: 1px solid #2C2C2C; border-radius: 8px; padding: 8px; } QTabBar::tab { background: transparent; color: #B0B0B0; border-radius: 12px; padding: 8px 16px; margin: 1px; font-size: 12px; font-weight: 500; min-width: 70px; } QTabBar::tab:selected { background: #007BFF; color: #FFFFFF; } QTabBar::tab:hover:!selected { background: #2C2C2C; color: #FFFFFF; } /* Section Headers */ QLabel.section-header { font-weight: bold; color: #FFFFFF; border-bottom: 1px solid #2C2C2C; margin-bottom: 8px; padding-bottom: 4px; font-size: 14px; } /* Regular Labels */ QLabel { color: #FFFFFF; font-size: 12px; background: transparent; } QLabel.secondary { color: #B0B0B0; } /* Buttons */ QPushButton { background: #007BFF; color: #FFFFFF; border-radius: 8px; font-size: 13px; font-weight: 600; padding: 10px 16px; border: none; } QPushButton:hover { background: #3399FF; } QPushButton.secondary { background: #2ECC71; } QPushButton.secondary:hover { background: #48D187; } QPushButton.warning { background: #E74C3C; } QPushButton.warning:hover { background: #FF6B5A; } /* Sliders */ QSlider::groove:horizontal { background: #1E1E1E; border: 1px solid #00E6E6; height: 6px; border-radius: 3px; } QSlider::handle:horizontal { background: #00E6E6; border-radius: 8px; width: 16px; height: 16px; margin: -5px 0; } QSlider::handle:horizontal:hover { background: #00FFFF; } /* Combo Boxes */ QComboBox { background: #1E1E1E; border: 1px solid #00E6E6; border-radius: 6px; padding: 6px 12px; color: #FFFFFF; font-size: 12px; min-height: 20px; } QComboBox::drop-down { border: none; width: 20px; } QComboBox::down-arrow { image: none; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #00E6E6; margin-right: 5px; } QComboBox QAbstractItemView { background: #1E1E1E; border: 1px solid #2C2C2C; color: #FFFFFF; selection-background-color: #007BFF; } /* Checkboxes */ QCheckBox { color: #FFFFFF; font-size: 12px; spacing: 8px; } QCheckBox::indicator { width: 16px; height: 16px; border-radius: 3px; border: 1px solid #2C2C2C; background: #1E1E1E; } QCheckBox::indicator:checked { background: #007BFF; border: 1px solid #007BFF; } QCheckBox::indicator:checked:hover { background: #3399FF; } /* Spin Boxes */ QSpinBox, QDoubleSpinBox { background: #1E1E1E; border: 1px solid #2C2C2C; border-radius: 6px; color: #FFFFFF; padding: 6px 8px; font-size: 12px; } QSpinBox::up-button, QDoubleSpinBox::up-button { background: #2C2C2C; border-radius: 3px; } QSpinBox::down-button, QDoubleSpinBox::down-button { background: #2C2C2C; border-radius: 3px; } /* Group Boxes */ QGroupBox { border: 1px solid #2C2C2C; border-radius: 8px; margin-top: 16px; background: transparent; font-weight: bold; color: #FFFFFF; font-size: 13px; padding-top: 10px; } QGroupBox::title { subcontrol-origin: margin; left: 12px; top: 0px; padding: 0 8px; background: #121212; } /* Scroll Area */ QScrollArea { background: transparent; border: none; } QScrollArea QWidget { background: transparent; } /* Scroll Bars */ QScrollBar:vertical { background: #1E1E1E; width: 8px; border-radius: 4px; } QScrollBar::handle:vertical { background: #2C2C2C; border-radius: 4px; } QScrollBar::handle:vertical:hover { background: #007BFF; } """ def initUI(self): """Initialize the modern UI""" layout = QVBoxLayout(self) layout.setContentsMargins(16, 16, 16, 16) layout.setSpacing(0) # Create tab widget with pill-style tabs self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.North) # Create tabs self._create_detection_tab() self._create_ai_insights_tab() self._create_display_tab() self._create_violations_tab() layout.addWidget(self.tabs) def _create_detection_tab(self): """Create advanced detection settings tab with dynamic model selection""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(24) layout.setContentsMargins(0, 16, 0, 16) # System Info Section system_group = QGroupBox("System Info") system_layout = QVBoxLayout(system_group) system_layout.setSpacing(6) # Reduced spacing # Auto-detected device info device_info_layout = QHBoxLayout() device_info_label = QLabel("Current Device:") device_info_label.setStyleSheet("color: #B0B0B0; font-size: 11px;") self.current_device_label = QLabel("AUTO") self.current_device_label.setStyleSheet(""" QLabel { color: #00E6E6; font-size: 11px; font-weight: bold; background: #1E1E1E; border: 1px solid #00E6E6; border-radius: 3px; padding: 2px 6px; } """) device_info_layout.addWidget(device_info_label) device_info_layout.addWidget(self.current_device_label) device_info_layout.addStretch() # Device selector device_selector_layout = QHBoxLayout() device_selector_label = QLabel("Override Device:") device_selector_label.setStyleSheet("color: #FFFFFF; font-size: 12px;") self.device_combo = QComboBox() self.device_combo.addItems(["AUTO", "CPU", "GPU", "NPU"]) self.device_combo.setCurrentText("AUTO") self.device_combo.currentTextChanged.connect(self._on_device_changed) device_selector_layout.addWidget(device_selector_label) device_selector_layout.addWidget(self.device_combo) device_selector_layout.addStretch() system_layout.addLayout(device_info_layout) system_layout.addLayout(device_selector_layout) # Model Settings Section (Enhanced) model_group = QGroupBox("Model Settings (Dynamic Selection)") model_layout = QVBoxLayout(model_group) model_layout.setSpacing(6) # Reduced spacing # Auto-selection explanation auto_info = QLabel("📊 Auto-select based on device:") auto_info.setStyleSheet("color: #03DAC5; font-weight: bold; font-size: 12px;") model_layout.addWidget(auto_info) # Device-model mapping info mapping_layout = QVBoxLayout() mapping_layout.setSpacing(2) # Tight spacing cpu_mapping = QLabel("• CPU → YOLOv11n (lightweight)") gpu_mapping = QLabel("• GPU → YOLOv11x (heavyweight)") npu_mapping = QLabel("• NPU → YOLOv11n (optimized)") for label in [cpu_mapping, gpu_mapping, npu_mapping]: label.setStyleSheet("color: #B0B0B0; font-size: 10px; margin-left: 12px;") mapping_layout.addWidget(label) model_layout.addLayout(mapping_layout) # Auto-selected model info model_info_layout = QHBoxLayout() model_info_label = QLabel("Auto-Selected Model:") model_info_label.setStyleSheet("color: #B0B0B0; font-size: 12px;") self.auto_model_label = QLabel("YOLOv11n (CPU Optimized)") self.auto_model_label.setStyleSheet(""" QLabel { color: #FFD700; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #FFD700; border-radius: 4px; padding: 4px 8px; } """) model_info_layout.addWidget(model_info_label) model_info_layout.addWidget(self.auto_model_label) model_info_layout.addStretch() # Manual model selector dropdown manual_model_layout = QHBoxLayout() manual_model_label = QLabel("Manual Override:") manual_model_label.setStyleSheet("color: #FFFFFF; font-size: 12px;") self.model_combo = QComboBox() self.model_combo.addItems(["AUTO", "YOLOv11n", "YOLOv11s", "YOLOv11m", "YOLOv11l", "YOLOv11x"]) self.model_combo.setCurrentText("YOLOv11x") # Default to YOLOv11x instead of AUTO self.model_combo.currentTextChanged.connect(self._on_model_changed) manual_model_layout.addWidget(manual_model_label) manual_model_layout.addWidget(self.model_combo) manual_model_layout.addStretch() # Quick-switch buttons with glow highlight quick_switch_label = QLabel("🚀 Quick-switch buttons:") quick_switch_label.setStyleSheet("color: #03DAC5; font-weight: bold; font-size: 11px; margin-top: 4px;") quick_switch_layout = QHBoxLayout() quick_switch_layout.setSpacing(6) # Reduced spacing self.lightweight_btn = QPushButton("Lightweight\n(YOLOv11n)") self.lightweight_btn.setObjectName("quickSwitchLight") self.lightweight_btn.clicked.connect(lambda: self._quick_switch_model("YOLOv11n")) self.heavyweight_btn = QPushButton("High-Accuracy\n(YOLOv11x)") self.heavyweight_btn.setObjectName("quickSwitchHeavy") self.heavyweight_btn.clicked.connect(lambda: self._quick_switch_model("YOLOv11x")) quick_switch_layout.addWidget(self.lightweight_btn) quick_switch_layout.addWidget(self.heavyweight_btn) model_layout.addLayout(model_info_layout) model_layout.addLayout(manual_model_layout) model_layout.addWidget(quick_switch_label) model_layout.addLayout(quick_switch_layout) layout.addWidget(system_group) layout.addWidget(model_group) layout.addStretch() # Add custom styling for enhanced elements self._add_detection_tab_styles() self.tabs.addTab(widget, "Detection") # Auto-detect system at startup self._auto_detect_system() def _add_detection_tab_styles(self): """Add custom styles for enhanced detection tab elements""" additional_style = """ QPushButton[objectName="quickSwitchLight"] { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2196F3, stop:1 #1976D2); color: #FFFFFF; border-radius: 8px; font-size: 10px; font-weight: 600; padding: 6px 8px; border: 1px solid transparent; text-align: center; max-height: 40px; } QPushButton[objectName="quickSwitchLight"]:hover { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #42A5F5, stop:1 #1E88E5); border: 1px solid #03DAC5; } QPushButton[objectName="quickSwitchLight"]:pressed { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #1565C0, stop:1 #0D47A1); } QPushButton[objectName="quickSwitchHeavy"] { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4CAF50, stop:1 #388E3C); color: #FFFFFF; border-radius: 8px; font-size: 10px; font-weight: 600; padding: 6px 8px; border: 1px solid transparent; text-align: center; max-height: 40px; } QPushButton[objectName="quickSwitchHeavy"]:hover { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #66BB6A, stop:1 #43A047); border: 1px solid #03DAC5; } QPushButton[objectName="quickSwitchHeavy"]:pressed { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2E7D32, stop:1 #1B5E20); } QPushButton[objectName="activeModelLight"] { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #03DAC5, stop:1 #018786); color: #121212; border: 3px solid #00FFFF; box-shadow: 0 0 20px rgba(3, 218, 197, 0.8); animation: glow 2s ease-in-out infinite alternate; } QPushButton[objectName="activeModelHeavy"] { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #03DAC5, stop:1 #018786); color: #121212; border: 3px solid #00FFFF; box-shadow: 0 0 20px rgba(3, 218, 197, 0.8); animation: glow 2s ease-in-out infinite alternate; } QComboBox { background: #232323; color: #FFFFFF; border: 1px solid #424242; border-radius: 6px; padding: 6px 12px; font-size: 12px; } QComboBox:hover { border: 1px solid #03DAC5; } QComboBox::drop-down { border: none; background: #232323; width: 20px; border-radius: 6px; } QComboBox::down-arrow { image: none; border: 2px solid #FFFFFF; border-top: none; border-right: none; width: 6px; height: 6px; transform: rotate(-45deg); margin-right: 8px; } QComboBox QAbstractItemView { background: #232323; border: 1px solid #424242; border-radius: 6px; selection-background-color: #03DAC5; color: #FFFFFF; } """ current_style = self.styleSheet() self.setStyleSheet(current_style + additional_style) def _auto_detect_system(self): """Auto-detect system capabilities and set defaults""" try: # Try to detect GPU using OpenVINO try: import openvino as ov core = ov.Core() available_devices = core.available_devices if 'GPU' in available_devices: detected_device = "GPU" recommended_model = "YOLOv11x (GPU Optimized)" self.auto_model_label.setText(recommended_model) self.auto_model_label.setStyleSheet(""" QLabel { color: #00FF00; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #00FF00; border-radius: 4px; padding: 4px 8px; } """) print(f"[CONFIG PANEL] GPU detected via OpenVINO: {available_devices}") else: detected_device = "CPU" recommended_model = "YOLOv11n (CPU Optimized)" print(f"[CONFIG PANEL] Only CPU available: {available_devices}") except ImportError: # Fallback: Try nvidia-smi for NVIDIA GPUs try: import subprocess result = subprocess.run(['nvidia-smi'], capture_output=True, text=True, shell=True) if result.returncode == 0: detected_device = "GPU" recommended_model = "YOLOv11x (GPU Optimized)" self.auto_model_label.setText(recommended_model) self.auto_model_label.setStyleSheet(""" QLabel { color: #00FF00; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #00FF00; border-radius: 4px; padding: 4px 8px; } """) print("[CONFIG PANEL] GPU detected via nvidia-smi") else: detected_device = "CPU" recommended_model = "YOLOv11n (CPU Optimized)" print("[CONFIG PANEL] nvidia-smi failed, defaulting to CPU") except Exception as e: detected_device = "CPU" recommended_model = "YOLOv11n (CPU Optimized)" print(f"[CONFIG PANEL] nvidia-smi exception: {e}") except Exception as e: detected_device = "CPU" recommended_model = "YOLOv11n (CPU Optimized)" print(f"[CONFIG PANEL] Detection failed: {e}") self.current_device_label.setText(detected_device) self.auto_model_label.setText(recommended_model) # Update device combo to show detected device if detected_device in ["CPU", "GPU"]: # Set as current self.device_combo.setCurrentText(detected_device) # Highlight the appropriate quick switch button and update model preview self._highlight_active_model("AUTO") # Model preview removed - no longer updating preview print(f"[CONFIG PANEL] ✅ Auto-detection complete: {detected_device} -> {recommended_model}") def _on_device_changed(self, device): """Handle device selection change""" if device == "AUTO": self._auto_detect_system() else: self.current_device_label.setText(device) if device == "GPU": self.auto_model_label.setText("YOLOv11x (GPU Optimized)") self.auto_model_label.setStyleSheet(""" QLabel { color: #00FF00; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #00FF00; border-radius: 4px; padding: 4px 8px; } """) # Model preview removed self._highlight_active_model("YOLOv11x") else: self.auto_model_label.setText("YOLOv11n (CPU Optimized)") self.auto_model_label.setStyleSheet(""" QLabel { color: #FFD700; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #FFD700; border-radius: 4px; padding: 4px 8px; } """) # Model preview removed self._highlight_active_model("YOLOv11n") self.device_switch_requested.emit(device) def _on_model_changed(self, model): """Handle manual model selection change""" if model != "AUTO": self.auto_model_label.setText(f"{model} (Manual Override)") self.auto_model_label.setStyleSheet(""" QLabel { color: #FF6B5A; font-size: 12px; font-weight: bold; background: #1E1E1E; border: 1px solid #FF6B5A; border-radius: 4px; padding: 4px 8px; } """) else: self.auto_model_label.setText("Auto-Select Model") self.auto_model_label.setStyleSheet(""" QLabel { color: #00E6E6; font-size: 12px; font-weight: normal; background: transparent; border: none; padding: 0px; } """) # Apply configuration immediately when model changes print(f"🔧 Model changed to: {model}, applying config...") print(f"🔧 Current model combo text: {self.model_combo.currentText()}") print(f"🔧 Current device combo text: {self.device_combo.currentText()}") self.apply_config() self._highlight_active_model(model) def _quick_switch_model(self, model): """Handle quick switch button clicks""" self.model_combo.setCurrentText(model) self._on_model_changed(model) def _highlight_active_model(self, model): """Highlight the currently active model button with glow effect""" # Reset both buttons self.lightweight_btn.setObjectName("quickSwitchLight") self.heavyweight_btn.setObjectName("quickSwitchHeavy") # Highlight active button with special glow styling if model in ["YOLOv11n", "AUTO"] and "YOLOv11n" in self.auto_model_label.text(): self.lightweight_btn.setObjectName("activeModelLight") elif model in ["YOLOv11x"] or ("YOLOv11x" in self.auto_model_label.text() and model == "AUTO"): self.heavyweight_btn.setObjectName("activeModelHeavy") # Refresh styles self._add_detection_tab_styles() def _create_display_tab(self): """Create display settings tab""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(20) layout.setContentsMargins(0, 16, 0, 16) # Display Options display_group = QGroupBox("Display Options") display_layout = QVBoxLayout(display_group) display_layout.setSpacing(12) self.show_boxes = QCheckBox("Show Bounding Boxes") self.show_boxes.setChecked(True) self.show_labels = QCheckBox("Show Class Labels") self.show_labels.setChecked(True) self.show_confidence = QCheckBox("Show Confidence Scores") self.show_confidence.setChecked(True) self.show_fps = QCheckBox("Show FPS Counter") self.show_fps.setChecked(True) display_layout.addWidget(self.show_boxes) display_layout.addWidget(self.show_labels) display_layout.addWidget(self.show_confidence) display_layout.addWidget(self.show_fps) # Visual Settings visual_group = QGroupBox("Visual Settings") visual_layout = QVBoxLayout(visual_group) visual_layout.setSpacing(16) # Box thickness thickness_label = QLabel("Bounding Box Thickness") self.thickness_slider = QSlider(Qt.Horizontal) self.thickness_slider.setRange(1, 5) self.thickness_slider.setValue(2) self.thickness_value = QLabel("2") self.thickness_value.setObjectName("secondary") self.thickness_slider.valueChanged.connect( lambda v: self.thickness_value.setText(str(v)) ) thickness_layout = QHBoxLayout() thickness_layout.addWidget(thickness_label) thickness_layout.addStretch() thickness_layout.addWidget(self.thickness_value) # Font size font_label = QLabel("Label Font Size") self.font_slider = QSlider(Qt.Horizontal) self.font_slider.setRange(10, 24) self.font_slider.setValue(14) self.font_value = QLabel("14") self.font_value.setObjectName("secondary") self.font_slider.valueChanged.connect( lambda v: self.font_value.setText(str(v)) ) font_layout = QHBoxLayout() font_layout.addWidget(font_label) font_layout.addStretch() font_layout.addWidget(self.font_value) visual_layout.addLayout(thickness_layout) visual_layout.addWidget(self.thickness_slider) visual_layout.addLayout(font_layout) visual_layout.addWidget(self.font_slider) layout.addWidget(display_group) layout.addWidget(visual_group) layout.addStretch() self.tabs.addTab(widget, "Display") def _create_violations_tab(self): """Create violations settings tab""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(20) layout.setContentsMargins(0, 16, 0, 16) # Violation Detection violations_group = QGroupBox("Violation Detection") violations_layout = QVBoxLayout(violations_group) violations_layout.setSpacing(12) self.red_light_detection = QCheckBox("Red Light Violations") self.red_light_detection.setChecked(True) self.speed_violations = QCheckBox("Speed Violations") self.speed_violations.setChecked(False) self.wrong_way_detection = QCheckBox("Wrong Way Detection") self.wrong_way_detection.setChecked(False) self.crosswalk_violations = QCheckBox("Crosswalk Violations") self.crosswalk_violations.setChecked(False) violations_layout.addWidget(self.red_light_detection) violations_layout.addWidget(self.speed_violations) violations_layout.addWidget(self.wrong_way_detection) violations_layout.addWidget(self.crosswalk_violations) # Alert Settings alerts_group = QGroupBox("Alert Settings") alerts_layout = QVBoxLayout(alerts_group) alerts_layout.setSpacing(12) self.sound_alerts = QCheckBox("Sound Alerts") self.sound_alerts.setChecked(True) self.email_notifications = QCheckBox("Email Notifications") self.email_notifications.setChecked(False) self.auto_screenshot = QCheckBox("Auto Screenshot on Violation") self.auto_screenshot.setChecked(True) alerts_layout.addWidget(self.sound_alerts) alerts_layout.addWidget(self.email_notifications) alerts_layout.addWidget(self.auto_screenshot) layout.addWidget(violations_group) layout.addWidget(alerts_group) layout.addStretch() self.tabs.addTab(widget, "Violations") def _create_ai_insights_tab(self): """Create AI insights tab with VLMInsightsWidget""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(20) layout.setContentsMargins(0, 16, 0, 16) # Add the VLM Insights Widget self.vlm_insights_widget = VLMInsightsWidget() layout.addWidget(self.vlm_insights_widget) layout.addStretch() self.tabs.addTab(widget, "AI Insights") def get_config(self): """Get current configuration as dictionary""" return { 'device': self.device_combo.currentText(), 'confidence_threshold': 0.5, # Default value 'iou_threshold': 0.45, # Default value 'model': self.model_combo.currentText(), 'input_size': int(self.input_size_combo.currentText()), 'show_boxes': self.show_boxes.isChecked(), 'show_labels': self.show_labels.isChecked(), 'show_confidence': self.show_confidence.isChecked(), 'show_fps': self.show_fps.isChecked(), 'box_thickness': self.thickness_slider.value(), 'font_size': self.font_slider.value(), 'red_light_detection': self.red_light_detection.isChecked(), 'speed_violations': self.speed_violations.isChecked(), 'wrong_way_detection': self.wrong_way_detection.isChecked(), 'crosswalk_violations': self.crosswalk_violations.isChecked(), 'sound_alerts': self.sound_alerts.isChecked(), 'email_notifications': self.email_notifications.isChecked(), 'auto_screenshot': self.auto_screenshot.isChecked(), 'enable_vlm': self.enable_vlm.isChecked(), 'traffic_analysis': self.traffic_analysis.isChecked(), 'anomaly_detection': self.anomaly_detection.isChecked(), 'crowd_analysis': self.crowd_analysis.isChecked(), 'frame_skip': self.frame_skip_slider.value(), 'batch_size': self.batch_size_spin.value() } def set_config(self, config): """Set configuration from dictionary""" try: # Handle nested config structure detection_config = config.get('detection', {}) display_config = config.get('display', {}) violations_config = config.get('violations', {}) # Detection settings if 'device' in detection_config: device = detection_config['device'] self.device_combo.setCurrentText(device) print(f"🔧 Config Panel: Set device to {device}") if 'model' in detection_config: model = detection_config['model'] # Convert model format if needed (yolo11n -> YOLOv11n) if model and model.lower() != 'auto': if 'yolo11' in model.lower(): if '11n' in model.lower(): model = 'YOLOv11n' elif '11x' in model.lower(): model = 'YOLOv11x' elif '11s' in model.lower(): model = 'YOLOv11s' elif '11m' in model.lower(): model = 'YOLOv11m' elif '11l' in model.lower(): model = 'YOLOv11l' # Try to find and set the model in combo box index = self.model_combo.findText(model) if index >= 0: self.model_combo.setCurrentIndex(index) print(f"🔧 Config Panel: Set model to {model}") else: print(f"⚠️ Config Panel: Model {model} not found in combo box") # Skip confidence and IOU threshold settings (removed from UI) # Display settings if 'show_confidence' in display_config: self.show_confidence.setChecked(display_config['show_confidence']) if 'show_labels' in display_config: self.show_labels.setChecked(display_config['show_labels']) if 'show_performance' in display_config: self.show_fps.setChecked(display_config['show_performance']) # Violations settings if 'enable_red_light' in violations_config: self.red_light_detection.setChecked(violations_config['enable_red_light']) print("✅ Config Panel: Configuration loaded successfully") except Exception as e: print(f"❌ Error setting config in panel: {e}") import traceback traceback.print_exc() @Slot() def apply_config(self): """Apply current configuration""" config = self.get_config() print(f"🔧 Config Panel: Applying config: {config}") self.config_changed.emit(config) @Slot() def reset_config(self): """Reset configuration to defaults""" try: # Reset to default values self.device_combo.setCurrentText("CPU") # Skip confidence and IOU sliders (removed from UI) self.model_combo.setCurrentIndex(0) self.input_size_combo.setCurrentText("640") # Display settings self.show_boxes.setChecked(True) self.show_labels.setChecked(True) self.show_confidence.setChecked(True) self.show_fps.setChecked(True) self.thickness_slider.setValue(2) self.font_slider.setValue(14) # Violations settings self.red_light_detection.setChecked(True) self.speed_violations.setChecked(False) self.wrong_way_detection.setChecked(False) self.crosswalk_violations.setChecked(False) # Alert settings self.sound_alerts.setChecked(True) self.email_notifications.setChecked(False) self.auto_screenshot.setChecked(True) # AI settings self.enable_vlm.setChecked(False) self.traffic_analysis.setChecked(True) self.anomaly_detection.setChecked(False) self.crowd_analysis.setChecked(False) self.frame_skip_slider.setValue(0) self.batch_size_spin.setValue(1) print("Configuration reset to defaults") except Exception as e: print(f"Error resetting config: {e}") @Slot(dict) def update_devices_info(self, device_info): """Update device information in the config panel""" try: # Update device combo with available devices available_devices = device_info.get('available_devices', ['CPU']) current_device = self.device_combo.currentText() # Clear and repopulate device combo self.device_combo.clear() self.device_combo.addItems(available_devices) # Restore previous selection if available if current_device in available_devices: self.device_combo.setCurrentText(current_device) else: # Default to first available device if available_devices: self.device_combo.setCurrentText(available_devices[0]) print(f"[CONFIG PANEL] Updated available devices: {available_devices}") except Exception as e: print(f"[CONFIG PANEL] Error updating device info: {e}") @Slot(str) def update_status(self, status_message): """Update status message (placeholder for compatibility)""" print(f"[CONFIG PANEL] Status: {status_message}") @Slot(dict) def update_model_info(self, model_info): """Update model information in the config panel""" try: # Update model combo if models are provided if 'available_models' in model_info: current_model = self.model_combo.currentText() self.model_combo.clear() self.model_combo.addItems(model_info['available_models']) # Restore previous selection if available if current_model in model_info['available_models']: self.model_combo.setCurrentText(current_model) print(f"[CONFIG PANEL] Updated model info: {model_info}") except Exception as e: print(f"[CONFIG PANEL] Error updating model info: {e}")