quality_rgb_top_panel.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. """
  2. RGB Top View Panel
  3. Panel for displaying top-down RGB camera view with defect markers and analysis.
  4. """
  5. import os
  6. from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy
  7. from PyQt5.QtCore import Qt
  8. from PyQt5.QtGui import QFont, QPixmap, QImage, QPainter, QColor, QPen, QBrush
  9. from ui.widgets.panel_header import PanelHeader
  10. class QualityRGBTopPanel(QWidget):
  11. """
  12. Panel for RGB top view camera display with defect detection overlays.
  13. Shows sample fruit image with defect markers and analysis information.
  14. """
  15. def __init__(self, parent=None):
  16. super().__init__(parent)
  17. self.sample_image = None
  18. self.defect_markers = []
  19. self.init_ui()
  20. def init_ui(self):
  21. """Initialize the panel UI."""
  22. # Set size policy to expand equally
  23. self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
  24. layout = QVBoxLayout(self)
  25. layout.setContentsMargins(0, 0, 0, 0)
  26. layout.setSpacing(0)
  27. # Main panel container with card styling
  28. self.setStyleSheet("""
  29. QWidget {
  30. background-color: white;
  31. border: 1px solid #ddd;
  32. }
  33. """)
  34. # Header using the PanelHeader widget
  35. header = PanelHeader(
  36. title="RGB Top View",
  37. color="#3498db" # Blue for RGB
  38. )
  39. layout.addWidget(header)
  40. # Content area
  41. content = QWidget()
  42. content.setStyleSheet("""
  43. background-color: #2c3e50;
  44. border: 1px solid #34495e;
  45. border-top: none;
  46. """)
  47. content_layout = QVBoxLayout(content)
  48. content_layout.setContentsMargins(10, 10, 10, 10)
  49. content_layout.setAlignment(Qt.AlignCenter)
  50. content_layout.setSpacing(5)
  51. # Create sample fruit image with defects
  52. self.image_display = SampleFruitWidget()
  53. self.image_display.setMinimumSize(200, 150)
  54. content_layout.addWidget(self.image_display)
  55. # Info labels
  56. info_widget = QWidget()
  57. info_layout = QVBoxLayout(info_widget)
  58. info_layout.setContentsMargins(0, 5, 0, 0)
  59. info_layout.setSpacing(2)
  60. # Resolution info (grayed out)
  61. res_label = QLabel("1920x1080 @ 30fps")
  62. res_label.setFont(QFont("Arial", 10))
  63. res_label.setStyleSheet("color: #7f8c8d;")
  64. res_label.setAlignment(Qt.AlignCenter)
  65. info_layout.addWidget(res_label)
  66. # Status info
  67. status_label = QLabel("🟢 ONLINE (Live Coming Soon)")
  68. status_label.setFont(QFont("Arial", 9))
  69. status_label.setStyleSheet("color: #27ae60; font-weight: bold;")
  70. status_label.setAlignment(Qt.AlignCenter)
  71. info_layout.addWidget(status_label)
  72. content_layout.addWidget(info_widget)
  73. layout.addWidget(content, 1)
  74. def update_defects(self, defects):
  75. """Update the defect markers on the image."""
  76. self.defect_markers = defects
  77. self.image_display.update_defects(defects)
  78. self.update()
  79. def set_image(self, image_path=None):
  80. """Set the image to display."""
  81. if image_path and os.path.exists(image_path):
  82. try:
  83. pixmap = QPixmap(image_path)
  84. if not pixmap.isNull():
  85. # Scale to fit display area
  86. scaled_pixmap = pixmap.scaled(
  87. 240, 170, Qt.KeepAspectRatio, Qt.SmoothTransformation
  88. )
  89. # Update display (you would need to modify SampleFruitWidget
  90. # to accept external images in a real implementation)
  91. self.image_display.update_with_image(scaled_pixmap)
  92. # Update status
  93. filename = os.path.basename(image_path)
  94. self.image_display.setToolTip(f"Loaded: {filename}")
  95. except Exception as e:
  96. print(f"Error loading image: {e}")
  97. else:
  98. # Show sample data
  99. self.image_display.update()
  100. class SampleFruitWidget(QWidget):
  101. """Widget showing sample fruit with defect markers."""
  102. def __init__(self, parent=None):
  103. super().__init__(parent)
  104. self.defect_markers = []
  105. self.setAttribute(Qt.WA_StyledBackground, True)
  106. def update_defects(self, defects):
  107. """Update defect markers."""
  108. self.defect_markers = defects
  109. self.update()
  110. def paintEvent(self, event):
  111. """Custom paint event to draw fruit, defects, or external image."""
  112. painter = QPainter(self)
  113. painter.setRenderHint(QPainter.Antialiasing)
  114. # Get widget dimensions
  115. width = self.width()
  116. height = self.height()
  117. # If we have an external image, display it
  118. if hasattr(self, 'external_pixmap') and self.external_pixmap:
  119. # Draw the external image scaled to fit
  120. scaled_pixmap = self.external_pixmap.scaled(
  121. width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation
  122. )
  123. # Center the image
  124. x = (width - scaled_pixmap.width()) // 2
  125. y = (height - scaled_pixmap.height()) // 2
  126. painter.drawPixmap(x, y, scaled_pixmap)
  127. else:
  128. # Draw default fruit visualization
  129. self._draw_fruit_visualization(painter, width, height)
  130. def _draw_fruit_visualization(self, painter, width, height):
  131. """Draw the default fruit visualization."""
  132. center_x = width // 2
  133. center_y = height // 2
  134. # Draw background
  135. painter.fillRect(0, 0, width, height, QColor("#2c3e50"))
  136. # Draw main fruit (circular shape)
  137. fruit_radius = min(width, height) // 4
  138. fruit_rect = (
  139. center_x - fruit_radius,
  140. center_y - fruit_radius,
  141. fruit_radius * 2,
  142. fruit_radius * 2
  143. )
  144. # Fruit body
  145. fruit_color = QColor("#8B4513") # Brown/orange fruit color
  146. painter.setBrush(fruit_color)
  147. painter.setPen(QPen(QColor("#654321"), 2)) # Darker border
  148. painter.drawEllipse(*fruit_rect)
  149. # Add some texture/pattern to fruit
  150. self._add_fruit_texture(painter, center_x, center_y, fruit_radius)
  151. # Draw defect markers if any
  152. for i, defect in enumerate(self.defect_markers):
  153. self._draw_defect_marker(painter, defect, center_x, center_y, fruit_radius)
  154. # Draw some sample defects for demonstration
  155. if not self.defect_markers:
  156. self._draw_sample_defects(painter, center_x, center_y, fruit_radius)
  157. # Draw locule counting visualization
  158. self._draw_locule_counting(painter, center_x, center_y, fruit_radius)
  159. def _add_fruit_texture(self, painter, center_x, center_y, radius):
  160. """Add texture pattern to fruit."""
  161. # Add some darker spots for texture
  162. import math
  163. for _ in range(5):
  164. spot_x = center_x + (-radius//2 + hash(str(_)) % radius)
  165. spot_y = center_y + (-radius//2 + hash(str(_*2)) % radius)
  166. spot_radius = 3
  167. # Only draw if within fruit bounds
  168. distance = math.sqrt((spot_x - center_x) ** 2 + (spot_y - center_y) ** 2)
  169. if distance + spot_radius <= radius:
  170. painter.setBrush(QBrush(QColor("#654321")))
  171. painter.setPen(Qt.NoPen)
  172. painter.drawEllipse(spot_x - spot_radius, spot_y - spot_radius,
  173. spot_radius * 2, spot_radius * 2)
  174. def _draw_defect_marker(self, painter, defect, center_x, center_y, fruit_radius):
  175. """Draw a single defect marker."""
  176. # Position relative to fruit center
  177. marker_x = center_x + (defect['x'] * fruit_radius // 50)
  178. marker_y = center_y + (defect['y'] * fruit_radius // 50)
  179. # Draw marker circle
  180. marker_radius = max(8, defect.get('size', 8))
  181. painter.setBrush(QBrush(QColor(defect.get('color', '#f39c12'))))
  182. painter.setPen(QPen(QColor('#e67e22'), 2))
  183. painter.drawEllipse(marker_x - marker_radius, marker_y - marker_radius,
  184. marker_radius * 2, marker_radius * 2)
  185. # Draw confidence text if available
  186. confidence = defect.get('confidence', 0)
  187. if confidence > 0:
  188. painter.setPen(QPen(QColor('white'), 1))
  189. painter.drawText(marker_x - 20, marker_y - marker_radius - 5,
  190. f"{confidence:.1f}%")
  191. def _draw_sample_defects(self, painter, center_x, center_y, fruit_radius):
  192. """Draw sample defects for demonstration."""
  193. sample_defects = [
  194. {'x': -20, 'y': -15, 'color': '#f39c12', 'confidence': 87.3, 'size': 8},
  195. {'x': 25, 'y': 30, 'color': '#f39c12', 'confidence': 72.8, 'size': 6},
  196. ]
  197. for defect in sample_defects:
  198. self._draw_defect_marker(painter, defect, center_x, center_y, fruit_radius)
  199. def _draw_locule_counting(self, painter, center_x, center_y, fruit_radius):
  200. """Draw locule counting visualization."""
  201. # Draw locule segments (colored regions within the fruit)
  202. locule_colors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6"]
  203. # Draw 4 sample locules (colored pie segments)
  204. num_locules = 4
  205. angle_step = 360 / num_locules
  206. for i in range(num_locules):
  207. start_angle = int(i * angle_step)
  208. span_angle = int(angle_step)
  209. # Create pie segment path
  210. painter.setBrush(QBrush(QColor(locule_colors[i % len(locule_colors)])))
  211. painter.setPen(QPen(QColor("#34495e"), 2))
  212. painter.drawPie(
  213. center_x - fruit_radius, center_y - fruit_radius,
  214. fruit_radius * 2, fruit_radius * 2,
  215. start_angle * 16, span_angle * 16 # Qt uses 1/16th degree units
  216. )
  217. def update(self):
  218. """Override update to ensure repaint."""
  219. super().update()
  220. self.repaint()
  221. def update_with_image(self, pixmap):
  222. """Update display with external image."""
  223. self.external_pixmap = pixmap
  224. self.has_external_image = True
  225. self.update()