ripeness_control_panel.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. """
  2. Ripeness Control Panel
  3. Control panel for ripeness testing with device selection and parameters.
  4. """
  5. from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
  6. QPushButton, QComboBox, QCheckBox)
  7. from PyQt5.QtCore import Qt, pyqtSignal
  8. from PyQt5.QtGui import QFont
  9. from ui.widgets.panel_header import PanelHeader
  10. from ui.widgets.mode_toggle import ModeToggle
  11. from ui.widgets.parameter_slider import ParameterSlider
  12. class RipenessControlPanel(QWidget):
  13. """
  14. Control panel for ripeness testing.
  15. Signals:
  16. run_test_clicked: Emitted when RUN TEST button is clicked (live mode)
  17. open_file_clicked: Emitted when OPEN FILE is clicked (file mode)
  18. stop_clicked: Emitted when STOP button is clicked
  19. reset_clicked: Emitted when RESET button is clicked
  20. mode_changed: Emitted when test mode changes (str: 'live' or 'file')
  21. """
  22. run_test_clicked = pyqtSignal()
  23. open_file_clicked = pyqtSignal()
  24. stop_clicked = pyqtSignal()
  25. reset_clicked = pyqtSignal()
  26. mode_changed = pyqtSignal(str)
  27. def __init__(self, parent=None):
  28. super().__init__(parent)
  29. self.current_mode = "file"
  30. self.init_ui()
  31. def init_ui(self):
  32. """Initialize the control panel UI."""
  33. layout = QVBoxLayout(self)
  34. layout.setContentsMargins(0, 0, 0, 0)
  35. layout.setSpacing(0)
  36. # Main panel container with card styling
  37. self.setStyleSheet("""
  38. QWidget {
  39. background-color: white;
  40. border: 1px solid #ddd;
  41. }
  42. """)
  43. # Header
  44. header = QWidget()
  45. header.setFixedHeight(25)
  46. header.setStyleSheet("background-color: #34495e;")
  47. header_layout = QHBoxLayout(header)
  48. header_layout.setContentsMargins(10, 0, 10, 0)
  49. header_layout.setSpacing(0)
  50. title = QLabel("Control Panel")
  51. title.setStyleSheet("color: white; font-weight: bold; font-size: 16px;")
  52. header_layout.addWidget(title)
  53. # Content area
  54. content = QWidget()
  55. content.setStyleSheet("""
  56. background-color: white;
  57. border: none;
  58. """)
  59. content_layout = QVBoxLayout(content)
  60. content_layout.setSpacing(10)
  61. content_layout.setContentsMargins(10, 10, 10, 10)
  62. # Camera Selection
  63. camera_label = QLabel("Camera Selection:")
  64. camera_label.setFont(QFont("Arial", 9, QFont.Bold))
  65. camera_label.setStyleSheet("color: #2c3e50;")
  66. content_layout.addWidget(camera_label)
  67. self.camera_combo = QComboBox()
  68. self.camera_combo.addItems(["Multispectral (Primary) ▼"])
  69. self.camera_combo.setFont(QFont("Arial", 9))
  70. self.camera_combo.setFixedHeight(25)
  71. self.camera_combo.setEnabled(False)
  72. self.camera_combo.setToolTip("Camera selection - Coming with hardware integration")
  73. self.camera_combo.setStyleSheet("""
  74. QComboBox {
  75. background-color: #ecf0f1;
  76. border: 1px solid #bdc3c7;
  77. padding: 3px;
  78. color: #7f8c8d;
  79. }
  80. """)
  81. content_layout.addWidget(self.camera_combo)
  82. # Microphone Selection
  83. mic_label = QLabel("Microphone:")
  84. mic_label.setFont(QFont("Arial", 9, QFont.Bold))
  85. mic_label.setStyleSheet("color: #2c3e50;")
  86. content_layout.addWidget(mic_label)
  87. self.mic_combo = QComboBox()
  88. self.mic_combo.addItems([
  89. "Piezo Sensor Array ▼",
  90. "Built-in Microphone",
  91. "USB Audio Device"
  92. ])
  93. self.mic_combo.setFont(QFont("Arial", 9))
  94. self.mic_combo.setFixedHeight(25)
  95. self.mic_combo.setStyleSheet("""
  96. QComboBox {
  97. background-color: #ecf0f1;
  98. border: 1px solid #bdc3c7;
  99. padding: 3px;
  100. }
  101. """)
  102. content_layout.addWidget(self.mic_combo)
  103. # Parameters Section
  104. params_label = QLabel("Parameters:")
  105. params_label.setFont(QFont("Arial", 9, QFont.Bold))
  106. params_label.setStyleSheet("color: #2c3e50; margin-top: 5px;")
  107. content_layout.addWidget(params_label)
  108. # Exposure slider (disabled - coming soon)
  109. self.exposure_slider = ParameterSlider("Exposure", 1, 250, 125, "1/{}s")
  110. self.exposure_slider.setEnabled(False)
  111. self.exposure_slider.setToolTip("Exposure control - Coming in future update")
  112. content_layout.addWidget(self.exposure_slider)
  113. # Gain slider (disabled - coming soon)
  114. self.gain_slider = ParameterSlider("Gain", 100, 1600, 400, "ISO {}")
  115. self.gain_slider.setEnabled(False)
  116. self.gain_slider.setToolTip("Gain control - Coming in future update")
  117. content_layout.addWidget(self.gain_slider)
  118. # Preprocessing Options
  119. preproc_label = QLabel("Preprocessing:")
  120. preproc_label.setFont(QFont("Arial", 9, QFont.Bold))
  121. preproc_label.setStyleSheet("color: #2c3e50; margin-top: 5px;")
  122. content_layout.addWidget(preproc_label)
  123. # Checkboxes
  124. self.normalize_checkbox = QCheckBox("Normalize Spectral Data")
  125. self.normalize_checkbox.setFont(QFont("Arial", 9))
  126. self.normalize_checkbox.setEnabled(False)
  127. self.normalize_checkbox.setToolTip("Spectral data normalization - Coming soon")
  128. content_layout.addWidget(self.normalize_checkbox)
  129. self.denoise_checkbox = QCheckBox("Denoise Audio Signal")
  130. self.denoise_checkbox.setFont(QFont("Arial", 9))
  131. self.denoise_checkbox.setChecked(True)
  132. self.denoise_checkbox.setToolTip("Apply noise reduction to audio signal")
  133. content_layout.addWidget(self.denoise_checkbox)
  134. self.bg_subtract_checkbox = QCheckBox("Background Subtraction")
  135. self.bg_subtract_checkbox.setFont(QFont("Arial", 9))
  136. self.bg_subtract_checkbox.setEnabled(False)
  137. self.bg_subtract_checkbox.setToolTip("Background subtraction - Coming soon")
  138. content_layout.addWidget(self.bg_subtract_checkbox)
  139. # Test Mode Toggle
  140. mode_label = QLabel("Test Mode:")
  141. mode_label.setFont(QFont("Arial", 9, QFont.Bold))
  142. mode_label.setStyleSheet("color: #2c3e50; margin-top: 5px;")
  143. content_layout.addWidget(mode_label)
  144. self.mode_toggle = ModeToggle()
  145. self.mode_toggle.mode_changed.connect(self._on_mode_changed)
  146. content_layout.addWidget(self.mode_toggle)
  147. # Control Buttons
  148. # RUN TEST button
  149. self.run_btn = QPushButton("RUN TEST")
  150. self.run_btn.setFont(QFont("Arial", 11, QFont.Bold))
  151. self.run_btn.setFixedHeight(32)
  152. self.run_btn.setStyleSheet("""
  153. QPushButton {
  154. background-color: #27ae60;
  155. border: 2px solid #229954;
  156. color: white;
  157. }
  158. QPushButton:hover {
  159. background-color: #229954;
  160. }
  161. QPushButton:pressed {
  162. background-color: #1e8449;
  163. }
  164. """)
  165. self.run_btn.clicked.connect(self._on_primary_action_clicked)
  166. self.run_btn.setToolTip("Select an audio file to analyze ripeness")
  167. content_layout.addWidget(self.run_btn)
  168. # STOP and RESET buttons
  169. bottom_buttons = QHBoxLayout()
  170. bottom_buttons.setSpacing(10)
  171. self.stop_btn = QPushButton("STOP")
  172. self.stop_btn.setFont(QFont("Arial", 8, QFont.Bold))
  173. self.stop_btn.setFixedHeight(22)
  174. self.stop_btn.setStyleSheet("""
  175. QPushButton {
  176. background-color: #e74c3c;
  177. border: 1px solid #c0392b;
  178. color: white;
  179. }
  180. QPushButton:hover {
  181. background-color: #c0392b;
  182. }
  183. """)
  184. self.stop_btn.clicked.connect(self.stop_clicked.emit)
  185. self.stop_btn.setEnabled(False)
  186. bottom_buttons.addWidget(self.stop_btn)
  187. self.reset_btn = QPushButton("RESET")
  188. self.reset_btn.setFont(QFont("Arial", 8, QFont.Bold))
  189. self.reset_btn.setFixedHeight(22)
  190. self.reset_btn.setStyleSheet("""
  191. QPushButton {
  192. background-color: #f39c12;
  193. border: 1px solid #e67e22;
  194. color: white;
  195. }
  196. QPushButton:hover {
  197. background-color: #e67e22;
  198. }
  199. """)
  200. self.reset_btn.clicked.connect(self.reset_clicked.emit)
  201. bottom_buttons.addWidget(self.reset_btn)
  202. content_layout.addLayout(bottom_buttons)
  203. content_layout.addStretch()
  204. layout.addWidget(header)
  205. layout.addWidget(content)
  206. # Initialize primary action label based on default mode
  207. self._update_primary_action_label()
  208. def set_processing(self, is_processing: bool):
  209. """
  210. Set the processing state.
  211. Args:
  212. is_processing: Whether processing is active
  213. """
  214. self.run_btn.setEnabled(not is_processing)
  215. self.stop_btn.setEnabled(is_processing)
  216. if is_processing:
  217. self.run_btn.setText("PROCESSING...")
  218. else:
  219. self._update_primary_action_label()
  220. def _on_mode_changed(self, mode: str):
  221. """Handle mode change from the toggle."""
  222. self.current_mode = mode
  223. self.mode_changed.emit(mode)
  224. self._update_primary_action_label()
  225. def _update_primary_action_label(self):
  226. """Update primary action button label and tooltip based on mode."""
  227. if getattr(self, 'current_mode', 'file') == 'file':
  228. self.run_btn.setText("OPEN FILE")
  229. self.run_btn.setToolTip("Open an audio file to analyze ripeness")
  230. else:
  231. self.run_btn.setText("RUN TEST")
  232. self.run_btn.setToolTip("Run live ripeness test (coming soon)")
  233. def _on_primary_action_clicked(self):
  234. """Emit the appropriate signal based on current mode."""
  235. if getattr(self, 'current_mode', 'file') == 'file':
  236. self.open_file_clicked.emit()
  237. else:
  238. self.run_test_clicked.emit()