init commit
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
"""Dialog for calibration/reference set selection and sequential capture."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
from PyQt6.QtWidgets import (
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QFormLayout,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPlainTextEdit,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
)
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
from python_app.models.dataset_model import TraceData
|
||||
|
||||
|
||||
class PreprocessDialog(QDialog):
|
||||
"""Standalone dialog for preprocessing capture workflows."""
|
||||
|
||||
refresh_requested = pyqtSignal()
|
||||
selection_changed = pyqtSignal(str, str)
|
||||
start_sequence_requested = pyqtSignal(str)
|
||||
capture_next_requested = pyqtSignal()
|
||||
abort_sequence_requested = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
"""Create dialog and build all widgets."""
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Preprocessing Setup")
|
||||
self.resize(1040, 760)
|
||||
self._build_ui()
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
"""Build dialog layout, controls, and preview plot."""
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
sets_group = QGroupBox("Calibration / Reference Sets", self)
|
||||
sets_layout = QGridLayout(sets_group)
|
||||
|
||||
self._set_name_input = QLineEdit("set_001", sets_group)
|
||||
self._calibration_combo = QComboBox(sets_group)
|
||||
self._reference_combo = QComboBox(sets_group)
|
||||
|
||||
refresh_button = QPushButton("Refresh Sets", sets_group)
|
||||
refresh_button.clicked.connect(self.refresh_requested.emit)
|
||||
|
||||
self._calibration_combo.currentTextChanged.connect(self._emit_selection_changed)
|
||||
self._reference_combo.currentTextChanged.connect(self._emit_selection_changed)
|
||||
|
||||
sets_layout.addWidget(QLabel("Set name"), 0, 0)
|
||||
sets_layout.addWidget(self._set_name_input, 0, 1)
|
||||
sets_layout.addWidget(refresh_button, 0, 2)
|
||||
|
||||
sets_layout.addWidget(QLabel("Calibration set"), 1, 0)
|
||||
sets_layout.addWidget(self._calibration_combo, 1, 1, 1, 2)
|
||||
|
||||
sets_layout.addWidget(QLabel("Reference set"), 2, 0)
|
||||
sets_layout.addWidget(self._reference_combo, 2, 1, 1, 2)
|
||||
|
||||
layout.addWidget(sets_group)
|
||||
|
||||
sequence_group = QGroupBox("Sequential Capture (Fill Full N*M)", self)
|
||||
sequence_layout = QGridLayout(sequence_group)
|
||||
|
||||
self._active_kind_label = QLabel("<none>", sequence_group)
|
||||
self._progress_label = QLabel("0 / 0", sequence_group)
|
||||
self._combo_label = QLabel("<none>", sequence_group)
|
||||
|
||||
self._tx_antenna_label_input = QLineEdit(sequence_group)
|
||||
self._rx_antenna_label_input = QLineEdit(sequence_group)
|
||||
self._tx_antenna_label_input.setPlaceholderText("e.g. TX_A")
|
||||
self._rx_antenna_label_input.setPlaceholderText("e.g. RX_B")
|
||||
|
||||
start_calibration_button = QPushButton("Start Calibration Sequence", sequence_group)
|
||||
start_calibration_button.clicked.connect(lambda: self.start_sequence_requested.emit("calibration"))
|
||||
|
||||
start_reference_button = QPushButton("Start Reference Sequence", sequence_group)
|
||||
start_reference_button.clicked.connect(lambda: self.start_sequence_requested.emit("reference"))
|
||||
|
||||
self._capture_next_button = QPushButton("Capture Current Combo", sequence_group)
|
||||
self._capture_next_button.clicked.connect(self.capture_next_requested.emit)
|
||||
self._capture_next_button.setEnabled(False)
|
||||
|
||||
self._abort_button = QPushButton("Abort Sequence", sequence_group)
|
||||
self._abort_button.clicked.connect(self.abort_sequence_requested.emit)
|
||||
self._abort_button.setEnabled(False)
|
||||
|
||||
button_row = QHBoxLayout()
|
||||
button_row.addWidget(start_calibration_button)
|
||||
button_row.addWidget(start_reference_button)
|
||||
button_row.addWidget(self._capture_next_button)
|
||||
button_row.addWidget(self._abort_button)
|
||||
|
||||
sequence_layout.addWidget(QLabel("Active type"), 0, 0)
|
||||
sequence_layout.addWidget(self._active_kind_label, 0, 1)
|
||||
sequence_layout.addWidget(QLabel("Progress"), 1, 0)
|
||||
sequence_layout.addWidget(self._progress_label, 1, 1)
|
||||
sequence_layout.addWidget(QLabel("Current combo"), 2, 0)
|
||||
sequence_layout.addWidget(self._combo_label, 2, 1)
|
||||
sequence_layout.addWidget(QLabel("TX antenna label"), 3, 0)
|
||||
sequence_layout.addWidget(self._tx_antenna_label_input, 3, 1)
|
||||
sequence_layout.addWidget(QLabel("RX antenna label"), 4, 0)
|
||||
sequence_layout.addWidget(self._rx_antenna_label_input, 4, 1)
|
||||
sequence_layout.addLayout(button_row, 5, 0, 1, 2)
|
||||
|
||||
self._capture_log = QPlainTextEdit(sequence_group)
|
||||
self._capture_log.setReadOnly(True)
|
||||
self._capture_log.setPlaceholderText("Capture history per combo")
|
||||
sequence_layout.addWidget(self._capture_log, 6, 0, 1, 2)
|
||||
|
||||
layout.addWidget(sequence_group)
|
||||
|
||||
self._status = QLabel("Ready", self)
|
||||
layout.addWidget(self._status)
|
||||
|
||||
self._plot = pg.PlotWidget(background="#101418")
|
||||
self._plot.showGrid(x=True, y=True, alpha=0.2)
|
||||
self._plot.setLabel("bottom", "Frequency", units="Hz")
|
||||
self._plot.setLabel("left", "Magnitude", units="dB")
|
||||
layout.addWidget(self._plot, stretch=1)
|
||||
|
||||
def set_name(self) -> str:
|
||||
"""Return requested target set name."""
|
||||
return self._set_name_input.text().strip()
|
||||
|
||||
def calibration_set(self) -> str:
|
||||
"""Return currently selected calibration set."""
|
||||
return self._calibration_combo.currentText().strip()
|
||||
|
||||
def reference_set(self) -> str:
|
||||
"""Return currently selected reference set."""
|
||||
return self._reference_combo.currentText().strip()
|
||||
|
||||
def antenna_labels(self) -> tuple[str, str]:
|
||||
"""Return optional TX/RX user labels used in capture logs."""
|
||||
return self._tx_antenna_label_input.text().strip(), self._rx_antenna_label_input.text().strip()
|
||||
|
||||
def clear_capture_log(self) -> None:
|
||||
"""Clear capture history text box."""
|
||||
self._capture_log.clear()
|
||||
|
||||
def append_capture_log_entry(
|
||||
self,
|
||||
*,
|
||||
kind: str,
|
||||
captured_count: int,
|
||||
total_count: int,
|
||||
input_pos: int,
|
||||
output_pos: int,
|
||||
tx_label: str,
|
||||
rx_label: str,
|
||||
) -> None:
|
||||
"""Append one capture progress row to dialog log."""
|
||||
tx_info = tx_label or "-"
|
||||
rx_info = rx_label or "-"
|
||||
self._capture_log.appendPlainText(
|
||||
f"{kind}: {captured_count}/{total_count} | "
|
||||
f"input={input_pos} output={output_pos} | "
|
||||
f"TX={tx_info} RX={rx_info}"
|
||||
)
|
||||
|
||||
def set_capture_state(
|
||||
self,
|
||||
*,
|
||||
kind: str | None,
|
||||
captured_count: int,
|
||||
total_count: int,
|
||||
next_input: int | None,
|
||||
next_output: int | None,
|
||||
) -> None:
|
||||
"""Update sequence progress/status widgets."""
|
||||
if kind is None:
|
||||
self._active_kind_label.setText("<none>")
|
||||
self._progress_label.setText("0 / 0")
|
||||
self._combo_label.setText("<none>")
|
||||
self._capture_next_button.setEnabled(False)
|
||||
self._abort_button.setEnabled(False)
|
||||
return
|
||||
|
||||
self._active_kind_label.setText(kind)
|
||||
self._progress_label.setText(f"{captured_count} / {total_count}")
|
||||
if next_input is None or next_output is None:
|
||||
self._combo_label.setText("<complete>")
|
||||
self._capture_next_button.setEnabled(False)
|
||||
self._abort_button.setEnabled(True)
|
||||
else:
|
||||
self._combo_label.setText(f"input={next_input}, output={next_output}")
|
||||
self._capture_next_button.setEnabled(True)
|
||||
self._abort_button.setEnabled(True)
|
||||
|
||||
def set_calibration_sets(self, names: list[str]) -> None:
|
||||
"""Replace calibration set choices while preserving current selection when possible."""
|
||||
self._set_combo_items(self._calibration_combo, names, self.calibration_set())
|
||||
|
||||
def set_reference_sets(self, names: list[str]) -> None:
|
||||
"""Replace reference set choices while preserving current selection when possible."""
|
||||
self._set_combo_items(self._reference_combo, names, self.reference_set())
|
||||
|
||||
def set_selected_sets(self, calibration_set: str, reference_set: str) -> None:
|
||||
"""Apply selected set names to both comboboxes and emit selection update."""
|
||||
if calibration_set:
|
||||
index = self._calibration_combo.findText(calibration_set)
|
||||
if index >= 0:
|
||||
self._calibration_combo.setCurrentIndex(index)
|
||||
|
||||
if reference_set:
|
||||
index = self._reference_combo.findText(reference_set)
|
||||
if index >= 0:
|
||||
self._reference_combo.setCurrentIndex(index)
|
||||
|
||||
self._emit_selection_changed()
|
||||
|
||||
def set_status(self, message: str) -> None:
|
||||
"""Set short human-readable status line."""
|
||||
self._status.setText(message)
|
||||
|
||||
def draw_last_trace(self, trace: TraceData, title: str) -> None:
|
||||
"""Draw the latest captured sweep trace in dB scale."""
|
||||
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(trace.s21), 1e-12))
|
||||
self._plot.clear()
|
||||
self._plot.plot(
|
||||
trace.frequency_hz,
|
||||
magnitude_db,
|
||||
pen=pg.mkPen("#4cc9f0", width=1.8),
|
||||
)
|
||||
combo = trace.combo
|
||||
self._status.setText(
|
||||
f"{title}: input={combo.input_pos}, output={combo.output_pos}, points={trace.frequency_hz.size}"
|
||||
)
|
||||
|
||||
def _emit_selection_changed(self) -> None:
|
||||
"""Emit current calibration/reference selection."""
|
||||
self.selection_changed.emit(self.calibration_set(), self.reference_set())
|
||||
|
||||
@staticmethod
|
||||
def _set_combo_items(combo: QComboBox, names: list[str], current_text: str) -> None:
|
||||
"""Replace combo contents and keep previous value when still available."""
|
||||
combo.clear()
|
||||
combo.addItems(names)
|
||||
if not current_text:
|
||||
return
|
||||
index = combo.findText(current_text)
|
||||
if index >= 0:
|
||||
combo.setCurrentIndex(index)
|
||||
Reference in New Issue
Block a user