init commit

This commit is contained in:
Ayzen
2026-03-05 14:42:33 +03:00
commit fd4618b20d
964 changed files with 325114 additions and 0 deletions
@@ -0,0 +1,19 @@
"""Composable UI section builders used by AppWindow UI mixin."""
from python_app.gui.controllers.sections.data_actions_section import build_data_actions_group
from python_app.gui.controllers.sections.hardware_actions_section import build_hardware_actions_group
from python_app.gui.controllers.sections.pipeline_section import build_pipeline_group
from python_app.gui.controllers.sections.preprocess_summary_section import build_preprocess_summary_group
from python_app.gui.controllers.sections.processing_section import build_processing_group
from python_app.gui.controllers.sections.radar_section import build_radar_group
from python_app.gui.controllers.sections.switch_section import build_switch_group
__all__ = [
"build_data_actions_group",
"build_hardware_actions_group",
"build_pipeline_group",
"build_preprocess_summary_group",
"build_processing_group",
"build_radar_group",
"build_switch_group",
]
@@ -0,0 +1,46 @@
"""Builder for snapshot and storage actions section."""
from __future__ import annotations
from PyQt6.QtWidgets import QGroupBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QSpinBox, QVBoxLayout
def build_data_actions_group(owner) -> QGroupBox:
"""Create snapshot save controls section."""
group = QGroupBox("Data Actions")
layout = QVBoxLayout(group)
layout.setSpacing(8)
save_row = QHBoxLayout()
save_row.setSpacing(8)
save_button = QPushButton("Save Numpy Snapshot")
save_button.clicked.connect(owner._save_snapshot)
owner._save_count = QSpinBox()
owner._save_count.setMinimum(1)
owner._save_count.setMaximum(10_000)
owner._save_count.setValue(10)
save_row.addWidget(save_button)
save_row.addWidget(QLabel("Last N"))
save_row.addWidget(owner._save_count)
save_row.addStretch(1)
layout.addLayout(save_row)
path_row = QHBoxLayout()
path_row.setSpacing(8)
owner._save_path_input = QLineEdit(str(owner._project_root / "python_app/data/snapshots"))
browse_button = QPushButton("Browse")
browse_button.clicked.connect(owner._browse_save_path)
path_row.addWidget(QLabel("Path"))
path_row.addWidget(owner._save_path_input, stretch=1)
path_row.addWidget(browse_button)
layout.addLayout(path_row)
name_row = QHBoxLayout()
name_row.setSpacing(8)
owner._save_name_input = QLineEdit("snapshot_manual")
name_row.addWidget(QLabel("Name"))
name_row.addWidget(owner._save_name_input, stretch=1)
layout.addLayout(name_row)
return group
@@ -0,0 +1,25 @@
"""Builder for hardware actions section."""
from __future__ import annotations
from PyQt6.QtWidgets import QGroupBox, QHBoxLayout, QPushButton
def build_hardware_actions_group(owner) -> QGroupBox:
"""Create hardware action buttons section."""
group = QGroupBox("Hardware Actions")
layout = QHBoxLayout(group)
layout.setSpacing(8)
apply_radar_button = QPushButton("Apply Radar Settings")
apply_radar_button.clicked.connect(owner._apply_radar_settings)
layout.addWidget(apply_radar_button)
save_config_button = QPushButton("Save Current Config")
save_config_button.clicked.connect(owner._save_current_config)
layout.addWidget(save_config_button)
preprocess_button = QPushButton("Preprocessing Panel")
preprocess_button.clicked.connect(owner._open_preprocess_panel)
layout.addWidget(preprocess_button)
return group
@@ -0,0 +1,34 @@
"""Builder for pipeline control section."""
from __future__ import annotations
from PyQt6.QtWidgets import QGroupBox, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
def build_pipeline_group(owner) -> QGroupBox:
"""Create Start/Single/Stop controls section."""
group = QGroupBox("Pipeline")
layout = QVBoxLayout(group)
layout.setSpacing(8)
action_row = QHBoxLayout()
action_row.setSpacing(8)
start_button = QPushButton("Start")
start_button.clicked.connect(owner._start_run)
action_row.addWidget(start_button)
single_button = QPushButton("Single Capture")
single_button.clicked.connect(owner._start_single_capture)
action_row.addWidget(single_button)
stop_button = QPushButton("Stop")
stop_button.clicked.connect(owner._stop_run)
action_row.addWidget(stop_button)
layout.addLayout(action_row)
hint = QLabel("Start continuous run or single processed collection capture.")
hint.setObjectName("hintLabel")
layout.addWidget(hint)
return group
@@ -0,0 +1,19 @@
"""Builder for preprocess set summary section."""
from __future__ import annotations
from PyQt6.QtWidgets import QFormLayout, QGroupBox, QLabel
def build_preprocess_summary_group(owner) -> QGroupBox:
"""Create selected calibration/reference summary section."""
group = QGroupBox("Selected Preprocess Sets")
form = QFormLayout(group)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._selected_calibration_label = QLabel("<not selected>")
owner._selected_reference_label = QLabel("<not selected>")
form.addRow("Calibration", owner._selected_calibration_label)
form.addRow("Reference", owner._selected_reference_label)
return group
@@ -0,0 +1,134 @@
"""Builder for processing mode and live-parameter section."""
from __future__ import annotations
from PyQt6.QtWidgets import (
QCheckBox,
QComboBox,
QDoubleSpinBox,
QFormLayout,
QGroupBox,
QHBoxLayout,
QPushButton,
QSizePolicy,
QStackedWidget,
QWidget,
)
def build_processing_group(owner) -> QGroupBox:
"""Create processing mode section with pass-through and B-scan pages."""
group = QGroupBox("Processing")
form = QFormLayout(group)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._processing_mode = QComboBox()
owner._processing_mode.addItems(["pass_through", "bscan"])
owner._processing_mode_pages = QStackedWidget(group)
owner._processing_mode_pages.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
pass_through_page = QWidget(owner._processing_mode_pages)
pass_through_page.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
pass_through_form = QFormLayout(pass_through_page)
pass_through_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._processing_gain_db = QDoubleSpinBox()
owner._processing_gain_db.setDecimals(2)
owner._processing_gain_db.setRange(-40.0, 40.0)
owner._processing_gain_db.setSingleStep(0.25)
owner._processing_gain_db.setValue(0.0)
owner._processing_phase_deg = QDoubleSpinBox()
owner._processing_phase_deg.setDecimals(1)
owner._processing_phase_deg.setRange(-180.0, 180.0)
owner._processing_phase_deg.setSingleStep(1.0)
owner._processing_phase_deg.setValue(0.0)
owner._show_magnitude_checkbox = QCheckBox("Show magnitude")
owner._show_magnitude_checkbox.setChecked(True)
owner._show_phase_checkbox = QCheckBox("Show phase")
owner._show_phase_checkbox.setChecked(True)
pass_through_form.addRow("Gain dB (live)", owner._processing_gain_db)
pass_through_form.addRow("Phase deg (live)", owner._processing_phase_deg)
pass_through_form.addRow(owner._show_magnitude_checkbox)
pass_through_form.addRow(owner._show_phase_checkbox)
owner._processing_mode_pages.addWidget(pass_through_page)
bscan_page = QWidget(owner._processing_mode_pages)
bscan_page.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
bscan_form = QFormLayout(bscan_page)
bscan_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._bscan_axis = QComboBox()
owner._bscan_axis.addItems(["abs", "real", "phase"])
owner._bscan_cut_m = QDoubleSpinBox()
owner._bscan_cut_m.setDecimals(3)
owner._bscan_cut_m.setRange(0.0, 2.0)
owner._bscan_cut_m.setSingleStep(0.001)
owner._bscan_cut_m.setValue(0.824)
owner._bscan_max_depth_m = QDoubleSpinBox()
owner._bscan_max_depth_m.setDecimals(1)
owner._bscan_max_depth_m.setRange(0.1, 5.0)
owner._bscan_max_depth_m.setSingleStep(0.1)
owner._bscan_max_depth_m.setValue(1.0)
owner._bscan_gain = QDoubleSpinBox()
owner._bscan_gain.setDecimals(1)
owner._bscan_gain.setRange(0.0, 3.0)
owner._bscan_gain.setSingleStep(0.1)
owner._bscan_gain.setValue(1.0)
owner._bscan_start_freq_mhz = QDoubleSpinBox()
owner._bscan_start_freq_mhz.setDecimals(1)
owner._bscan_start_freq_mhz.setRange(100.0, 8800.0)
owner._bscan_start_freq_mhz.setSingleStep(10.0)
owner._bscan_start_freq_mhz.setValue(100.0)
owner._bscan_stop_freq_mhz = QDoubleSpinBox()
owner._bscan_stop_freq_mhz.setDecimals(1)
owner._bscan_stop_freq_mhz.setRange(100.0, 8800.0)
owner._bscan_stop_freq_mhz.setSingleStep(10.0)
owner._bscan_stop_freq_mhz.setValue(8800.0)
owner._bscan_clear_history_button = QPushButton("Clear B-scan History")
owner._bscan_clear_history_button.clicked.connect(owner._on_bscan_clear_history_clicked)
owner._bscan_remove_last_button = QPushButton("Remove Last Sweep")
owner._bscan_remove_last_button.clicked.connect(owner._on_bscan_remove_last_sweep_clicked)
bscan_actions = QWidget(owner._processing_mode_pages)
bscan_actions_layout = QHBoxLayout(bscan_actions)
bscan_actions_layout.setContentsMargins(0, 0, 0, 0)
bscan_actions_layout.setSpacing(8)
bscan_actions_layout.addWidget(owner._bscan_remove_last_button)
bscan_actions_layout.addWidget(owner._bscan_clear_history_button)
bscan_form.addRow("Axis", owner._bscan_axis)
bscan_form.addRow("Cut m", owner._bscan_cut_m)
bscan_form.addRow("Max depth m", owner._bscan_max_depth_m)
bscan_form.addRow("Gain", owner._bscan_gain)
bscan_form.addRow("Start MHz", owner._bscan_start_freq_mhz)
bscan_form.addRow("Stop MHz", owner._bscan_stop_freq_mhz)
bscan_form.addRow(bscan_actions)
owner._processing_mode_pages.addWidget(bscan_page)
owner._processing_mode.currentTextChanged.connect(owner._on_processing_mode_changed)
owner._processing_gain_db.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._processing_phase_deg.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._show_magnitude_checkbox.toggled.connect(owner._on_trace_visibility_changed)
owner._show_phase_checkbox.toggled.connect(owner._on_trace_visibility_changed)
form.addRow("Mode", owner._processing_mode)
form.addRow(owner._processing_mode_pages)
owner._bscan_axis.currentTextChanged.connect(owner._on_processing_live_settings_changed)
owner._bscan_cut_m.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._bscan_max_depth_m.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._bscan_gain.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._bscan_start_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._bscan_stop_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._on_processing_mode_changed(owner._processing_mode.currentText())
return group
@@ -0,0 +1,51 @@
"""Builder for radar settings section."""
from __future__ import annotations
from PyQt6.QtWidgets import QComboBox, QFormLayout, QGroupBox, QLabel, QLineEdit
def build_radar_group(owner) -> QGroupBox:
"""Create radar settings controls and labels."""
group = QGroupBox("Radar")
form = QFormLayout(group)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
defaults = owner._defaults_config.radar
owner._serial_input = QLineEdit(defaults.serial)
owner._serial_input.setPlaceholderText("Optional: empty = auto-detect first LibreVNA")
owner._radar_mode = QComboBox()
owner._radar_mode.addItems(["mock", "native"])
owner._radar_mode.setToolTip("mock: synthetic signal, native: real LibreVNA hardware")
owner._set_combo_current_text(owner._radar_mode, defaults.driver_mode)
owner._start_hz_input = QLineEdit(f"{defaults.sweep.start_hz:g}")
owner._stop_hz_input = QLineEdit(f"{defaults.sweep.stop_hz:g}")
owner._points_input = QLineEdit(str(defaults.sweep.points))
owner._ifbw_input = QLineEdit(f"{defaults.sweep.if_bandwidth_hz:g}")
owner._power_input = QLineEdit(f"{defaults.sweep.power_dbm:g}")
owner._power_input.setToolTip("Device power limits are available only in native mode.")
owner._serial_input.editingFinished.connect(owner._on_radar_identity_changed)
owner._radar_mode.currentTextChanged.connect(owner._on_radar_identity_changed)
owner._start_hz_input.editingFinished.connect(owner._on_radar_sweep_limits_changed)
owner._stop_hz_input.editingFinished.connect(owner._on_radar_sweep_limits_changed)
owner._radar_start_label = QLabel("Start Hz")
owner._radar_stop_label = QLabel("Stop Hz")
owner._radar_points_label = QLabel("Points")
owner._radar_ifbw_label = QLabel("IF BW Hz")
owner._radar_power_label = QLabel("Stimulus Power dBm")
owner._radar_limits_hint = QLabel("Mock mode: device limits are not applied.")
owner._radar_limits_hint.setObjectName("hintLabel")
form.addRow("Serial", owner._serial_input)
form.addRow("Mode", owner._radar_mode)
form.addRow(owner._radar_start_label, owner._start_hz_input)
form.addRow(owner._radar_stop_label, owner._stop_hz_input)
form.addRow(owner._radar_points_label, owner._points_input)
form.addRow(owner._radar_ifbw_label, owner._ifbw_input)
form.addRow(owner._radar_power_label, owner._power_input)
form.addRow(owner._radar_limits_hint)
return group
@@ -0,0 +1,82 @@
"""Builder for switch and combo settings section."""
from __future__ import annotations
from PyQt6.QtWidgets import QComboBox, QFormLayout, QGroupBox, QHBoxLayout, QLineEdit, QVBoxLayout
def build_switch_group(owner) -> QGroupBox:
"""Create input/output switch controls and run combos settings."""
group = QGroupBox("Switches")
layout = QVBoxLayout(group)
input_defaults = owner._defaults_config.input_switch
output_defaults = owner._defaults_config.output_switch
global_form = QFormLayout()
global_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._settling_ms = QLineEdit(str(owner._defaults_config.runtime.settling_ms))
owner._combos_text = QLineEdit("")
owner._combos_text.setPlaceholderText("input:output,input:output or empty for full")
global_form.addRow("Settling ms", owner._settling_ms)
global_form.addRow("Run combos", owner._combos_text)
layout.addLayout(global_form)
switch_columns = QHBoxLayout()
input_group = QGroupBox("Input Switch (Radar Port 2)")
input_form = QFormLayout(input_group)
input_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._input_mode = QComboBox()
owner._input_mode.addItems(["mock", "native"])
owner._set_combo_current_text(owner._input_mode, input_defaults.driver_mode)
owner._input_driver = QComboBox()
owner._input_driver.addItems(["hmc349a", "h7992"])
owner._set_combo_current_text(owner._input_driver, input_defaults.driver)
owner._input_positions = QLineEdit(str(input_defaults.positions))
owner._input_gpio_chip = QLineEdit(input_defaults.gpio_chip)
owner._input_pin_a = QLineEdit(str(input_defaults.pin_a))
owner._input_pin_b = QLineEdit(str(input_defaults.pin_b))
owner._input_invert_logic = QComboBox()
owner._input_invert_logic.addItems(["false", "true"])
owner._set_combo_current_text(owner._input_invert_logic, "true" if input_defaults.invert_logic else "false")
input_form.addRow("Mode", owner._input_mode)
input_form.addRow("Driver", owner._input_driver)
input_form.addRow("Positions", owner._input_positions)
input_form.addRow("GPIO chip", owner._input_gpio_chip)
input_form.addRow("Pin A", owner._input_pin_a)
input_form.addRow("Pin B", owner._input_pin_b)
input_form.addRow("Invert logic", owner._input_invert_logic)
output_group = QGroupBox("Output Switch (Radar Port 1)")
output_form = QFormLayout(output_group)
output_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
owner._output_mode = QComboBox()
owner._output_mode.addItems(["mock", "native"])
owner._set_combo_current_text(owner._output_mode, output_defaults.driver_mode)
owner._output_driver = QComboBox()
owner._output_driver.addItems(["h7992", "hmc349a"])
owner._set_combo_current_text(owner._output_driver, output_defaults.driver)
owner._output_positions = QLineEdit(str(output_defaults.positions))
owner._output_gpio_chip = QLineEdit(output_defaults.gpio_chip)
owner._output_pin_a = QLineEdit(str(output_defaults.pin_a))
owner._output_pin_b = QLineEdit(str(output_defaults.pin_b))
owner._output_invert_logic = QComboBox()
owner._output_invert_logic.addItems(["false", "true"])
owner._set_combo_current_text(owner._output_invert_logic, "true" if output_defaults.invert_logic else "false")
output_form.addRow("Mode", owner._output_mode)
output_form.addRow("Driver", owner._output_driver)
output_form.addRow("Positions", owner._output_positions)
output_form.addRow("GPIO chip", owner._output_gpio_chip)
output_form.addRow("Pin A", owner._output_pin_a)
output_form.addRow("Pin B", owner._output_pin_b)
output_form.addRow("Invert logic", owner._output_invert_logic)
switch_columns.addWidget(input_group)
switch_columns.addWidget(output_group)
layout.addLayout(switch_columns)
return group