| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- """
- Manual Input Dialog
- Dialog for manual camera input from different sources.
- """
- from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
- QPushButton, QLineEdit, QFileDialog, QGroupBox,
- QFormLayout, QMessageBox, QCheckBox, QWidget)
- from PyQt5.QtCore import Qt, pyqtSignal
- from PyQt5.QtGui import QPixmap
- from pathlib import Path
- from resources.styles import GROUP_BOX_STYLE
- class ManualInputDialog(QDialog):
- """
- Dialog for collecting manual camera inputs.
-
- Allows users to select files from different camera sources:
- - DSLR (RGB images)
- - Multispectral camera
- - Thermal camera
-
- Signals:
- inputs_confirmed: Emitted when user confirms inputs with paths dict
- """
-
- inputs_confirmed = pyqtSignal(dict) # Emits dict of {source: file_path}
-
- def __init__(self, parent=None):
- """Initialize the manual input dialog."""
- super().__init__(parent)
- self.setWindowTitle("Manual Camera Input")
- self.setModal(True)
- self.setMinimumWidth(900)
- self.setMinimumHeight(700)
-
- self.inputs = {
- 'dslr_side': '', # Side view for defect model
- 'dslr_top': '', # Top view for locule counter
- 'multispectral': '',
- 'thermal': '',
- 'audio': ''
- }
-
- # Get project root directory
- from pathlib import Path
- self.project_root = Path(__file__).parent.parent.parent # Navigate to project root
-
- self.init_ui()
-
- def init_ui(self):
- """Initialize the UI components."""
- layout = QVBoxLayout(self)
-
- # Instructions
- instructions = QLabel(
- "Please select data from camera and sensor sources.\n"
- "At least one source is required to proceed. All inputs are optional."
- )
- instructions.setWordWrap(True)
- instructions.setStyleSheet("font-size: 16px; color: #555; padding: 10px;")
- layout.addWidget(instructions)
-
- # DSLR Input Group
- dslr_group = QGroupBox("DSLR Camera (RGB)")
- dslr_group.setStyleSheet(GROUP_BOX_STYLE)
- dslr_layout = QFormLayout()
-
- # Side View (Plain) - for defect model
- self.dslr_side_path_edit = QLineEdit()
- self.dslr_side_path_edit.setPlaceholderText("No file selected...")
- self.dslr_side_path_edit.setReadOnly(True)
- self.dslr_side_path_edit.setMinimumHeight(30)
- self.dslr_side_path_edit.setStyleSheet("font-size: 14px; padding: 5px;")
-
- dslr_side_btn = QPushButton("Browse...")
- dslr_side_btn.clicked.connect(lambda: self.select_file('dslr_side'))
- dslr_side_btn.setMinimumHeight(35)
- dslr_side_btn.setStyleSheet("font-size: 14px; font-weight: bold;")
-
- dslr_side_layout = QHBoxLayout()
- dslr_side_layout.addWidget(self.dslr_side_path_edit, 1)
- dslr_side_layout.addWidget(dslr_side_btn)
-
- side_label = QLabel("Side View (Plain):")
- side_label.setStyleSheet("font-size: 15px; font-weight: bold;")
- dslr_layout.addRow(side_label, dslr_side_layout)
-
- # Top View (RGB) - for locule counter
- self.dslr_top_path_edit = QLineEdit()
- self.dslr_top_path_edit.setPlaceholderText("No file selected...")
- self.dslr_top_path_edit.setReadOnly(True)
- self.dslr_top_path_edit.setMinimumHeight(30)
- self.dslr_top_path_edit.setStyleSheet("font-size: 14px; padding: 5px;")
-
- dslr_top_btn = QPushButton("Browse...")
- dslr_top_btn.clicked.connect(lambda: self.select_file('dslr_top'))
- dslr_top_btn.setMinimumHeight(35)
- dslr_top_btn.setStyleSheet("font-size: 14px; font-weight: bold;")
-
- dslr_top_layout = QHBoxLayout()
- dslr_top_layout.addWidget(self.dslr_top_path_edit, 1)
- dslr_top_layout.addWidget(dslr_top_btn)
-
- top_label = QLabel("Top View (RGB):")
- top_label.setStyleSheet("font-size: 15px; font-weight: bold;")
- dslr_layout.addRow(top_label, dslr_top_layout)
-
- dslr_group.setLayout(dslr_layout)
- layout.addWidget(dslr_group)
-
- # Multispectral Input Group
- multi_group = QGroupBox("Multispectral Camera (2nd Look)")
- multi_group.setStyleSheet(GROUP_BOX_STYLE)
- multi_layout = QFormLayout()
-
- self.multi_path_edit = QLineEdit()
- self.multi_path_edit.setPlaceholderText("No file selected...")
- self.multi_path_edit.setReadOnly(True)
- self.multi_path_edit.setMinimumHeight(30)
- self.multi_path_edit.setStyleSheet("font-size: 14px; padding: 5px;")
-
- multi_btn = QPushButton("Browse...")
- multi_btn.clicked.connect(lambda: self.select_file('multispectral'))
- multi_btn.setMinimumHeight(35)
- multi_btn.setStyleSheet("font-size: 14px; font-weight: bold;")
-
- multi_input_layout = QHBoxLayout()
- multi_input_layout.addWidget(self.multi_path_edit, 1)
- multi_input_layout.addWidget(multi_btn)
-
- multi_label = QLabel("TIFF File:")
- multi_label.setStyleSheet("font-size: 15px; font-weight: bold;")
- multi_layout.addRow(multi_label, multi_input_layout)
- multi_group.setLayout(multi_layout)
- layout.addWidget(multi_group)
-
- # Thermal Input Group
- thermal_group = QGroupBox("Thermal Camera (AnalyzIR)")
- thermal_group.setStyleSheet(GROUP_BOX_STYLE)
- thermal_layout = QFormLayout()
-
- self.thermal_path_edit = QLineEdit()
- self.thermal_path_edit.setPlaceholderText("No file selected...")
- self.thermal_path_edit.setReadOnly(True)
- self.thermal_path_edit.setMinimumHeight(30)
- self.thermal_path_edit.setStyleSheet("font-size: 14px; padding: 5px;")
-
- thermal_btn = QPushButton("Browse...")
- thermal_btn.clicked.connect(lambda: self.select_file('thermal'))
- thermal_btn.setMinimumHeight(35)
- thermal_btn.setStyleSheet("font-size: 14px; font-weight: bold;")
-
- thermal_input_layout = QHBoxLayout()
- thermal_input_layout.addWidget(self.thermal_path_edit, 1)
- thermal_input_layout.addWidget(thermal_btn)
-
- thermal_label = QLabel("CSV File:")
- thermal_label.setStyleSheet("font-size: 15px; font-weight: bold;")
- thermal_layout.addRow(thermal_label, thermal_input_layout)
- thermal_group.setLayout(thermal_layout)
- layout.addWidget(thermal_group)
-
- # Audio Input Group
- audio_group = QGroupBox("Audio/Sound Sensor")
- audio_group.setStyleSheet(GROUP_BOX_STYLE)
- audio_layout = QFormLayout()
-
- self.audio_path_edit = QLineEdit()
- self.audio_path_edit.setPlaceholderText("No file selected...")
- self.audio_path_edit.setReadOnly(True)
- self.audio_path_edit.setMinimumHeight(30)
- self.audio_path_edit.setStyleSheet("font-size: 14px; padding: 5px;")
-
- audio_btn = QPushButton("Browse...")
- audio_btn.clicked.connect(lambda: self.select_file('audio'))
- audio_btn.setMinimumHeight(35)
- audio_btn.setStyleSheet("font-size: 14px; font-weight: bold;")
-
- audio_input_layout = QHBoxLayout()
- audio_input_layout.addWidget(self.audio_path_edit, 1)
- audio_input_layout.addWidget(audio_btn)
-
- audio_label = QLabel("WAV File:")
- audio_label.setStyleSheet("font-size: 15px; font-weight: bold;")
- audio_layout.addRow(audio_label, audio_input_layout)
- audio_group.setLayout(audio_layout)
- layout.addWidget(audio_group)
-
- layout.addStretch()
-
- # Button box
- button_layout = QHBoxLayout()
- button_layout.addStretch()
-
- cancel_btn = QPushButton("Cancel")
- cancel_btn.clicked.connect(self.reject)
- cancel_btn.setMinimumWidth(100)
- cancel_btn.setMinimumHeight(40)
- cancel_btn.setStyleSheet("font-size: 15px; font-weight: bold;")
- button_layout.addWidget(cancel_btn)
-
- confirm_btn = QPushButton("Confirm")
- confirm_btn.clicked.connect(self.confirm_inputs)
- confirm_btn.setStyleSheet("""
- QPushButton {
- background-color: #27ae60;
- color: white;
- font-weight: bold;
- font-size: 15px;
- padding: 10px 16px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #229954;
- }
- """)
- confirm_btn.setMinimumWidth(100)
- confirm_btn.setMinimumHeight(40)
- button_layout.addWidget(confirm_btn)
-
- layout.addLayout(button_layout)
-
- def select_file(self, source: str):
- """
- Open file dialog for specific camera source.
-
- Args:
- source: Camera source ('dslr_side', 'dslr_top', 'multispectral', 'thermal', 'audio')
- """
- if source == 'dslr_side':
- title = "Select DSLR Side View Image"
- filters = "Image Files (*.jpg *.jpeg *.png *.bmp);;All Files (*.*)"
- edit_widget = self.dslr_side_path_edit
- elif source == 'dslr_top':
- title = "Select DSLR Top View Image (RGB)"
- filters = "Image Files (*.jpg *.jpeg *.png *.bmp);;All Files (*.*)"
- edit_widget = self.dslr_top_path_edit
- elif source == 'multispectral':
- title = "Select Multispectral TIFF"
- filters = "TIFF Files (*.tif *.tiff);;All Files (*.*)"
- edit_widget = self.multi_path_edit
- elif source == 'thermal':
- title = "Select Thermal CSV"
- filters = "CSV Files (*.csv);;All Files (*.*)"
- edit_widget = self.thermal_path_edit
- elif source == 'audio':
- title = "Select Audio File"
- filters = "Audio Files (*.wav);;All Files (*.*)"
- edit_widget = self.audio_path_edit
- else:
- return
-
- try:
- file_path, _ = QFileDialog.getOpenFileName(
- self,
- title,
- str(self.project_root),
- filters,
- options=QFileDialog.DontUseNativeDialog
- )
-
- if file_path and Path(file_path).exists():
- self.inputs[source] = file_path
- edit_widget.setText(file_path)
- except Exception as e:
- print(f"Error in file dialog: {e}")
-
- def confirm_inputs(self):
- """Validate and confirm inputs."""
- # Check if at least one input is provided (side view or multispectral counts)
- has_input = (
- bool(self.inputs.get('dslr_side')) or
- bool(self.inputs.get('dslr_top')) or
- bool(self.inputs.get('multispectral')) or
- bool(self.inputs.get('thermal')) or
- bool(self.inputs.get('audio'))
- )
-
- if not has_input:
- QMessageBox.warning(
- self,
- "No Input Selected",
- "Please select at least one camera input before confirming."
- )
- return
-
- # Emit signal with inputs
- self.inputs_confirmed.emit(self.inputs)
- self.accept()
- class CameraAppCheckDialog(QDialog):
- """
- Dialog to inform user about missing camera applications.
- """
-
- def __init__(self, missing_apps: list, parent=None):
- """
- Initialize the dialog.
-
- Args:
- missing_apps: List of missing application names
- parent: Parent widget
- """
- super().__init__(parent)
- self.setWindowTitle("Camera Applications Not Found")
- self.setModal(True)
- self.setMinimumWidth(500)
- self.setMinimumHeight(350)
-
- self.missing_apps = missing_apps
- self.init_ui()
-
- def init_ui(self):
- """Initialize the UI components."""
- layout = QVBoxLayout(self)
-
- # Warning icon and message
- message = QLabel(
- "The following camera applications are not currently running:\n"
- )
- message.setStyleSheet("font-size: 15px; font-weight: bold; color: #e74c3c;")
- layout.addWidget(message)
-
- # List of missing apps
- for app in self.missing_apps:
- app_label = QLabel(f" • {app}")
- app_label.setStyleSheet("font-size: 14px; color: #555; padding-left: 20px;")
- layout.addWidget(app_label)
-
- # Instructions
- instructions = QLabel(
- "\nPlease ensure these applications are opened and running "
- "before attempting automated camera capture.\n\n"
- "You can either:\n"
- "1. Open the required applications and try again\n"
- "2. Use Manual Input mode instead"
- )
- instructions.setWordWrap(True)
- instructions.setStyleSheet("font-size: 13px; color: #666; padding: 10px;")
- layout.addWidget(instructions)
-
- layout.addStretch()
-
- # OK button
- button_layout = QHBoxLayout()
- button_layout.addStretch()
-
- ok_btn = QPushButton("OK")
- ok_btn.clicked.connect(self.accept)
- ok_btn.setMinimumWidth(100)
- ok_btn.setStyleSheet("""
- QPushButton {
- padding: 8px 16px;
- background-color: #3498db;
- color: white;
- font-weight: bold;
- font-size: 14px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #2980b9;
- }
- """)
- button_layout.addWidget(ok_btn)
-
- layout.addLayout(button_layout)
|