""" Device Panel — UI for selecting audio input/output devices. """ from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QPushButton, QGroupBox, QMessageBox) from PyQt6.QtCore import pyqtSignal from core.audio_device import DeviceManager class DevicePanel(QWidget): # Signal emitted when devices are successfully applied devices_changed = pyqtSignal() def __init__(self, device_manager: DeviceManager): super().__init__() self.device_manager = device_manager self._init_ui() self.refresh_device_lists() def _init_ui(self): layout = QVBoxLayout() layout.setSpacing(20) # --- Audio Hardware Settings Group --- hw_group = QGroupBox("Audio Hardware Setup") hw_layout = QVBoxLayout() # Info text info_lbl = QLabel( "Select the sound card inputs and outputs connected to your cassette deck.\n" "• Connect PC Output to Tape Deck REC IN\n" "• Connect Tape Deck PLAY OUT to PC Input" ) info_lbl.setWordWrap(True) info_lbl.setStyleSheet("color: #a0a0b0; font-style: italic; margin-bottom: 10px;") hw_layout.addWidget(info_lbl) # Input Device Row in_row = QHBoxLayout() in_lbl = QLabel("Recording Input (From Tape):") in_lbl.setFixedWidth(180) self.in_combo = QComboBox() self.in_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) in_row.addWidget(in_lbl) in_row.addWidget(self.in_combo, 1) hw_layout.addLayout(in_row) # Output Device Row out_row = QHBoxLayout() out_lbl = QLabel("Playback Output (To Tape):") out_lbl.setFixedWidth(180) self.out_combo = QComboBox() self.out_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) out_row.addWidget(out_lbl) out_row.addWidget(self.out_combo, 1) hw_layout.addLayout(out_row) # Sample Rate Row sr_row = QHBoxLayout() sr_lbl = QLabel("Sample Rate:") sr_lbl.setFixedWidth(180) self.sr_combo = QComboBox() self.sr_combo.addItems(["44100 Hz", "48000 Hz", "96000 Hz"]) self.sr_combo.setCurrentText("44100 Hz") sr_row.addWidget(sr_lbl) sr_row.addWidget(self.sr_combo) sr_row.addStretch() hw_layout.addLayout(sr_row) hw_group.setLayout(hw_layout) layout.addWidget(hw_group) # --- Actions Row --- action_row = QHBoxLayout() self.refresh_btn = QPushButton("Refresh Devices") self.refresh_btn.clicked.connect(self.refresh_device_lists) self.apply_btn = QPushButton("Apply Settings") self.apply_btn.setObjectName("primaryButton") self.apply_btn.clicked.connect(self.apply_settings) action_row.addWidget(self.refresh_btn) action_row.addStretch() action_row.addWidget(self.apply_btn) layout.addLayout(action_row) layout.addStretch() self.setLayout(layout) def refresh_device_lists(self): """Update the dropdowns with available devices.""" self.device_manager.refresh_devices() self.in_combo.clear() for dev in self.device_manager.get_input_devices(): self.in_combo.addItem(str(dev), dev.index) self.out_combo.clear() for dev in self.device_manager.get_output_devices(): self.out_combo.addItem(str(dev), dev.index) # Try to select current if set if self.device_manager.input_device_id is not None: idx = self.in_combo.findData(self.device_manager.input_device_id) if idx >= 0: self.in_combo.setCurrentIndex(idx) if self.device_manager.output_device_id is not None: idx = self.out_combo.findData(self.device_manager.output_device_id) if idx >= 0: self.out_combo.setCurrentIndex(idx) def apply_settings(self): """Apply selected devices to the DeviceManager.""" in_idx = self.in_combo.currentData() out_idx = self.out_combo.currentData() sr_str = self.sr_combo.currentText().split()[0] sr = int(sr_str) try: if in_idx is not None: self.device_manager.input_device_id = in_idx if out_idx is not None: self.device_manager.output_device_id = out_idx self.device_manager.sample_rate = sr self.devices_changed.emit() # Change button appearance briefly to show success original_text = self.apply_btn.text() self.apply_btn.setText("Applied ✓") self.apply_btn.setStyleSheet("background-color: #4CAF50; color: white;") # Reset after 2 seconds import PyQt6.QtCore as QtCore QtCore.QTimer.singleShot(2000, lambda: self._reset_apply_btn(original_text)) except Exception as e: QMessageBox.critical(self, "Device Error", f"Failed to apply audio settings:\n{str(e)}") def _reset_apply_btn(self, text): self.apply_btn.setText(text) self.apply_btn.setStyleSheet("") # Revert to stylesheet default