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,192 @@
"""UI construction mixin for the main radar control window."""
from __future__ import annotations
from PyQt6.QtWidgets import (
QComboBox,
QFrame,
QGroupBox,
QHBoxLayout,
QLabel,
QPlainTextEdit,
QPushButton,
QScrollArea,
QStackedWidget,
QVBoxLayout,
QWidget,
)
import pyqtgraph as pg
from python_app.gui.controllers.sections import (
build_data_actions_group,
build_hardware_actions_group,
build_pipeline_group,
build_preprocess_summary_group,
build_processing_group,
build_radar_group,
build_switch_group,
)
class AppWindowUiMixin:
"""Builds and wires all static UI widgets."""
def _build_ui(self) -> None:
"""Build main window widgets, plot area, and settings panel."""
self.setWindowTitle("Radar System Control")
root = QWidget(self)
self.setCentralWidget(root)
layout = QHBoxLayout(root)
layout.setContentsMargins(12, 12, 12, 12)
layout.setSpacing(14)
self._plot_stack = QStackedWidget(root)
self._plot = pg.PlotWidget(background="#0f141c")
self._plot.showGrid(x=True, y=True, alpha=0.2)
self._plot.setLabel("bottom", "Frequency", units="Hz")
self._plot.setLabel("left", "Magnitude", units="dB")
self._plot_stack.addWidget(self._plot)
self._trace_plots_container = QWidget(root)
trace_layout = QVBoxLayout(self._trace_plots_container)
trace_layout.setContentsMargins(0, 0, 0, 0)
trace_layout.setSpacing(6)
self._trace_magnitude_plot = pg.PlotWidget(background="#0f141c")
self._trace_magnitude_plot.showGrid(x=True, y=True, alpha=0.2)
self._trace_magnitude_plot.setLabel("left", "Magnitude", units="dB")
self._trace_magnitude_plot.getPlotItem().showAxis("bottom", show=False)
self._trace_magnitude_plot.getPlotItem().setDownsampling(mode="peak")
self._trace_magnitude_plot.getPlotItem().setClipToView(True)
trace_layout.addWidget(self._trace_magnitude_plot, stretch=1)
self._trace_phase_plot = pg.PlotWidget(background="#0f141c")
self._trace_phase_plot.showGrid(x=True, y=True, alpha=0.2)
self._trace_phase_plot.setLabel("left", "Phase", units="deg")
self._trace_phase_plot.setLabel("bottom", "Frequency", units="Hz")
self._trace_phase_plot.getPlotItem().setDownsampling(mode="peak")
self._trace_phase_plot.getPlotItem().setClipToView(True)
trace_layout.addWidget(self._trace_phase_plot, stretch=1)
self._plot_stack.addWidget(self._trace_plots_container)
self._plot_stack.setCurrentWidget(self._trace_plots_container)
layout.addWidget(self._plot_stack, stretch=11)
self._settings_toggle_button = QPushButton("<")
self._settings_toggle_button.setObjectName("settingsToggleButton")
self._settings_toggle_button.setFixedWidth(26)
self._settings_toggle_button.clicked.connect(self._toggle_settings_panel)
layout.addWidget(self._settings_toggle_button, stretch=0)
self._settings_panel = QWidget(root)
self._settings_panel.setMinimumWidth(659)
right_layout = QVBoxLayout(self._settings_panel)
right_layout.setContentsMargins(0, 0, 0, 0)
right_layout.setSpacing(10)
# Build log widget early so error handlers can safely write during UI construction.
self._log_box = QPlainTextEdit(self._settings_panel)
self._log_box.setReadOnly(True)
self._log_box.setMinimumHeight(170)
pipeline_group = self._build_pipeline_group()
hardware_actions_group = self._build_hardware_actions_group()
data_actions_group = self._build_data_actions_group()
preprocess_summary_group = self._build_preprocess_summary_group()
radar_group = self._build_radar_group()
processing_group = self._build_processing_group()
switch_group = self._build_switch_group()
controls = QWidget(self._settings_panel)
controls_layout = QVBoxLayout(controls)
controls_layout.setContentsMargins(0, 0, 0, 0)
controls_layout.setSpacing(10)
controls_layout.addWidget(pipeline_group)
controls_layout.addWidget(hardware_actions_group)
controls_layout.addWidget(data_actions_group)
controls_layout.addWidget(preprocess_summary_group)
controls_layout.addWidget(processing_group)
controls_layout.addWidget(radar_group)
controls_layout.addWidget(switch_group)
controls_layout.addStretch(1)
scroll = QScrollArea(self._settings_panel)
scroll.setWidgetResizable(True)
scroll.setFrameShape(QFrame.Shape.NoFrame)
scroll.setWidget(controls)
right_layout.addWidget(scroll, stretch=1)
self._status_label = QLabel("Status: idle", self._settings_panel)
self._status_label.setObjectName("statusLabel")
right_layout.addWidget(self._status_label)
self._history_label = QLabel("History: raw=0, preprocessed=0, results=0", self._settings_panel)
self._history_label.setObjectName("hintLabel")
right_layout.addWidget(self._history_label)
right_layout.addWidget(self._log_box, stretch=0)
layout.addWidget(self._settings_panel, stretch=8)
self._set_settings_panel_visible(True)
self.resize(1650, 940)
def _toggle_settings_panel(self) -> None:
"""Toggle settings panel visibility."""
self._set_settings_panel_visible(not self._settings_panel.isVisible())
def _set_settings_panel_visible(self, visible: bool) -> None:
"""Set settings panel visibility and update toggle button glyph."""
self._settings_panel.setVisible(visible)
if visible:
self._settings_toggle_button.setText(">")
self._settings_toggle_button.setToolTip("Hide settings panel")
else:
self._settings_toggle_button.setText("<")
self._settings_toggle_button.setToolTip("Show settings panel")
def _set_plot_mode(self, mode: str) -> None:
"""Switch visible plot surface based on processing mode."""
if mode == "bscan":
self._plot_stack.setCurrentWidget(self._plot)
return
self._plot_stack.setCurrentWidget(self._trace_plots_container)
def _build_pipeline_group(self) -> QGroupBox:
"""Build pipeline controls section."""
return build_pipeline_group(self)
def _build_hardware_actions_group(self) -> QGroupBox:
"""Build hardware actions section."""
return build_hardware_actions_group(self)
def _build_data_actions_group(self) -> QGroupBox:
"""Build data actions section."""
return build_data_actions_group(self)
def _build_preprocess_summary_group(self) -> QGroupBox:
"""Build selected preprocess sets summary section."""
return build_preprocess_summary_group(self)
def _build_processing_group(self) -> QGroupBox:
"""Build processing mode section."""
return build_processing_group(self)
def _build_radar_group(self) -> QGroupBox:
"""Build radar settings section."""
return build_radar_group(self)
def _build_switch_group(self) -> QGroupBox:
"""Build switch settings section."""
return build_switch_group(self)
@staticmethod
def _set_combo_current_text(combo: QComboBox, value: str) -> None:
"""Select combo item by text, appending it when missing."""
index = combo.findText(value)
if index >= 0:
combo.setCurrentIndex(index)
return
combo.addItem(value)
combo.setCurrentIndex(combo.count() - 1)