multispectral_panel.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. """
  2. Multispectral Analysis Panel
  3. Panel for displaying multispectral camera analysis (coming soon feature).
  4. """
  5. from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy
  6. from PyQt5.QtCore import Qt
  7. from PyQt5.QtGui import QFont, QPixmap, QImage, QCursor
  8. from ui.widgets.panel_header import PanelHeader
  9. from ui.widgets.coming_soon_overlay import ComingSoonOverlay
  10. from ui.dialogs.image_preview_dialog import ImagePreviewDialog
  11. class MultispectralPanel(QWidget):
  12. """
  13. Panel for multispectral camera analysis.
  14. Currently shows "COMING SOON" placeholder for future camera integration.
  15. """
  16. def __init__(self, parent=None):
  17. super().__init__(parent)
  18. self.init_ui()
  19. def init_ui(self):
  20. """Initialize the panel UI."""
  21. # Set size policy to expand equally
  22. self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
  23. layout = QVBoxLayout(self)
  24. layout.setContentsMargins(0, 0, 0, 0)
  25. layout.setSpacing(0)
  26. # Main panel container with card styling
  27. self.setStyleSheet("""
  28. QWidget {
  29. background-color: white;
  30. border: 1px solid #ddd;
  31. }
  32. """)
  33. # Header
  34. header = QWidget()
  35. header.setFixedHeight(25)
  36. header.setStyleSheet("background-color: #8e44ad;")
  37. header_layout = QHBoxLayout(header)
  38. header_layout.setContentsMargins(10, 0, 10, 0)
  39. header_layout.setSpacing(0)
  40. title = QLabel("Multispectral Analysis")
  41. title.setStyleSheet("color: white; font-weight: bold; font-size: 16px;")
  42. status_indicator = QWidget()
  43. status_indicator.setFixedSize(10, 10)
  44. status_indicator.setStyleSheet("background-color: #27ae60; border-radius: 5px;")
  45. header_layout.addWidget(title)
  46. header_layout.addStretch()
  47. header_layout.addWidget(status_indicator)
  48. # Content area
  49. self.content = QWidget()
  50. self.content.setMinimumSize(250, 250)
  51. # content.setMaximumSize(400, 400)
  52. self.content.setStyleSheet("""
  53. background-color: #8e44ad;
  54. border: 1px solid #9b59b6;
  55. border-top: none;
  56. """)
  57. content_layout = QVBoxLayout(self.content)
  58. content_layout.setAlignment(Qt.AlignCenter)
  59. content_layout.setSpacing(5)
  60. # Image display label (initially hidden)
  61. self.image_label = QLabel()
  62. self.image_label.setAlignment(Qt.AlignCenter)
  63. self.image_label.setScaledContents(False) # We'll scale manually for better control
  64. self.image_label.setMinimumSize(250, 250)
  65. self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
  66. self.image_label.setStyleSheet("""
  67. QLabel {
  68. background-color: #2c3e50;
  69. border: 2px solid #34495e;
  70. }
  71. """)
  72. self.image_label.hide()
  73. self.image_label.setCursor(QCursor(Qt.PointingHandCursor))
  74. self.image_label.mousePressEvent = self._on_image_clicked
  75. self.current_pixmap = None # Store the original pixmap for dialog
  76. content_layout.addWidget(self.image_label, 1) # Give it stretch factor
  77. # Main text container (shown by default)
  78. self.text_container = QWidget()
  79. text_layout = QVBoxLayout(self.text_container)
  80. text_layout.setAlignment(Qt.AlignCenter)
  81. text_layout.setSpacing(2)
  82. # Spectral data text
  83. data_label = QLabel("8-Band Spectral Data")
  84. data_label.setFont(QFont("Arial", 12))
  85. data_label.setStyleSheet("color: #e8daef;")
  86. data_label.setAlignment(Qt.AlignCenter)
  87. text_layout.addWidget(data_label)
  88. # NIR enhancement text
  89. enhancement_label = QLabel("NIR Enhancement Active\n(Coming soon)")
  90. enhancement_label.setFont(QFont("Arial", 10))
  91. enhancement_label.setStyleSheet("color: #d5a6df;")
  92. enhancement_label.setAlignment(Qt.AlignCenter)
  93. text_layout.addWidget(enhancement_label)
  94. content_layout.addWidget(self.text_container)
  95. # Band selection area
  96. bands_frame = QWidget()
  97. bands_frame.setMaximumHeight(40)
  98. bands_layout = QHBoxLayout(bands_frame)
  99. bands_layout.setContentsMargins(10, 5, 10, 5)
  100. bands_label = QLabel("Active Bands:")
  101. bands_label.setStyleSheet("font-weight: bold; font-size: 12px;")
  102. bands_layout.addWidget(bands_label)
  103. bands_data = [
  104. ("680nm", "#e74c3c"),
  105. ("750nm", "#f39c12"),
  106. ("850nm", "#27ae60"),
  107. ("950nm", "#3498db")
  108. ]
  109. for band_name, color in bands_data:
  110. band_frame = QWidget()
  111. band_layout = QHBoxLayout(band_frame)
  112. band_layout.setContentsMargins(0, 0, 0, 0)
  113. band_layout.setSpacing(5)
  114. color_box = QWidget()
  115. color_box.setFixedSize(20, 12)
  116. color_box.setStyleSheet(f"background-color: {color};")
  117. name_label = QLabel(band_name)
  118. name_label.setStyleSheet("font-size: 12px;")
  119. band_layout.addWidget(color_box)
  120. band_layout.addWidget(name_label)
  121. bands_layout.addWidget(band_frame)
  122. bands_layout.addStretch()
  123. layout.addWidget(header)
  124. layout.addWidget(self.content, 1)
  125. layout.addWidget(bands_frame, 0)
  126. # Tooltip
  127. self.setToolTip("Multispectral camera analysis - Coming with hardware integration")
  128. def set_image(self, pixmap: QPixmap):
  129. """Display an image in the panel."""
  130. if pixmap and not pixmap.isNull():
  131. # Store the original pixmap for dialog display
  132. self.current_pixmap = pixmap
  133. # Show the image label
  134. self.image_label.show()
  135. self.text_container.hide()
  136. # Update the pixmap - use a timer to ensure layout is complete
  137. from PyQt5.QtCore import QTimer
  138. QTimer.singleShot(50, self._update_image_display)
  139. # Also try immediate update in case widget is already sized
  140. self._update_image_display()
  141. # Update tooltip to indicate clickability
  142. self.image_label.setToolTip("Click to view full-size image")
  143. def _update_image_display(self):
  144. """Update the image display with proper scaling."""
  145. if self.current_pixmap and not self.current_pixmap.isNull() and self.image_label.isVisible():
  146. # Get the current size of the label
  147. label_size = self.image_label.size()
  148. if label_size.width() <= 0 or label_size.height() <= 0:
  149. # If not yet sized, use minimum size
  150. label_size = self.image_label.minimumSize()
  151. # Calculate scaled size maintaining aspect ratio
  152. scaled_pixmap = self.current_pixmap.scaled(
  153. label_size.width(),
  154. label_size.height(),
  155. Qt.KeepAspectRatio,
  156. Qt.SmoothTransformation
  157. )
  158. self.image_label.setPixmap(scaled_pixmap)
  159. def resizeEvent(self, event):
  160. """Handle resize events to update image scaling."""
  161. super().resizeEvent(event)
  162. if self.current_pixmap and not self.current_pixmap.isNull():
  163. # Use QTimer to update after layout is complete
  164. from PyQt5.QtCore import QTimer
  165. QTimer.singleShot(10, self._update_image_display)
  166. def update_image(self, pixmap: QPixmap):
  167. """Update the displayed image (alias for set_image)."""
  168. self.set_image(pixmap)
  169. def clear(self):
  170. """Clear the image display and show default text."""
  171. self.image_label.clear()
  172. self.image_label.hide()
  173. self.text_container.show()
  174. self.current_pixmap = None
  175. def _on_image_clicked(self, event):
  176. """Handle click on image to show full-size dialog."""
  177. if self.current_pixmap and not self.current_pixmap.isNull():
  178. dialog = ImagePreviewDialog(
  179. self.current_pixmap,
  180. title="Multispectral Analysis - Grad-CAM Visualization",
  181. parent=self
  182. )
  183. dialog.exec_()
  184. def _create_band_indicator(self, wavelength: str, color: str) -> QWidget:
  185. """Create a band indicator widget."""
  186. widget = QWidget()
  187. layout = QHBoxLayout(widget)
  188. layout.setContentsMargins(0, 0, 0, 0)
  189. layout.setSpacing(3)
  190. # Color box
  191. color_box = QLabel()
  192. color_box.setFixedSize(20, 12)
  193. color_box.setStyleSheet(f"background-color: {color}; border: 1px solid #2c3e50;")
  194. layout.addWidget(color_box)
  195. # Wavelength label
  196. wavelength_label = QLabel(wavelength)
  197. wavelength_label.setFont(QFont("Arial", 8))
  198. wavelength_label.setStyleSheet("color: #2c3e50;")
  199. layout.addWidget(wavelength_label)
  200. return widget