""" Maturity Control Panel Control panel for multispectral maturity 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 MaturityControlPanel(QWidget): """ Control panel for multispectral maturity 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 Camera (MSC2-NIR8-1-A) ▼"]) self.camera_combo.setFont(QFont("Arial", 9)) self.camera_combo.setFixedHeight(25) self.camera_combo.setEnabled(False) self.camera_combo.setToolTip("Multispectral 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) # Band Selection (for future use) band_label = QLabel("Active Bands:") band_label.setFont(QFont("Arial", 9, QFont.Bold)) band_label.setStyleSheet("color: #2c3e50;") content_layout.addWidget(band_label) self.band_combo = QComboBox() self.band_combo.addItems(["All 8 Bands ▼"]) self.band_combo.setFont(QFont("Arial", 9)) self.band_combo.setFixedHeight(25) self.band_combo.setEnabled(False) self.band_combo.setToolTip("Band selection - Coming in future update") self.band_combo.setStyleSheet(""" QComboBox { background-color: #ecf0f1; border: 1px solid #bdc3c7; padding: 3px; color: #7f8c8d; } """) content_layout.addWidget(self.band_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) # Mask band slider (disabled - coming soon) self.mask_band_slider = ParameterSlider("Mask Band", 0, 7, 4, "Band {}") self.mask_band_slider.setEnabled(False) self.mask_band_slider.setToolTip("Band index for masking (default: 4, 860nm)") content_layout.addWidget(self.mask_band_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.setChecked(True) self.normalize_checkbox.setEnabled(False) self.normalize_checkbox.setToolTip("Spectral data normalization (always enabled)") content_layout.addWidget(self.normalize_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) self.gradcam_checkbox = QCheckBox("Generate Grad-CAM") self.gradcam_checkbox.setFont(QFont("Arial", 9)) self.gradcam_checkbox.setChecked(True) self.gradcam_checkbox.setToolTip("Generate Grad-CAM visualization overlay") content_layout.addWidget(self.gradcam_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("OPEN FILE") self.run_btn.setFont(QFont("Arial", 11, QFont.Bold)) self.run_btn.setFixedHeight(32) self.run_btn.setStyleSheet(""" QPushButton { background-color: #8e44ad; border: 2px solid #7d3c98; color: white; } QPushButton:hover { background-color: #7d3c98; } QPushButton:pressed { background-color: #6c3483; } """) self.run_btn.clicked.connect(self._on_primary_action_clicked) self.run_btn.setToolTip("Select a multispectral TIFF file to analyze maturity") 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 a multispectral TIFF file to analyze maturity") else: self.run_btn.setText("RUN TEST") self.run_btn.setToolTip("Run live maturity 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()