added white theme, some features to processing and saving
This commit is contained in:
@@ -168,7 +168,8 @@ class AppWindowLiveProcessingMixin:
|
||||
f"cut={self._bscan_cut_m.value():g} m, "
|
||||
f"max_depth={self._bscan_max_depth_m.value():g} m, "
|
||||
f"gain={self._bscan_gain.value():g}, "
|
||||
f"freq={self._bscan_start_freq_mhz.value():g}..{self._bscan_stop_freq_mhz.value():g} MHz)"
|
||||
f"freq={self._bscan_start_freq_mhz.value():g}..{self._bscan_stop_freq_mhz.value():g} MHz, "
|
||||
f"subtract_mean_ascan={self._bscan_subtract_mean_ascan.isChecked()})"
|
||||
)
|
||||
elif mode == "gpr":
|
||||
self._log(
|
||||
|
||||
@@ -169,6 +169,7 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._bscan_gain,
|
||||
self._bscan_start_freq_mhz,
|
||||
self._bscan_stop_freq_mhz,
|
||||
self._bscan_subtract_mean_ascan,
|
||||
self._gpr_config_mode,
|
||||
self._gpr_relative_permittivity,
|
||||
self._gpr_tx_geometry_input,
|
||||
@@ -228,6 +229,7 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._bscan_gain.setValue(float(gui_state.processing.bscan.gain))
|
||||
self._bscan_start_freq_mhz.setValue(float(gui_state.processing.bscan.start_freq_mhz))
|
||||
self._bscan_stop_freq_mhz.setValue(float(gui_state.processing.bscan.stop_freq_mhz))
|
||||
self._bscan_subtract_mean_ascan.setChecked(bool(gui_state.processing.bscan.subtract_mean_ascan))
|
||||
|
||||
self._set_combo_current_text(self._gpr_config_mode, str(config.gpr.mode))
|
||||
self._gpr_relative_permittivity.setValue(float(config.gpr.relative_permittivity))
|
||||
|
||||
@@ -157,6 +157,7 @@ class AppWindowConfigStateBuildersMixin:
|
||||
gain=1.0,
|
||||
start_freq_mhz=100.0,
|
||||
stop_freq_mhz=8800.0,
|
||||
subtract_mean_ascan=False,
|
||||
),
|
||||
gpr=GuiGprStateModel(
|
||||
input_positions=self._default_gpr_input_positions_from_config(config),
|
||||
@@ -235,6 +236,7 @@ class AppWindowConfigStateBuildersMixin:
|
||||
gain=float(self._bscan_gain.value()),
|
||||
start_freq_mhz=float(self._bscan_start_freq_mhz.value()),
|
||||
stop_freq_mhz=float(self._bscan_stop_freq_mhz.value()),
|
||||
subtract_mean_ascan=bool(self._bscan_subtract_mean_ascan.isChecked()),
|
||||
),
|
||||
gpr=GuiGprStateModel(
|
||||
input_positions=self._gpr_input_positions_input.text().strip(),
|
||||
|
||||
@@ -39,6 +39,8 @@ def _result_tail(
|
||||
|
||||
def build_bscan_signature(
|
||||
live_config: ProcessingLiveConfig,
|
||||
*,
|
||||
subtract_mean_ascan_enabled: bool,
|
||||
result_history: list[ResultCollection],
|
||||
history_limit: int,
|
||||
floor_collection_id: int,
|
||||
@@ -57,11 +59,25 @@ def build_bscan_signature(
|
||||
float(live_config.bscan_gain),
|
||||
float(live_config.bscan_start_freq_mhz),
|
||||
float(live_config.bscan_stop_freq_mhz),
|
||||
bool(subtract_mean_ascan_enabled),
|
||||
int(floor_collection_id),
|
||||
tuple((int(collection.collection_id), int(collection.monotonic_ns), len(collection.blocks)) for collection in result_tail),
|
||||
)
|
||||
|
||||
|
||||
def apply_mean_ascan_subtraction(
|
||||
history: deque[np.ndarray],
|
||||
*,
|
||||
enabled: bool,
|
||||
) -> np.ndarray:
|
||||
"""Subtract mean A-scan across sweeps when enabled."""
|
||||
sweeps = np.vstack(history).astype(np.float32, copy=False)
|
||||
if not enabled:
|
||||
return sweeps
|
||||
mean_trace = np.mean(sweeps, axis=0, dtype=np.float32)
|
||||
return sweeps - mean_trace
|
||||
|
||||
|
||||
def rebuild_bscan_history_from_results(
|
||||
result_history: list[ResultCollection],
|
||||
history_limit: int,
|
||||
@@ -183,7 +199,10 @@ class AppWindowBscanPlotMixin:
|
||||
if not history or depth_axis is None:
|
||||
return False
|
||||
|
||||
sweeps = np.vstack(history).astype(np.float32, copy=False)
|
||||
sweeps = apply_mean_ascan_subtraction(
|
||||
history,
|
||||
enabled=bool(self._bscan_subtract_mean_ascan.isChecked()),
|
||||
)
|
||||
if sweeps.size == 0:
|
||||
return False
|
||||
|
||||
@@ -229,6 +248,7 @@ class AppWindowBscanPlotMixin:
|
||||
result_history = list(self._result_history)
|
||||
return build_bscan_signature(
|
||||
live_config=live_config,
|
||||
subtract_mean_ascan_enabled=bool(self._bscan_subtract_mean_ascan.isChecked()),
|
||||
result_history=result_history,
|
||||
history_limit=self._bscan_history_limit,
|
||||
floor_collection_id=self._bscan_history_floor_collection_id,
|
||||
|
||||
@@ -18,9 +18,9 @@ class AppWindowSnapshotMixin:
|
||||
return snapshot_dir / "config_profile.json"
|
||||
|
||||
@staticmethod
|
||||
def _vna_json_config_profile_path(output_root: Path, output_stem: str) -> Path:
|
||||
def _vna_json_config_profile_path(output_dir: Path) -> Path:
|
||||
"""Return companion config-profile path for one VNA-history JSON export batch."""
|
||||
return output_root / f"{output_stem}_config_profile.json"
|
||||
return output_dir / "config_profile.json"
|
||||
|
||||
def _save_snapshot(self) -> None:
|
||||
"""Save runtime snapshot in numpy-directory format."""
|
||||
@@ -98,8 +98,9 @@ class AppWindowSnapshotMixin:
|
||||
output_stem = str(summary.get("output_stem", "")).strip()
|
||||
if not output_stem:
|
||||
raise RuntimeError("VNA history JSON export did not report output_stem for config companion save")
|
||||
output_dir = Path(str(summary.get("output_dir", "")).strip() or output_paths[0].parent)
|
||||
|
||||
config_profile_path = self._vna_json_config_profile_path(output_root, output_stem)
|
||||
config_profile_path = self._vna_json_config_profile_path(output_dir)
|
||||
try:
|
||||
self._write_gui_profile_to_path(config_profile_path, allow_overwrite=False)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@@ -110,6 +111,7 @@ class AppWindowSnapshotMixin:
|
||||
"VNA history JSON files were saved, but the adjacent config profile could not be written",
|
||||
details=(
|
||||
f"output_root={output_root}\n"
|
||||
f"output_dir={output_dir}\n"
|
||||
f"config_profile_path={config_profile_path}\n"
|
||||
f"saved_json_files={len(output_paths)}\n"
|
||||
f"{exported_preview}\n\n"
|
||||
|
||||
@@ -134,6 +134,9 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
owner._bscan_stop_freq_mhz.setSingleStep(10.0)
|
||||
owner._bscan_stop_freq_mhz.setValue(float(bscan_defaults.stop_freq_mhz))
|
||||
|
||||
owner._bscan_subtract_mean_ascan = QCheckBox("Subtract mean A-scan")
|
||||
owner._bscan_subtract_mean_ascan.setChecked(bool(bscan_defaults.subtract_mean_ascan))
|
||||
|
||||
bscan_page = _build_processing_mode_page(
|
||||
owner._processing_mode_pages,
|
||||
[
|
||||
@@ -143,8 +146,9 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
("Gain", owner._bscan_gain),
|
||||
("Start MHz", owner._bscan_start_freq_mhz),
|
||||
("Stop MHz", owner._bscan_stop_freq_mhz),
|
||||
owner._bscan_subtract_mean_ascan,
|
||||
],
|
||||
split_index=3,
|
||||
split_index=4,
|
||||
)
|
||||
owner._processing_mode_pages.addWidget(bscan_page)
|
||||
|
||||
@@ -314,6 +318,7 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
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._bscan_subtract_mean_ascan.toggled.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_input_positions_input.editingFinished.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_output_positions_input.editingFinished.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_min_depth_m.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
|
||||
@@ -14,14 +14,14 @@ if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from python_app.gui.app_window import AppWindow
|
||||
from python_app.gui.theme import apply_dark_theme
|
||||
from python_app.gui.theme import apply_light_theme
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run Qt event loop and show main radar control window."""
|
||||
app = QApplication(sys.argv)
|
||||
apply_dark_theme(app)
|
||||
pg.setConfigOptions(antialias=True, foreground="#dbe4f1")
|
||||
apply_light_theme(app)
|
||||
pg.setConfigOptions(antialias=True, background="#ffffff", foreground="#334155")
|
||||
window = AppWindow(PROJECT_ROOT)
|
||||
window.showMaximized()
|
||||
return app.exec()
|
||||
|
||||
+34
-34
@@ -6,19 +6,19 @@ from PyQt6.QtGui import QColor, QPalette
|
||||
from PyQt6.QtWidgets import QApplication, QStyleFactory
|
||||
|
||||
|
||||
_DARK_STYLESHEET = """
|
||||
_LIGHT_STYLESHEET = """
|
||||
QMainWindow, QDialog {
|
||||
background-color: #0f131a;
|
||||
background-color: #f3f6fb;
|
||||
}
|
||||
|
||||
QWidget {
|
||||
color: #e7edf7;
|
||||
color: #1f2937;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
background-color: #151b24;
|
||||
border: 1px solid #263142;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #d7dee8;
|
||||
border-radius: 10px;
|
||||
margin-top: 14px;
|
||||
padding: 10px;
|
||||
@@ -28,39 +28,39 @@ QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 6px;
|
||||
color: #9fb4ce;
|
||||
color: #526277;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
background-color: #1d2735;
|
||||
border: 1px solid #314055;
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #cad4e1;
|
||||
border-radius: 8px;
|
||||
padding: 7px 12px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: #243246;
|
||||
background-color: #eef3f9;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: #1a2432;
|
||||
background-color: #e4ebf4;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: #2f7ee6;
|
||||
border-color: #5d88bd;
|
||||
border-color: #2f7ee6;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
QPushButton:checked:hover {
|
||||
background-color: #3c89ee;
|
||||
background-color: #3b8bf4;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: #6b7d95;
|
||||
background-color: #151d27;
|
||||
border-color: #232e3d;
|
||||
color: #98a4b3;
|
||||
background-color: #f3f5f8;
|
||||
border-color: #dde4ec;
|
||||
}
|
||||
|
||||
QPushButton#settingsToggleButton {
|
||||
@@ -83,11 +83,11 @@ QTextEdit,
|
||||
QComboBox,
|
||||
QSpinBox,
|
||||
QDoubleSpinBox {
|
||||
background-color: #101721;
|
||||
border: 1px solid #2e3b4e;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #c9d4e1;
|
||||
border-radius: 7px;
|
||||
padding: 5px 8px;
|
||||
selection-background-color: #2f7ee6;
|
||||
selection-background-color: #cfe3ff;
|
||||
}
|
||||
|
||||
QLineEdit:focus,
|
||||
@@ -96,7 +96,7 @@ QTextEdit:focus,
|
||||
QComboBox:focus,
|
||||
QSpinBox:focus,
|
||||
QDoubleSpinBox:focus {
|
||||
border: 1px solid #5d88bd;
|
||||
border: 1px solid #2f7ee6;
|
||||
}
|
||||
|
||||
QTextEdit#runtimeLogBox {
|
||||
@@ -116,34 +116,34 @@ QScrollArea {
|
||||
}
|
||||
|
||||
QLabel#statusLabel {
|
||||
color: #94b4d9;
|
||||
color: #35507a;
|
||||
font-weight: 600;
|
||||
padding: 2px 1px;
|
||||
}
|
||||
|
||||
QLabel#hintLabel {
|
||||
color: #7f94af;
|
||||
color: #6c7b8d;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def apply_dark_theme(app: QApplication) -> None:
|
||||
"""Apply a minimal modern dark theme shared by all windows."""
|
||||
def apply_light_theme(app: QApplication) -> None:
|
||||
"""Apply a minimal light theme shared by all windows."""
|
||||
app.setStyle(QStyleFactory.create("Fusion"))
|
||||
|
||||
palette = QPalette()
|
||||
palette.setColor(QPalette.ColorRole.Window, QColor("#0f131a"))
|
||||
palette.setColor(QPalette.ColorRole.WindowText, QColor("#e7edf7"))
|
||||
palette.setColor(QPalette.ColorRole.Base, QColor("#101721"))
|
||||
palette.setColor(QPalette.ColorRole.AlternateBase, QColor("#151b24"))
|
||||
palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#151b24"))
|
||||
palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#e7edf7"))
|
||||
palette.setColor(QPalette.ColorRole.Text, QColor("#e7edf7"))
|
||||
palette.setColor(QPalette.ColorRole.Button, QColor("#1d2735"))
|
||||
palette.setColor(QPalette.ColorRole.ButtonText, QColor("#e7edf7"))
|
||||
palette.setColor(QPalette.ColorRole.Window, QColor("#f3f6fb"))
|
||||
palette.setColor(QPalette.ColorRole.WindowText, QColor("#1f2937"))
|
||||
palette.setColor(QPalette.ColorRole.Base, QColor("#ffffff"))
|
||||
palette.setColor(QPalette.ColorRole.AlternateBase, QColor("#eef3f8"))
|
||||
palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#ffffff"))
|
||||
palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#1f2937"))
|
||||
palette.setColor(QPalette.ColorRole.Text, QColor("#1f2937"))
|
||||
palette.setColor(QPalette.ColorRole.Button, QColor("#f8fafc"))
|
||||
palette.setColor(QPalette.ColorRole.ButtonText, QColor("#1f2937"))
|
||||
palette.setColor(QPalette.ColorRole.BrightText, QColor("#ffffff"))
|
||||
palette.setColor(QPalette.ColorRole.Link, QColor("#5d88bd"))
|
||||
palette.setColor(QPalette.ColorRole.Link, QColor("#2f7ee6"))
|
||||
palette.setColor(QPalette.ColorRole.Highlight, QColor("#2f7ee6"))
|
||||
palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#ffffff"))
|
||||
app.setPalette(palette)
|
||||
app.setStyleSheet(_DARK_STYLESHEET)
|
||||
app.setStyleSheet(_LIGHT_STYLESHEET)
|
||||
|
||||
Reference in New Issue
Block a user