""" Quality Classification Tab This tab handles comprehensive quality assessment with multiple camera views and analysis. """ import os from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QPushButton, QFrame, QGroupBox, QTableWidget, QTableWidgetItem, QHeaderView) from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QFont, QPixmap, QImage from resources.styles import COLORS, STYLES from ui.panels.quality_rgb_top_panel import QualityRGBTopPanel from ui.panels.quality_rgb_side_panel import QualityRGBSidePanel from ui.panels.quality_thermal_panel import QualityThermalPanel from ui.panels.quality_defects_panel import QualityDefectsPanel from ui.panels.quality_control_panel import QualityControlPanel from ui.panels.quality_results_panel import QualityResultsPanel from ui.panels.quality_history_panel import QualityHistoryPanel from ui.dialogs.image_preview_dialog import ImagePreviewDialog from models.locule_model import LoculeModel from models.defect_model import DefectModel from utils.config import get_device class QualityTab(QWidget): """ Comprehensive quality assessment tab with multiple camera views and analysis. Signals: load_image_requested: Emitted when user wants to load an image file """ load_image_requested = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.locule_model = None self.defect_model = None self.last_processed_file = None self.init_ui() self._initialize_models() def init_ui(self): """Initialize the UI components with new 2x2+2 grid layout.""" layout = QVBoxLayout(self) layout.setContentsMargins(10, 10, 10, 10) layout.setSpacing(10) # # Title # title = QLabel("🔍 Quality Assessment") # title.setFont(QFont("Arial", 16, QFont.Bold)) # title.setAlignment(Qt.AlignCenter) # title.setStyleSheet(f"color: {COLORS['text_primary']}; margin: 10px;") # layout.addWidget(title) # Main content area with 2x2+2 grid layout content_widget = QWidget() content_layout = QGridLayout(content_widget) content_layout.setContentsMargins(0, 0, 0, 0) content_layout.setSpacing(8) # Create all panels self.rgb_top_panel = QualityRGBTopPanel() self.rgb_side_panel = QualityRGBSidePanel() self.thermal_panel = QualityThermalPanel() self.defects_panel = QualityDefectsPanel() self.quality_results_panel = QualityResultsPanel() self.control_panel = QualityControlPanel() self.history_panel = QualityHistoryPanel() # Add panels to grid layout # Row 0, Col 0: RGB Top View Panel content_layout.addWidget(self.rgb_top_panel, 0, 0) # Row 0, Col 1: RGB Side View Panel content_layout.addWidget(self.rgb_side_panel, 0, 1) # Row 1, Col 0: Thermal Analysis Panel content_layout.addWidget(self.thermal_panel, 1, 0) # Row 1, Col 1: Defect Detection Panel content_layout.addWidget(self.defects_panel, 1, 1) # Row 0-1, Col 2: Quality Control Panel (span 2 rows) content_layout.addWidget(self.control_panel, 0, 2, 2, 1) # Row 0-1, Col 3: Quality Results Panel (span 1 row) content_layout.addWidget(self.quality_results_panel, 0, 3) # Row 1, Col 3: Quality History Panel (span 1 row) content_layout.addWidget(self.history_panel, 1, 3) # Set column stretches: [2, 2, 1, 2] (control panel gets less space) content_layout.setColumnStretch(0, 2) content_layout.setColumnStretch(1, 2) content_layout.setColumnStretch(2, 1) content_layout.setColumnStretch(3, 2) layout.addWidget(content_widget, 1) # Status bar at bottom self.status_label = QLabel("Ready for quality assessment. Use Control Panel to analyze samples.") self.status_label.setAlignment(Qt.AlignCenter) self.status_label.setStyleSheet(f"color: {COLORS['text_secondary']}; font-size: 11px; padding: 5px;") layout.addWidget(self.status_label) # Connect control panel signals self._connect_signals() def _initialize_models(self): """Initialize the locule and defect models.""" try: # Get device configuration device = get_device() print(f"Initializing quality models on device: {device}") # Initialize locule model try: self.locule_model = LoculeModel(device=device) if not self.locule_model.load(): print("⚠ Warning: Could not load locule model. Check if 'locule.pt' exists in project root.") self.status_label.setText("Warning: Locule model not loaded. Check model file: locule.pt") self.locule_model = None else: print(f"✓ Locule model loaded successfully on {device}") except Exception as e: print(f"✗ Error initializing locule model: {e}") import traceback traceback.print_exc() self.locule_model = None # Initialize defect model try: self.defect_model = DefectModel(device=device) if not self.defect_model.load(): print("⚠ Warning: Could not load defect model. Check if 'best.pt' exists in project root.") self.status_label.setText("Warning: Defect model not loaded. Check model file: best.pt") self.defect_model = None else: print(f"✓ Defect model loaded successfully on {device}") except Exception as e: print(f"✗ Error initializing defect model: {e}") import traceback traceback.print_exc() self.defect_model = None # Update status if both models loaded if self.locule_model and self.locule_model.is_loaded and self.defect_model and self.defect_model.is_loaded: self.status_label.setText("Ready for quality assessment. Click 'OPEN FILE' to analyze an image.") self.status_label.setStyleSheet(f"color: {COLORS['success']}; font-size: 11px;") except Exception as e: print(f"✗ Error initializing models: {e}") import traceback traceback.print_exc() self.locule_model = None self.defect_model = None self.status_label.setText(f"Error initializing models: {str(e)}") self.status_label.setStyleSheet(f"color: {COLORS.get('error', '#e74c3c')}; font-size: 11px;") def _connect_signals(self): """Connect signals between panels.""" # Connect control panel signals self.control_panel.analyze_requested.connect(self._on_analyze_requested) self.control_panel.open_file_requested.connect(self._on_open_file_requested) self.control_panel.parameter_changed.connect(self._on_parameter_changed) self.control_panel.mode_changed.connect(self._on_mode_changed) # Connect defect panel signals self.defects_panel.annotated_image_requested.connect(self._on_annotated_image_requested) self.defects_panel.defect_image_requested.connect(self._on_defect_image_requested) def _on_analyze_requested(self): """Handle analyze button click from control panel.""" # For now, simulate processing by updating all panels with sample data self._simulate_processing() def _on_parameter_changed(self, parameter_name, value): """Handle parameter changes from control panel.""" # Update status to show parameter change self.status_label.setText(f"Parameter '{parameter_name}' changed to {value}") def _on_mode_changed(self, mode: str): """Handle mode change from control panel.""" if mode == 'file': self.status_label.setText("File mode selected. Click 'OPEN FILE' to select an image.") else: self.status_label.setText("Live mode selected (Coming Soon).") def _on_open_file_requested(self, file_path: str): """Handle file selection from control panel.""" if file_path and file_path.strip(): self._process_image_file(file_path) else: self.status_label.setText("No file selected. Please select an image file.") self.status_label.setStyleSheet(f"color: {COLORS['warning']}; font-size: 11px;") def _on_annotated_image_requested(self): """Handle annotated image request from clicking on locule count.""" # Show the most recent annotated image if available if hasattr(self, 'last_processed_file') and self.last_processed_file: self._show_annotated_image_dialog(self.last_processed_file) else: self.status_label.setText("No processed image available to display.") def _on_defect_image_requested(self): """Handle defect image request from clicking on defect analysis.""" # Show the defect-annotated image if available if hasattr(self, 'last_processed_file') and self.last_processed_file: self._show_defect_annotated_image_dialog(self.last_processed_file) else: self.status_label.setText("No processed image available to display.") def _simulate_processing(self): """Simulate processing for demonstration purposes.""" # Set control panel to analyzing state self.control_panel.set_analyzing(True) # Update status self.status_label.setText("Processing sample...") # Simulate processing delay (in real app, this would be handled by workers) from PyQt5.QtCore import QTimer QTimer.singleShot(2000, self._complete_processing) def _complete_processing(self): """Complete the processing simulation.""" # Reset control panel self.control_panel.set_analyzing(False) # Add result to history from datetime import datetime current_time = datetime.now().strftime("%H:%M:%S") test_id = f"Q-{len(self.history_panel.test_history) + 1:04d}" # Sample result based on current parameters params = self.control_panel.get_parameters() grade = "B" # Default grade score = 78.5 # Default score defects = "2" # Default defect count self.history_panel.add_test_result(current_time, test_id, grade, f"{score:.1f}%", defects) # Update status self.status_label.setText("Processing complete. Results updated.") def _process_image_file(self, file_path: str): """Process the selected image file with quality models.""" import os from pathlib import Path try: # Validate file exists if not os.path.exists(file_path): raise FileNotFoundError(f"File not found: {file_path}") # Validate it's an image file valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.JPG', '.JPEG', '.PNG', '.BMP'] file_ext = Path(file_path).suffix if file_ext not in valid_extensions: raise ValueError(f"Invalid image file format: {file_ext}. Supported formats: {', '.join(valid_extensions)}") # Set loading state self.control_panel.set_analyzing(True) filename = os.path.basename(file_path) self.status_label.setText(f"Processing: {filename}...") self.status_label.setStyleSheet(f"color: {COLORS['warning']}; font-size: 11px;") # Load and display the image in RGB panels self._display_image_in_panels(file_path) # Process with models immediately (no artificial delay) self._analyze_image_with_models(file_path) except Exception as e: error_msg = f"Error processing file: {str(e)}" self.status_label.setText(error_msg) self.status_label.setStyleSheet(f"color: {COLORS.get('error', '#e74c3c')}; font-size: 11px;") self.control_panel.set_analyzing(False) print(f"Error in _process_image_file: {e}") import traceback traceback.print_exc() def _display_image_in_panels(self, file_path: str): """Display the loaded image in the RGB panels.""" from PyQt5.QtGui import QPixmap # Load image as pixmap pixmap = QPixmap(file_path) if pixmap.isNull(): raise ValueError(f"Could not load image: {file_path}") # Scale to fit panels scaled_pixmap = pixmap.scaled( 250, 180, Qt.KeepAspectRatio, Qt.SmoothTransformation ) # Update RGB panels with the loaded image # Note: In a real implementation, you would process the image with models first # and then display the annotated results self.rgb_top_panel.set_image(file_path) self.rgb_side_panel.set_image(file_path) def _analyze_image_with_models(self, file_path: str): """Analyze the image using defect and locule models.""" # Process immediately without artificial delay # The models will handle their own processing time self._complete_image_analysis(file_path) def _complete_image_analysis(self, file_path: str): """Complete the image analysis with both locule and defect model results.""" # Store the file path for later use self.last_processed_file = file_path try: # Initialize results locule_result = None defect_result = None # Check if models are loaded if not self.locule_model or not self.locule_model.is_loaded: print("Warning: Locule model not loaded. Check model path: locule.pt") if not self.defect_model or not self.defect_model.is_loaded: print("Warning: Defect model not loaded. Check model path: best.pt") # Use actual locule model if available if self.locule_model and self.locule_model.is_loaded: try: print(f"Running locule analysis on: {file_path}") locule_result = self.locule_model.predict(file_path) if locule_result['success']: print(f"✓ Locule analysis complete: {locule_result['locule_count']} locules detected") else: print(f"✗ Locule model failed: {locule_result.get('error', 'Unknown error')}") except Exception as e: print(f"Exception during locule prediction: {e}") import traceback traceback.print_exc() else: print("Locule model not available - skipping locule analysis") # Use actual defect model if available if self.defect_model and self.defect_model.is_loaded: try: print(f"Running defect analysis on: {file_path}") defect_result = self.defect_model.predict(file_path) if defect_result['success']: print(f"✓ Defect analysis complete: {defect_result['total_detections']} detections, primary class: {defect_result.get('primary_class', 'N/A')}") else: print(f"✗ Defect model failed: {defect_result.get('error', 'Unknown error')}") except Exception as e: print(f"Exception during defect prediction: {e}") import traceback traceback.print_exc() else: print("Defect model not available - skipping defect analysis") # Update panels with results self._update_panels_with_results(locule_result, defect_result, file_path) except Exception as e: print(f"Error in image analysis: {e}") import traceback traceback.print_exc() self._fallback_analysis(file_path) # Reset control panel self.control_panel.set_analyzing(False) # Update status import os filename = os.path.basename(file_path) self.status_label.setText(f"Analysis complete: {filename}") self.status_label.setStyleSheet(f"color: {COLORS['success']}; font-size: 11px;") # Show both dialogs - locule and defect analysis (only if we have results) if (locule_result and locule_result.get('success')) or (defect_result and defect_result.get('success')): self._show_dual_analysis_dialogs(file_path) def _calculate_quality_grade(self, locule_count: int, has_defects: bool) -> tuple: """ Calculate quality grade based on locule count and defect status. Rules: - 5 locules + no defects = Class A - Less than 5 locules + no defects = Class B - Less than 5 locules + defects = Class C Args: locule_count: Number of locules detected has_defects: True if defects are present, False otherwise Returns: tuple: (grade_letter, score_percentage) """ if locule_count == 5 and not has_defects: return ("A", 95.0) elif locule_count < 5 and not has_defects: return ("B", 85.0) elif locule_count < 5 and has_defects: return ("C", 70.0) else: # Edge cases: 5 locules with defects, or more than 5 locules # Default to B for 5 locules with defects, C for more than 5 with defects if locule_count >= 5 and has_defects: return ("B", 80.0) # Still good locule count but has defects elif locule_count > 5 and not has_defects: return ("B", 85.0) # More than ideal but no defects else: # Fallback return ("B", 75.0) def _update_panels_with_results(self, locule_result, defect_result, file_path: str): """Update all panels with locule and defect model results.""" # Initialize values locule_count = 0 has_defects = False total_detections = 0 primary_class = "No Defects" # Handle locule results if locule_result and locule_result['success']: locule_count = locule_result['locule_count'] locule_detections = locule_result['detections'] # Update the clickable locule count widget with actual AI results self.defects_panel.update_locule_count(locule_count) # Update the locule count metric in quality results self.quality_results_panel.update_locule_count(locule_count, 94.5) else: # Fallback locule count locule_count = 4 self.defects_panel.update_locule_count(locule_count) self.quality_results_panel.update_locule_count(locule_count) # Handle defect results if defect_result and defect_result['success']: total_detections = defect_result['total_detections'] primary_class = defect_result['primary_class'] class_counts = defect_result['class_counts'] defect_detections = defect_result['detections'] # Determine if defects are present # "No Defects" class means no defects, anything else means defects exist has_defects = (primary_class != "No Defects") # Update defect detection panel with actual defect results self.defects_panel.update_defect_count(total_detections, primary_class) # Create defect analysis results for the panel defect_analysis_results = [] # Add individual defect detections for detection in defect_detections: defect_analysis_results.append({ 'type': detection['class_name'], 'confidence': detection['confidence'], 'color': '#f39c12', 'category': 'warning' }) # Add shape analysis (placeholder for now) defect_analysis_results.append({ 'type': 'Shape Analysis', 'result': 'Regular', 'symmetry': '91.2%', 'confidence': 94.1, 'color': '#27ae60', 'category': 'success' }) # Update defect detection panel with all results self.defects_panel.update_defects(defect_analysis_results) else: # Fallback defect analysis - assume no defects if model failed has_defects = False total_detections = 0 primary_class = "No Defects" self.defects_panel.update_defect_count(0, "No defects") self.defects_panel.update_defects([ { 'type': 'Shape Analysis', 'result': 'Regular', 'symmetry': '91.2%', 'confidence': 94.1, 'color': '#27ae60', 'category': 'success' } ]) # Calculate quality grade based on locule count and defect status grade, score = self._calculate_quality_grade(locule_count, has_defects) # Update quality results panel with calculated grade self.quality_results_panel.update_grade(grade, score) # Update history with calculated grade from datetime import datetime current_time = datetime.now().strftime("%H:%M:%S") self.history_panel.add_test_result( time=current_time, test_id=f"Q-{len(self.history_panel.test_history) + 1:04d}", grade=grade, score=f"{score:.1f}%", defects=str(total_detections) ) print(f"Quality Grade Calculated: Grade {grade} (Score: {score:.1f}%) - Locules: {locule_count}, Defects: {'Yes' if has_defects else 'No'}") def _fallback_analysis(self, file_path: str): """Fallback analysis when model is not available.""" # Update locule count widget with fallback data self.defects_panel.update_locule_count(4) # Update locule count in quality results panel self.quality_results_panel.update_locule_count(4) # Generate sample analysis results defects_list = [ { 'type': 'Shape Analysis', 'result': 'Regular', 'symmetry': '91.2%', 'confidence': 94.1, 'color': '#27ae60', 'category': 'success' } ] # Update defect detection panel self.defects_panel.update_defects(defects_list) # Calculate grade for fallback (assume 4 locules, no defects = Grade B) grade, score = self._calculate_quality_grade(4, False) # Update quality results panel with calculated grade self.quality_results_panel.update_grade(grade, score) # Update history from datetime import datetime current_time = datetime.now().strftime("%H:%M:%S") self.history_panel.add_test_result( time=current_time, test_id=f"Q-{len(self.history_panel.test_history) + 1:04d}", grade=grade, score=f"{score:.1f}%", defects="0" ) def _generate_annotated_image(self, original_path: str): """Generate an annotated image using the actual LoculeModel.""" try: # Check if locule model is available if self.locule_model is None or not self.locule_model.is_loaded: print("Locule model not available, using fallback annotation") return self._generate_fallback_annotated_image(original_path) # Use actual locule model for prediction result = self.locule_model.predict(original_path) if result['success']: # Return the actual annotated QImage from the model return self._qimage_to_pixmap(result['annotated_image']) else: print(f"Locule model prediction failed: {result['error']}") return self._generate_fallback_annotated_image(original_path) except Exception as e: print(f"Error in locule model prediction: {e}") return self._generate_fallback_annotated_image(original_path) def _generate_fallback_annotated_image(self, original_path: str): """Fallback annotation method when model is not available.""" try: from PyQt5.QtGui import QPixmap, QPainter, QColor, QPen, QBrush import math # Load original image original_pixmap = QPixmap(original_path) if original_pixmap.isNull(): return None # Create a copy for annotation annotated_pixmap = original_pixmap.copy() # Create painter for annotations painter = QPainter(annotated_pixmap) painter.setRenderHint(QPainter.Antialiasing) # Get image dimensions width = annotated_pixmap.width() height = annotated_pixmap.height() # Draw defect markers (sample data for now) defect_positions = [ (width * 0.3, height * 0.4, "Mechanical Damage"), (width * 0.7, height * 0.6, "Surface Blemish") ] for x, y, defect_type in defect_positions: # Draw defect circle painter.setBrush(QBrush(QColor("#f39c12"))) painter.setPen(QPen(QColor("#e67e22"), 3)) painter.drawEllipse(int(x - 15), int(y - 15), 30, 30) # Draw confidence text painter.setPen(QPen(QColor("white"), 2)) painter.drawText(int(x - 20), int(y - 20), f"87%") # Draw locule counting visualization center_x, center_y = width // 2, height // 2 fruit_radius = min(width, height) // 4 # Draw locule segments locule_colors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12"] num_locules = 4 angle_step = 360 / num_locules for i in range(num_locules): start_angle = int(i * angle_step) span_angle = int(angle_step) # Draw locule segment painter.setBrush(QBrush(QColor(locule_colors[i]))) painter.setPen(QPen(QColor("#34495e"), 2)) painter.drawPie( center_x - fruit_radius, center_y - fruit_radius, fruit_radius * 2, fruit_radius * 2, start_angle * 16, span_angle * 16 ) painter.end() return annotated_pixmap except Exception as e: print(f"Error generating fallback annotated image: {e}") return None def _qimage_to_pixmap(self, qimage): """Convert QImage to QPixmap.""" from PyQt5.QtGui import QPixmap return QPixmap.fromImage(qimage) def _show_annotated_image_dialog(self, file_path: str): """Show dialog with annotated image results.""" try: # Generate annotated image annotated_pixmap = self._generate_annotated_image(file_path) if annotated_pixmap: # Show in preview dialog dialog = ImagePreviewDialog( annotated_pixmap, title=f"Quality Analysis Results - {os.path.basename(file_path)}", parent=self ) dialog.exec_() else: # Fallback to original image original_pixmap = QPixmap(file_path) if not original_pixmap.isNull(): dialog = ImagePreviewDialog( original_pixmap, title=f"Original Image - {os.path.basename(file_path)}", parent=self ) dialog.exec_() except Exception as e: print(f"Error showing image dialog: {e}") def _show_defect_annotated_image_dialog(self, file_path: str): """Show dialog with defect-annotated image results.""" try: # Generate defect-annotated image annotated_pixmap = self._generate_defect_annotated_image(file_path) if annotated_pixmap: # Show in preview dialog dialog = ImagePreviewDialog( annotated_pixmap, title=f"Defect Detection Results - {os.path.basename(file_path)}", parent=self ) dialog.exec_() else: # Fallback to original image original_pixmap = QPixmap(file_path) if not original_pixmap.isNull(): dialog = ImagePreviewDialog( original_pixmap, title=f"Original Image - {os.path.basename(file_path)}", parent=self ) dialog.exec_() except Exception as e: print(f"Error showing defect image dialog: {e}") def _generate_defect_annotated_image(self, original_path: str): """Generate a defect-annotated image using the actual DefectModel.""" try: # Check if defect model is available if self.defect_model is None or not self.defect_model.is_loaded: print("Defect model not available, using fallback annotation") return self._generate_fallback_defect_annotated_image(original_path) # Use actual defect model for prediction result = self.defect_model.predict(original_path) if result['success']: # Return the actual annotated QImage from the model return self._qimage_to_pixmap(result['annotated_image']) else: print(f"Defect model prediction failed: {result['error']}") return self._generate_fallback_defect_annotated_image(original_path) except Exception as e: print(f"Error in defect model prediction: {e}") return self._generate_fallback_defect_annotated_image(original_path) def _generate_fallback_defect_annotated_image(self, original_path: str): """Fallback defect annotation method when model is not available.""" try: from PyQt5.QtGui import QPixmap, QPainter, QColor, QPen, QBrush # Load original image original_pixmap = QPixmap(original_path) if original_pixmap.isNull(): return None # Create a copy for annotation annotated_pixmap = original_pixmap.copy() # Create painter for annotations painter = QPainter(annotated_pixmap) painter.setRenderHint(QPainter.Antialiasing) # Get image dimensions width = annotated_pixmap.width() height = annotated_pixmap.height() # Draw sample defect markers defect_positions = [ (width * 0.3, height * 0.4, "Minor Defect", "#f39c12"), (width * 0.7, height * 0.6, "Surface Blemish", "#e67e22") ] for x, y, defect_type, color_hex in defect_positions: # Draw defect bounding box color = QColor(color_hex) painter.setPen(QPen(color, 3)) painter.setBrush(QBrush(QColor(0, 0, 0, 0))) # Transparent fill # Draw rectangle box_size = 40 painter.drawRect(int(x - box_size/2), int(y - box_size/2), box_size, box_size) # Draw confidence text painter.setPen(QPen(QColor("white"), 2)) painter.drawText(int(x - 20), int(y - 25), f"87%") # Draw label painter.drawText(int(x - 30), int(y + 30), defect_type) painter.end() return annotated_pixmap except Exception as e: print(f"Error generating fallback defect annotated image: {e}") return None def _show_dual_analysis_dialogs(self, file_path: str): """Show both locule and defect analysis dialogs.""" try: # Show locule analysis dialog first self._show_annotated_image_dialog(file_path) # Then show defect analysis dialog self._show_defect_annotated_image_dialog(file_path) except Exception as e: print(f"Error showing dual analysis dialogs: {e}") def set_loading(self, is_loading: bool): """Set loading state for all panels.""" # Update control panel analyzing state self.control_panel.set_analyzing(is_loading) if is_loading: self.status_label.setText("Processing sample...") self.status_label.setStyleSheet(f"color: {COLORS['warning']}; font-size: 11px;") else: self.status_label.setText("Ready for quality assessment.") self.status_label.setStyleSheet(f"color: {COLORS['success']}; font-size: 11px;") def update_results(self, annotated_image: QImage, primary_class: str, class_counts: dict, total_detections: int, file_path: str): """ Update the tab with processing results. This method is kept for compatibility with existing workers. Args: annotated_image: QImage with bounding boxes drawn primary_class: Primary defect class detected class_counts: Dictionary of class counts total_detections: Total number of detections file_path: Path to the image file """ # For now, use the new panel structure # In a real implementation, this would update the appropriate panels # with the processed image data # Update defect detection results defects_list = [] for class_name, count in class_counts.items(): defects_list.append({ 'type': class_name, 'confidence': 85.0, # Placeholder confidence 'color': '#f39c12', 'category': 'warning' }) self.defects_panel.update_defects(defects_list) # Update file path in status import os filename = os.path.basename(file_path) self.status_label.setText(f"Processed: {filename}") self.set_loading(False) def clear_results(self): """Clear all displayed results.""" # Clear defect detection results self.defects_panel.clear_defects() # Clear history selection self.history_panel.history_table.clearSelection() # Reset status self.status_label.setText("Ready for quality assessment. Use Control Panel to analyze samples.") self.status_label.setStyleSheet(f"color: {COLORS['text_secondary']}; font-size: 11px;") def get_panel_status(self): """Get status information from all panels.""" return { 'rgb_top': 'ONLINE', 'rgb_side': 'ONLINE', 'thermal': 'OFFLINE', 'defects': 'ONLINE', 'control': 'READY', 'history': 'READY' }