""" Ripeness Control Panel Control panel for ripeness testing with device selection and parameters. """ from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox, QCheckBox) from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QFont from ui.widgets.panel_header import PanelHeader from ui.widgets.mode_toggle import ModeToggle from ui.widgets.parameter_slider import ParameterSlider class RipenessControlPanel(QWidget): """ Control panel for ripeness testing. Signals: run_test_clicked: Emitted when RUN TEST button is clicked (live mode) open_file_clicked: Emitted when OPEN FILE is clicked (file mode) stop_clicked: Emitted when STOP button is clicked reset_clicked: Emitted when RESET button is clicked mode_changed: Emitted when test mode changes (str: 'live' or 'file') """ run_test_clicked = pyqtSignal() open_file_clicked = pyqtSignal() stop_clicked = pyqtSignal() reset_clicked = pyqtSignal() mode_changed = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.current_mode = "file" self.init_ui() def init_ui(self): """Initialize the control panel UI.""" 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 header = QWidget() header.setFixedHeight(25) header.setStyleSheet("background-color: #34495e;") header_layout = QHBoxLayout(header) header_layout.setContentsMargins(10, 0, 10, 0) header_layout.setSpacing(0) title = QLabel("Control Panel") title.setStyleSheet("color: white; font-weight: bold; font-size: 16px;") header_layout.addWidget(title) # Content area content = QWidget() content.setStyleSheet(""" background-color: white; border: none; """) content_layout = QVBoxLayout(content) content_layout.setSpacing(10) content_layout.setContentsMargins(10, 10, 10, 10) # Camera Selection camera_label = QLabel("Camera Selection:") camera_label.setFont(QFont("Arial", 9, QFont.Bold)) camera_label.setStyleSheet("color: #2c3e50;") content_layout.addWidget(camera_label) self.camera_combo = QComboBox() self.camera_combo.addItems(["Multispectral (Primary) ▼"]) self.camera_combo.setFont(QFont("Arial", 9)) self.camera_combo.setFixedHeight(25) self.camera_combo.setEnabled(False) self.camera_combo.setToolTip("Camera selection - Coming with hardware integration") self.camera_combo.setStyleSheet(""" QComboBox { background-color: #ecf0f1; border: 1px solid #bdc3c7; padding: 3px; color: #7f8c8d; } """) content_layout.addWidget(self.camera_combo) # Microphone Selection mic_label = QLabel("Microphone:") mic_label.setFont(QFont("Arial", 9, QFont.Bold)) mic_label.setStyleSheet("color: #2c3e50;") content_layout.addWidget(mic_label) self.mic_combo = QComboBox() self.mic_combo.addItems([ "Piezo Sensor Array ▼", "Built-in Microphone", "USB Audio Device" ]) self.mic_combo.setFont(QFont("Arial", 9)) self.mic_combo.setFixedHeight(25) self.mic_combo.setStyleSheet(""" QComboBox { background-color: #ecf0f1; border: 1px solid #bdc3c7; padding: 3px; } """) content_layout.addWidget(self.mic_combo) # Parameters Section params_label = QLabel("Parameters:") params_label.setFont(QFont("Arial", 9, QFont.Bold)) params_label.setStyleSheet("color: #2c3e50; margin-top: 5px;") content_layout.addWidget(params_label) # Exposure slider (disabled - coming soon) self.exposure_slider = ParameterSlider("Exposure", 1, 250, 125, "1/{}s") self.exposure_slider.setEnabled(False) self.exposure_slider.setToolTip("Exposure control - Coming in future update") content_layout.addWidget(self.exposure_slider) # Gain slider (disabled - coming soon) self.gain_slider = ParameterSlider("Gain", 100, 1600, 400, "ISO {}") self.gain_slider.setEnabled(False) self.gain_slider.setToolTip("Gain control - Coming in future update") content_layout.addWidget(self.gain_slider) # Preprocessing Options preproc_label = QLabel("Preprocessing:") preproc_label.setFont(QFont("Arial", 9, QFont.Bold)) preproc_label.setStyleSheet("color: #2c3e50; margin-top: 5px;") content_layout.addWidget(preproc_label) # Checkboxes self.normalize_checkbox = QCheckBox("Normalize Spectral Data") self.normalize_checkbox.setFont(QFont("Arial", 9)) self.normalize_checkbox.setEnabled(False) self.normalize_checkbox.setToolTip("Spectral data normalization - Coming soon") content_layout.addWidget(self.normalize_checkbox) self.denoise_checkbox = QCheckBox("Denoise Audio Signal") self.denoise_checkbox.setFont(QFont("Arial", 9)) self.denoise_checkbox.setChecked(True) self.denoise_checkbox.setToolTip("Apply noise reduction to audio signal") content_layout.addWidget(self.denoise_checkbox) self.bg_subtract_checkbox = QCheckBox("Background Subtraction") self.bg_subtract_checkbox.setFont(QFont("Arial", 9)) self.bg_subtract_checkbox.setEnabled(False) self.bg_subtract_checkbox.setToolTip("Background subtraction - Coming soon") content_layout.addWidget(self.bg_subtract_checkbox) # Test Mode Toggle mode_label = QLabel("Test Mode:") mode_label.setFont(QFont("Arial", 9, QFont.Bold)) mode_label.setStyleSheet("color: #2c3e50; margin-top: 5px;") content_layout.addWidget(mode_label) self.mode_toggle = ModeToggle() self.mode_toggle.mode_changed.connect(self._on_mode_changed) content_layout.addWidget(self.mode_toggle) # Control Buttons # RUN TEST button self.run_btn = QPushButton("RUN TEST") self.run_btn.setFont(QFont("Arial", 11, QFont.Bold)) self.run_btn.setFixedHeight(32) self.run_btn.setStyleSheet(""" QPushButton { background-color: #27ae60; border: 2px solid #229954; color: white; } QPushButton:hover { background-color: #229954; } QPushButton:pressed { background-color: #1e8449; } """) self.run_btn.clicked.connect(self._on_primary_action_clicked) self.run_btn.setToolTip("Select an audio file to analyze ripeness") content_layout.addWidget(self.run_btn) # STOP and RESET buttons bottom_buttons = QHBoxLayout() bottom_buttons.setSpacing(10) self.stop_btn = QPushButton("STOP") self.stop_btn.setFont(QFont("Arial", 8, QFont.Bold)) self.stop_btn.setFixedHeight(22) self.stop_btn.setStyleSheet(""" QPushButton { background-color: #e74c3c; border: 1px solid #c0392b; color: white; } QPushButton:hover { background-color: #c0392b; } """) self.stop_btn.clicked.connect(self.stop_clicked.emit) self.stop_btn.setEnabled(False) bottom_buttons.addWidget(self.stop_btn) self.reset_btn = QPushButton("RESET") self.reset_btn.setFont(QFont("Arial", 8, QFont.Bold)) self.reset_btn.setFixedHeight(22) self.reset_btn.setStyleSheet(""" QPushButton { background-color: #f39c12; border: 1px solid #e67e22; color: white; } QPushButton:hover { background-color: #e67e22; } """) self.reset_btn.clicked.connect(self.reset_clicked.emit) bottom_buttons.addWidget(self.reset_btn) content_layout.addLayout(bottom_buttons) content_layout.addStretch() layout.addWidget(header) layout.addWidget(content) # Initialize primary action label based on default mode self._update_primary_action_label() def set_processing(self, is_processing: bool): """ Set the processing state. Args: is_processing: Whether processing is active """ self.run_btn.setEnabled(not is_processing) self.stop_btn.setEnabled(is_processing) if is_processing: self.run_btn.setText("PROCESSING...") else: self._update_primary_action_label() def _on_mode_changed(self, mode: str): """Handle mode change from the toggle.""" self.current_mode = mode self.mode_changed.emit(mode) self._update_primary_action_label() def _update_primary_action_label(self): """Update primary action button label and tooltip based on mode.""" if getattr(self, 'current_mode', 'file') == 'file': self.run_btn.setText("OPEN FILE") self.run_btn.setToolTip("Open an audio file to analyze ripeness") else: self.run_btn.setText("RUN TEST") self.run_btn.setToolTip("Run live ripeness test (coming soon)") def _on_primary_action_clicked(self): """Emit the appropriate signal based on current mode.""" if getattr(self, 'current_mode', 'file') == 'file': self.open_file_clicked.emit() else: self.run_test_clicked.emit()