""" RGB Top View Panel Panel for displaying top-down RGB camera view with defect markers and analysis. """ import os from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont, QPixmap, QImage, QPainter, QColor, QPen, QBrush from ui.widgets.panel_header import PanelHeader class QualityRGBTopPanel(QWidget): """ Panel for RGB top view camera display with defect detection overlays. Shows sample fruit image with defect markers and analysis information. """ def __init__(self, parent=None): super().__init__(parent) self.sample_image = None self.defect_markers = [] self.init_ui() def init_ui(self): """Initialize the panel UI.""" # Set size policy to expand equally self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # Main panel container with card styling self.setStyleSheet(""" QWidget { background-color: white; border: 1px solid #ddd; } """) # Header using the PanelHeader widget header = PanelHeader( title="RGB Top View", color="#3498db" # Blue for RGB ) layout.addWidget(header) # Content area content = QWidget() content.setStyleSheet(""" background-color: #2c3e50; border: 1px solid #34495e; border-top: none; """) content_layout = QVBoxLayout(content) content_layout.setContentsMargins(10, 10, 10, 10) content_layout.setAlignment(Qt.AlignCenter) content_layout.setSpacing(5) # Create sample fruit image with defects self.image_display = SampleFruitWidget() self.image_display.setMinimumSize(200, 150) content_layout.addWidget(self.image_display) # Info labels info_widget = QWidget() info_layout = QVBoxLayout(info_widget) info_layout.setContentsMargins(0, 5, 0, 0) info_layout.setSpacing(2) # Resolution info (grayed out) res_label = QLabel("1920x1080 @ 30fps") res_label.setFont(QFont("Arial", 10)) res_label.setStyleSheet("color: #7f8c8d;") res_label.setAlignment(Qt.AlignCenter) info_layout.addWidget(res_label) # Status info status_label = QLabel("🟢 ONLINE (Live Coming Soon)") status_label.setFont(QFont("Arial", 9)) status_label.setStyleSheet("color: #27ae60; font-weight: bold;") status_label.setAlignment(Qt.AlignCenter) info_layout.addWidget(status_label) content_layout.addWidget(info_widget) layout.addWidget(content, 1) def update_defects(self, defects): """Update the defect markers on the image.""" self.defect_markers = defects self.image_display.update_defects(defects) self.update() def set_image(self, image_path=None): """Set the image to display.""" if image_path and os.path.exists(image_path): try: pixmap = QPixmap(image_path) if not pixmap.isNull(): # Scale to fit display area scaled_pixmap = pixmap.scaled( 240, 170, Qt.KeepAspectRatio, Qt.SmoothTransformation ) # Update display (you would need to modify SampleFruitWidget # to accept external images in a real implementation) self.image_display.update_with_image(scaled_pixmap) # Update status filename = os.path.basename(image_path) self.image_display.setToolTip(f"Loaded: {filename}") except Exception as e: print(f"Error loading image: {e}") else: # Show sample data self.image_display.update() class SampleFruitWidget(QWidget): """Widget showing sample fruit with defect markers.""" def __init__(self, parent=None): super().__init__(parent) self.defect_markers = [] self.setAttribute(Qt.WA_StyledBackground, True) def update_defects(self, defects): """Update defect markers.""" self.defect_markers = defects self.update() def paintEvent(self, event): """Custom paint event to draw fruit, defects, or external image.""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Get widget dimensions width = self.width() height = self.height() # If we have an external image, display it if hasattr(self, 'external_pixmap') and self.external_pixmap: # Draw the external image scaled to fit scaled_pixmap = self.external_pixmap.scaled( width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation ) # Center the image x = (width - scaled_pixmap.width()) // 2 y = (height - scaled_pixmap.height()) // 2 painter.drawPixmap(x, y, scaled_pixmap) else: # Draw default fruit visualization self._draw_fruit_visualization(painter, width, height) def _draw_fruit_visualization(self, painter, width, height): """Draw the default fruit visualization.""" center_x = width // 2 center_y = height // 2 # Draw background painter.fillRect(0, 0, width, height, QColor("#2c3e50")) # Draw main fruit (circular shape) fruit_radius = min(width, height) // 4 fruit_rect = ( center_x - fruit_radius, center_y - fruit_radius, fruit_radius * 2, fruit_radius * 2 ) # Fruit body fruit_color = QColor("#8B4513") # Brown/orange fruit color painter.setBrush(fruit_color) painter.setPen(QPen(QColor("#654321"), 2)) # Darker border painter.drawEllipse(*fruit_rect) # Add some texture/pattern to fruit self._add_fruit_texture(painter, center_x, center_y, fruit_radius) # Draw defect markers if any for i, defect in enumerate(self.defect_markers): self._draw_defect_marker(painter, defect, center_x, center_y, fruit_radius) # Draw some sample defects for demonstration if not self.defect_markers: self._draw_sample_defects(painter, center_x, center_y, fruit_radius) # Draw locule counting visualization self._draw_locule_counting(painter, center_x, center_y, fruit_radius) def _add_fruit_texture(self, painter, center_x, center_y, radius): """Add texture pattern to fruit.""" # Add some darker spots for texture import math for _ in range(5): spot_x = center_x + (-radius//2 + hash(str(_)) % radius) spot_y = center_y + (-radius//2 + hash(str(_*2)) % radius) spot_radius = 3 # Only draw if within fruit bounds distance = math.sqrt((spot_x - center_x) ** 2 + (spot_y - center_y) ** 2) if distance + spot_radius <= radius: painter.setBrush(QBrush(QColor("#654321"))) painter.setPen(Qt.NoPen) painter.drawEllipse(spot_x - spot_radius, spot_y - spot_radius, spot_radius * 2, spot_radius * 2) def _draw_defect_marker(self, painter, defect, center_x, center_y, fruit_radius): """Draw a single defect marker.""" # Position relative to fruit center marker_x = center_x + (defect['x'] * fruit_radius // 50) marker_y = center_y + (defect['y'] * fruit_radius // 50) # Draw marker circle marker_radius = max(8, defect.get('size', 8)) painter.setBrush(QBrush(QColor(defect.get('color', '#f39c12')))) painter.setPen(QPen(QColor('#e67e22'), 2)) painter.drawEllipse(marker_x - marker_radius, marker_y - marker_radius, marker_radius * 2, marker_radius * 2) # Draw confidence text if available confidence = defect.get('confidence', 0) if confidence > 0: painter.setPen(QPen(QColor('white'), 1)) painter.drawText(marker_x - 20, marker_y - marker_radius - 5, f"{confidence:.1f}%") def _draw_sample_defects(self, painter, center_x, center_y, fruit_radius): """Draw sample defects for demonstration.""" sample_defects = [ {'x': -20, 'y': -15, 'color': '#f39c12', 'confidence': 87.3, 'size': 8}, {'x': 25, 'y': 30, 'color': '#f39c12', 'confidence': 72.8, 'size': 6}, ] for defect in sample_defects: self._draw_defect_marker(painter, defect, center_x, center_y, fruit_radius) def _draw_locule_counting(self, painter, center_x, center_y, fruit_radius): """Draw locule counting visualization.""" # Draw locule segments (colored regions within the fruit) locule_colors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6"] # Draw 4 sample locules (colored pie segments) 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) # Create pie segment path painter.setBrush(QBrush(QColor(locule_colors[i % len(locule_colors)]))) 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 # Qt uses 1/16th degree units ) def update(self): """Override update to ensure repaint.""" super().update() self.repaint() def update_with_image(self, pixmap): """Update display with external image.""" self.external_pixmap = pixmap self.has_external_image = True self.update()