From 5c503a21fd7fc392e77cbc016f7f5c0b63a8dbed Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Wed, 29 Jan 2025 22:56:25 +0100 Subject: [PATCH 1/5] fix: qt exit handling --- src/NanoVNASaver/NanoVNASaver.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NanoVNASaver/NanoVNASaver.py b/src/NanoVNASaver/NanoVNASaver.py index d13810b5..24c18558 100644 --- a/src/NanoVNASaver/NanoVNASaver.py +++ b/src/NanoVNASaver/NanoVNASaver.py @@ -700,7 +700,6 @@ def closeEvent(self, a0: QtGui.QCloseEvent) -> None: self.serial_control.disconnect_device() a0.accept() - sys.exit() def changeFont(self, font: QtGui.QFont) -> None: qf_new = QtGui.QFontMetricsF(font) From 638917d3dfca6526509bd14ccdc1f416a3eed7ae Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Wed, 29 Jan 2025 22:58:51 +0100 Subject: [PATCH 2/5] fix: change font handler --- src/NanoVNASaver/Windows/DisplaySettings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NanoVNASaver/Windows/DisplaySettings.py b/src/NanoVNASaver/Windows/DisplaySettings.py index 7db6c7f0..34ab67af 100644 --- a/src/NanoVNASaver/Windows/DisplaySettings.py +++ b/src/NanoVNASaver/Windows/DisplaySettings.py @@ -185,7 +185,7 @@ def __init__(self, app: NanoVNASaver) -> None: self.font_dropdown.setMinimumHeight(20) self.font_dropdown.addItems(["7", "8", "9", "10", "11", "12"]) self.font_dropdown.setCurrentText(str(app_config.gui.font_size)) - self.changeFont() + self.changeFont(str(app_config.gui.font_size)) self.font_dropdown.currentTextChanged.connect(self.changeFont) font_options_layout.addRow("Font size", self.font_dropdown) @@ -516,8 +516,9 @@ def setShowBands(self, show_bands) -> None: for c in self.app.subscribing_charts: c.update() - def changeFont(self) -> None: - font_size = int(self.font_dropdown.currentText()) + def changeFont(self, new_font_size: str) -> None: + font_size = int(new_font_size) + app_config = get_app_config() app_config.gui.font_size = font_size app: QtWidgets.QApplication = QtWidgets.QApplication.instance() font = app.font() From 2665a9065be9f0d017f259cf0500e0be8114abce Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Wed, 29 Jan 2025 22:59:25 +0100 Subject: [PATCH 3/5] fix: access app_config only by getter --- src/NanoVNASaver/Charts/Chart.py | 5 +- src/NanoVNASaver/Controls/MarkerControl.py | 5 +- src/NanoVNASaver/Defaults.py | 103 +++++++++++--------- src/NanoVNASaver/NanoVNASaver.py | 7 +- src/NanoVNASaver/Windows/DisplaySettings.py | 14 ++- tests/test_settings.py | 14 ++- 6 files changed, 90 insertions(+), 58 deletions(-) diff --git a/src/NanoVNASaver/Charts/Chart.py b/src/NanoVNASaver/Charts/Chart.py index 973214e3..875fa7f9 100644 --- a/src/NanoVNASaver/Charts/Chart.py +++ b/src/NanoVNASaver/Charts/Chart.py @@ -24,7 +24,7 @@ from PySide6.QtCore import Qt, Signal from PySide6.QtGui import QAction, QColor, QColorConstants -from ..Defaults import app_config +from ..Defaults import get_app_config from ..Marker.Widget import Marker from ..RFTools import Datapoint @@ -92,6 +92,7 @@ def __init__(self, qp: QtGui.QPaintDevice): self.qp = qp def draw(self, x: int, y: int, color: QtGui.QColor, text: str = ""): + app_config = get_app_config() offset = int(app_config.chart.marker_size // 2) if app_config.chart.marker_at_tip: y -= offset @@ -179,7 +180,7 @@ def setPointSize(self, size) -> None: self.update() def setMarkerSize(self, size) -> None: - app_config.chart.marker_size = size + get_app_config().chart.marker_size = size self.update() def setSweepTitle(self, title) -> None: diff --git a/src/NanoVNASaver/Controls/MarkerControl.py b/src/NanoVNASaver/Controls/MarkerControl.py index cbe7e49d..49323b59 100644 --- a/src/NanoVNASaver/Controls/MarkerControl.py +++ b/src/NanoVNASaver/Controls/MarkerControl.py @@ -23,7 +23,7 @@ from NanoVNASaver import NanoVNASaver -from ..Defaults import app_config +from ..Defaults import get_app_config from ..Marker.Widget import Marker from .Control import Control @@ -32,6 +32,7 @@ class ShowButton(QtWidgets.QPushButton): def setText(self, text: str = ""): + app_config = get_app_config() if not text: text = ( "Show data" if app_config.gui.markers_hidden else "Hide data" @@ -44,6 +45,7 @@ class MarkerControl(Control): def __init__(self, app: NanoVNASaver): super().__init__(app, "Markers") + app_config = get_app_config() for i in range(app_config.chart.marker_count): marker = Marker("", self.app.settings) # marker.setFixedHeight(20) @@ -86,6 +88,7 @@ def __init__(self, app: NanoVNASaver): def toggle_frame(self): def settings(hidden: bool): + app_config = get_app_config() app_config.gui.markers_hidden = not hidden self.app.marker_frame.setHidden(app_config.gui.markers_hidden) self.showMarkerButton.setText() diff --git a/src/NanoVNASaver/Defaults.py b/src/NanoVNASaver/Defaults.py index ecacdcb9..8ec65eb3 100644 --- a/src/NanoVNASaver/Defaults.py +++ b/src/NanoVNASaver/Defaults.py @@ -133,10 +133,56 @@ class AppConfig: markers: MarkersConfig = field(default_factory=MarkersConfig) -app_config = AppConfig() +# noinspection PyDataclass +class AppSettings(QSettings): + def store_dataclass(self, name: str, data: object) -> None: + assert is_dataclass(data) + self.beginGroup(name) + for field_it in fields(data): + value = getattr(data, field_it.name) + try: + assert isinstance(value, field_it.type) + except AssertionError as exc: + logger.error( + "%s: %s of type %s is not a %s", + name, + field_it.name, + type(value), + field_it.type, + ) + raise TypeError from exc + self.setValue(field_it.name, _from_type(value)) + self.endGroup() + + def restore_dataclass(self, name: str, data: object) -> object: + assert is_dataclass(data) + + result = replace(data) + self.beginGroup(name) + for field_it in fields(data): + default = getattr(data, field_it.name) + value = self.value(field_it.name, type=str, defaultValue="") + if not value: + setattr(result, field_it.name, default) + continue + try: + setattr(result, field_it.name, _to_type(value, field_it.type)) + except TypeError: + setattr(result, field_it.name, default) + self.endGroup() + return result + + +_app_config = AppConfig() -def restore_config(settings: "AppSettings") -> AppConfig: +def get_app_config() -> AppConfig: + return _app_config + + +def restore_config(settings: AppSettings) -> AppConfig: + logger.info("Loading settings from: %s", settings.fileName()) + result = AppConfig() for field_it in fields(result): value = settings.restore_dataclass( @@ -144,15 +190,17 @@ def restore_config(settings: "AppSettings") -> AppConfig: ) setattr(result, field_it.name, value) logger.debug("restored\n(\n%s\n)", result) - return result + global _app_config + _app_config = result + return _app_config -def store_config(settings: "AppSettings", data: AppConfig | None = None) -> None: - data = data or app_config - logger.debug("storing\n(\n%s\n)", data) - assert isinstance(data, AppConfig) - for field_it in fields(data): - data_class = getattr(data, field_it.name) +def store_config(settings: AppSettings) -> None: + logger.info("Saving settings to: %s", settings.fileName()) + + logger.debug("storing\n(\n%s\n)", _app_config) + for field_it in fields(_app_config): + data_class = getattr(_app_config, field_it.name) assert is_dataclass(data_class) settings.store_dataclass(field_it.name.upper(), data_class) @@ -182,41 +230,4 @@ def _to_type(data: object, data_type: type) -> object: ) -# noinspection PyDataclass -class AppSettings(QSettings): - def store_dataclass(self, name: str, data: object) -> None: - assert is_dataclass(data) - self.beginGroup(name) - for field_it in fields(data): - value = getattr(data, field_it.name) - try: - assert isinstance(value, field_it.type) - except AssertionError as exc: - logger.error( - "%s: %s of type %s is not a %s", - name, - field_it.name, - type(value), - field_it.type, - ) - raise TypeError from exc - self.setValue(field_it.name, _from_type(value)) - self.endGroup() - - def restore_dataclass(self, name: str, data: object) -> object: - assert is_dataclass(data) - result = replace(data) - self.beginGroup(name) - for field_it in fields(data): - default = getattr(data, field_it.name) - value = self.value(field_it.name, type=str, defaultValue="") - if not value: - setattr(result, field_it.name, default) - continue - try: - setattr(result, field_it.name, _to_type(value, field_it.type)) - except TypeError: - setattr(result, field_it.name, default) - self.endGroup() - return result diff --git a/src/NanoVNASaver/NanoVNASaver.py b/src/NanoVNASaver/NanoVNASaver.py index 24c18558..f6456a70 100644 --- a/src/NanoVNASaver/NanoVNASaver.py +++ b/src/NanoVNASaver/NanoVNASaver.py @@ -18,7 +18,6 @@ # along with this program. If not, see . import contextlib import logging -import sys import threading from time import localtime, strftime @@ -55,7 +54,7 @@ from .Controls.MarkerControl import MarkerControl from .Controls.SerialControl import SerialControl from .Controls.SweepControl import SweepControl -from .Defaults import AppSettings, app_config, restore_config, store_config +from .Defaults import AppSettings, get_app_config, restore_config, store_config from .Formatting import format_frequency, format_gain, format_vswr from .Hardware.Hardware import Interface from .Hardware.VNA import VNA @@ -100,7 +99,6 @@ def __init__(self) -> None: "NanoVNASaver", "NanoVNASaver", ) - logger.info("Settings from: %s", self.settings.fileName()) app_config = restore_config(self.settings) self.threadpool = QtCore.QThreadPool() self.sweep = Sweep() @@ -690,11 +688,12 @@ def closeEvent(self, a0: QtGui.QCloseEvent) -> None: self.bands.saveSettings() self.threadpool.waitForDone(2500) + app_config = get_app_config() app_config.chart.marker_count = Marker.count() app_config.gui.window_width = self.width() app_config.gui.window_height = self.height() app_config.gui.splitter_sizes = self.splitter.saveState() - store_config(self.settings, app_config) + store_config(self.settings) # Dosconnect connected devices and release serial port self.serial_control.disconnect_device() diff --git a/src/NanoVNASaver/Windows/DisplaySettings.py b/src/NanoVNASaver/Windows/DisplaySettings.py index 34ab67af..28095cb0 100644 --- a/src/NanoVNASaver/Windows/DisplaySettings.py +++ b/src/NanoVNASaver/Windows/DisplaySettings.py @@ -24,7 +24,7 @@ from NanoVNASaver import NanoVNASaver from ..Charts.Chart import Chart, ChartColors -from ..Defaults import app_config, store_config +from ..Defaults import get_app_config from ..Marker.Widget import Marker from .Bands import BandsWindow from .Defaults import make_scrollable @@ -68,6 +68,7 @@ def __init__(self, app: NanoVNASaver) -> None: ) display_options_layout.addRow("", self.returnloss_is_positive) + app_config = get_app_config() self.returnloss_is_positive.setChecked( app_config.chart.returnloss_is_positive ) @@ -422,6 +423,7 @@ def changeChart(self, x, y, chart) -> None: def changeReturnLoss(self) -> None: state = self.returnloss_is_positive.isChecked() + app_config = get_app_config() app_config.chart.returnloss_is_positive = bool(state) for m in self.app.markers: m.returnloss_is_positive = state @@ -433,43 +435,51 @@ def changeReturnLoss(self) -> None: def changeShowLines(self) -> None: state = self.show_lines_option.isChecked() + app_config = get_app_config() app_config.chart.show_lines = bool(state) for c in self.app.subscribing_charts: c.setDrawLines(state) def changeShowMarkerNumber(self) -> None: + app_config = get_app_config() app_config.chart.marker_label = bool( self.show_marker_number_option.isChecked() ) self.updateCharts() def changeFilledMarkers(self): + app_config = get_app_config() app_config.chart.marker_filled = bool( self.filled_marker_option.isChecked() ) self.updateCharts() def changeMarkerAtTip(self) -> None: + app_config = get_app_config() app_config.chart.marker_at_tip = bool(self.marker_at_tip.isChecked()) self.updateCharts() def changePointSize(self, size: int) -> None: + app_config = get_app_config() app_config.chart.point_size = size for c in self.app.subscribing_charts: c.setPointSize(size) def changeLineThickness(self, size: int) -> None: + app_config = get_app_config() app_config.chart.line_thickness = size for c in self.app.subscribing_charts: c.setLineThickness(size) def changeMarkerSize(self, size: int) -> None: + app_config = get_app_config() app_config.chart.marker_size = size self.markerSizeInput.setValue(size) self.updateCharts() def changeDarkMode(self) -> None: state = self.dark_mode_option.isChecked() + app_config = get_app_config() app_config.gui.dark_mode = bool(state) Chart.color.foreground = QColor(QColorConstants.LightGray) if state: @@ -608,4 +618,4 @@ def removeVSWRMarker(self) -> None: def updateCharts(self) -> None: for c in self.app.subscribing_charts: c.update() - store_config(self.app.settings, app_config) + self.app.settings.sync() diff --git a/tests/test_settings.py b/tests/test_settings.py index d0739885..81dc04ca 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -19,7 +19,14 @@ import unittest from dataclasses import dataclass, field -from NanoVNASaver.Defaults import AppConfig, AppSettings, GuiConfig, QSettings, restore_config, store_config +from NanoVNASaver.Defaults import ( + AppSettings, + GuiConfig, + QSettings, + get_app_config, + restore_config, + store_config, +) @dataclass @@ -90,9 +97,10 @@ def test_restore_empty(self): self.assertEqual(tc_3, TConfig()) def test_store(self): - tc_1 = AppConfig() + tc_1 = get_app_config() tc_1.gui.dark_mode = not tc_1.gui.dark_mode - store_config(self.settings_2, tc_1) + + store_config(self.settings_2) tc_2 = restore_config(self.settings_2) print(f"\n{tc_1}\n{tc_2}\n") self.assertEqual(tc_1, tc_2) From 5fe6bc33d120081423b7663260f95e43540e8d95 Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Wed, 29 Jan 2025 23:27:44 +0100 Subject: [PATCH 4/5] fix: save sweep settings --- src/NanoVNASaver/Controls/SweepControl.py | 25 ++++++++++++++++------- src/NanoVNASaver/Defaults.py | 17 +++++++++------ src/NanoVNASaver/NanoVNASaver.py | 5 +++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/NanoVNASaver/Controls/SweepControl.py b/src/NanoVNASaver/Controls/SweepControl.py index 0ac6d080..2b9ba0aa 100644 --- a/src/NanoVNASaver/Controls/SweepControl.py +++ b/src/NanoVNASaver/Controls/SweepControl.py @@ -22,6 +22,7 @@ from NanoVNASaver import NanoVNASaver +from ..Defaults import SweepConfig, get_app_config from ..Formatting import ( format_frequency_short, format_frequency_sweep, @@ -37,6 +38,7 @@ class SweepControl(Control): def __init__(self, app: NanoVNASaver): super().__init__(app, "Sweep control") + sweep_settings = self.get_settings() line = QtWidgets.QFrame() line.setFrameShape(QtWidgets.QFrame.Shape.VLine) @@ -48,7 +50,7 @@ def __init__(self, app: NanoVNASaver): input_layout.addLayout(input_right_layout) self.layout.addRow(input_layout) - self.input_start = FrequencyInputWidget() + self.input_start = FrequencyInputWidget(sweep_settings.start) self.input_start.setFixedHeight(20) self.input_start.setMinimumWidth(60) self.input_start.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) @@ -56,14 +58,14 @@ def __init__(self, app: NanoVNASaver): self.input_start.textChanged.connect(self.update_step_size) input_left_layout.addRow(QtWidgets.QLabel("Start"), self.input_start) - self.input_end = FrequencyInputWidget() + self.input_end = FrequencyInputWidget(sweep_settings.end) self.input_end.setFixedHeight(20) self.input_end.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) self.input_end.textEdited.connect(self.update_center_span) self.input_end.textChanged.connect(self.update_step_size) input_left_layout.addRow(QtWidgets.QLabel("Stop"), self.input_end) - self.input_center = FrequencyInputWidget() + self.input_center = FrequencyInputWidget(sweep_settings.center) self.input_center.setFixedHeight(20) self.input_center.setMinimumWidth(60) self.input_center.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) @@ -71,16 +73,14 @@ def __init__(self, app: NanoVNASaver): input_right_layout.addRow(QtWidgets.QLabel("Center"), self.input_center) - self.input_span = FrequencyInputWidget() + self.input_span = FrequencyInputWidget(sweep_settings.span) self.input_span.setFixedHeight(20) self.input_span.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) self.input_span.textEdited.connect(self.update_start_end) input_right_layout.addRow(QtWidgets.QLabel("Span"), self.input_span) - self.input_segments = QtWidgets.QLineEdit( - self.app.settings.value("Segments", "1") - ) + self.input_segments = QtWidgets.QLineEdit(sweep_settings.segments) self.input_segments.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) self.input_segments.setFixedHeight(20) self.input_segments.setFixedWidth(60) @@ -229,3 +229,14 @@ def update_sweep(self): def update_sweep_btn(self, enabled: bool) -> None: self.btn_start.setEnabled(enabled) + + def get_settings(self) -> SweepConfig: + return get_app_config().sweep_settings + + def store_settings(self) -> None: + settings = self.get_settings() + settings.start = self.input_start.text() + settings.end = self.input_end.text() + settings.center = self.input_center.text() + settings.span = self.input_span.text() + settings.segments = self.input_segments.text() diff --git a/src/NanoVNASaver/Defaults.py b/src/NanoVNASaver/Defaults.py index 8ec65eb3..5af87adf 100644 --- a/src/NanoVNASaver/Defaults.py +++ b/src/NanoVNASaver/Defaults.py @@ -123,6 +123,13 @@ class MarkersConfig: default_factory=lambda: QColor(QColorConstants.LightGray) ) +@dataclass +class SweepConfig: + start: str = "" + end: str = "" + center: str = "" + span: str = "" + segments: str = "1" @dataclass class AppConfig: @@ -131,6 +138,7 @@ class AppConfig: chart: ChartConfig = field(default_factory=ChartConfig) chart_colors: ChartColorsConfig = field(default_factory=ChartColorsConfig) markers: MarkersConfig = field(default_factory=MarkersConfig) + sweep_settings: SweepConfig = field(default_factory=SweepConfig) # noinspection PyDataclass @@ -182,7 +190,7 @@ def get_app_config() -> AppConfig: def restore_config(settings: AppSettings) -> AppConfig: logger.info("Loading settings from: %s", settings.fileName()) - + result = AppConfig() for field_it in fields(result): value = settings.restore_dataclass( @@ -190,14 +198,14 @@ def restore_config(settings: AppSettings) -> AppConfig: ) setattr(result, field_it.name, value) logger.debug("restored\n(\n%s\n)", result) - global _app_config + global _app_config #noqa PLW0603 _app_config = result return _app_config def store_config(settings: AppSettings) -> None: logger.info("Saving settings to: %s", settings.fileName()) - + logger.debug("storing\n(\n%s\n)", _app_config) for field_it in fields(_app_config): data_class = getattr(_app_config, field_it.name) @@ -228,6 +236,3 @@ def _to_type(data: object, data_type: type) -> object: return ( type_map[data_type](data) if data_type in type_map else data_type(data) ) - - - diff --git a/src/NanoVNASaver/NanoVNASaver.py b/src/NanoVNASaver/NanoVNASaver.py index f6456a70..207adf82 100644 --- a/src/NanoVNASaver/NanoVNASaver.py +++ b/src/NanoVNASaver/NanoVNASaver.py @@ -490,8 +490,6 @@ def sweep_start(self): self.s21_max_gain_label.setText("") self.tdr_result_label.setText("") - self.settings.setValue("Segments", self.sweep_control.get_segments()) - logger.debug("Starting worker thread") self.threadpool.start(self.worker) @@ -693,6 +691,9 @@ def closeEvent(self, a0: QtGui.QCloseEvent) -> None: app_config.gui.window_width = self.width() app_config.gui.window_height = self.height() app_config.gui.splitter_sizes = self.splitter.saveState() + + self.sweep_control.store_settings() + store_config(self.settings) # Dosconnect connected devices and release serial port From 942a9a368faa891c3a3cbc384344a6f1a7c8769f Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Wed, 29 Jan 2025 23:43:07 +0100 Subject: [PATCH 5/5] fix: refactor settings class --- src/NanoVNASaver/Defaults.py | 122 +++++++++++++++++-------------- src/NanoVNASaver/NanoVNASaver.py | 13 +--- tests/test_settings.py | 31 +++----- 3 files changed, 81 insertions(+), 85 deletions(-) diff --git a/src/NanoVNASaver/Defaults.py b/src/NanoVNASaver/Defaults.py index 5af87adf..50a39252 100644 --- a/src/NanoVNASaver/Defaults.py +++ b/src/NanoVNASaver/Defaults.py @@ -143,7 +143,20 @@ class AppConfig: # noinspection PyDataclass class AppSettings(QSettings): - def store_dataclass(self, name: str, data: object) -> None: + + def __init__(self, organization: str = "NanoVNASaver", application: str = "NanoVNASaver") -> None: + super().__init__( + QSettings.Format.IniFormat, + QSettings.Scope.UserScope, + organization, + application) + + self._app_config = AppConfig() + + def get_app_config(self) -> AppConfig: + return _app_config + + def _store_dataclass(self, name: str, data: object) -> None: assert is_dataclass(data) self.beginGroup(name) for field_it in fields(data): @@ -159,10 +172,10 @@ def store_dataclass(self, name: str, data: object) -> None: field_it.type, ) raise TypeError from exc - self.setValue(field_it.name, _from_type(value)) + self.setValue(field_it.name, AppSettings._from_type(value)) self.endGroup() - def restore_dataclass(self, name: str, data: object) -> object: + def _restore_dataclass(self, name: str, data: object) -> object: assert is_dataclass(data) result = replace(data) @@ -174,65 +187,64 @@ def restore_dataclass(self, name: str, data: object) -> object: setattr(result, field_it.name, default) continue try: - setattr(result, field_it.name, _to_type(value, field_it.type)) + setattr(result, field_it.name, AppSettings._to_type(value, field_it.type)) except TypeError: setattr(result, field_it.name, default) self.endGroup() return result + def restore_config(self) -> AppConfig: + logger.info("Loading settings from: %s", self.fileName()) + + result = AppConfig() + for field_it in fields(result): + value = self._restore_dataclass( + field_it.name.upper(), getattr(result, field_it.name) + ) + setattr(result, field_it.name, value) + logger.debug("restored\n(\n%s\n)", result) + self._app_config = result + return get_app_config() + + + def store_config(self) -> None: + logger.info("Saving settings to: %s", self.fileName()) + + logger.debug("storing\n(\n%s\n)", _app_config) + for field_it in fields(_app_config): + data_class = getattr(_app_config, field_it.name) + assert is_dataclass(data_class) + self._store_dataclass(field_it.name.upper(), data_class) + + @staticmethod + def _from_type(data) -> str: + type_map = { + bytearray: bytearray.hex, + QColor: QColor.getRgb, + QByteArray: QByteArray.toHex, + } + return ( + f"{type_map[type(data)](data)}" if type(data) in type_map else f"{data}" + ) + @staticmethod + def _to_type(data: object, data_type: type) -> object: + type_map = { + bool: lambda x: x.lower() == "true", + bytearray: bytearray.fromhex, + list: literal_eval, + tuple: literal_eval, + QColor: lambda x: QColor.fromRgb(*literal_eval(x)), + QByteArray: lambda x: QByteArray.fromHex(literal_eval(x)), + } + return ( + type_map[data_type](data) if data_type in type_map else data_type(data) + ) -_app_config = AppConfig() - -def get_app_config() -> AppConfig: - return _app_config - - -def restore_config(settings: AppSettings) -> AppConfig: - logger.info("Loading settings from: %s", settings.fileName()) - result = AppConfig() - for field_it in fields(result): - value = settings.restore_dataclass( - field_it.name.upper(), getattr(result, field_it.name) - ) - setattr(result, field_it.name, value) - logger.debug("restored\n(\n%s\n)", result) - global _app_config #noqa PLW0603 - _app_config = result - return _app_config - - -def store_config(settings: AppSettings) -> None: - logger.info("Saving settings to: %s", settings.fileName()) - - logger.debug("storing\n(\n%s\n)", _app_config) - for field_it in fields(_app_config): - data_class = getattr(_app_config, field_it.name) - assert is_dataclass(data_class) - settings.store_dataclass(field_it.name.upper(), data_class) - - -def _from_type(data) -> str: - type_map = { - bytearray: bytearray.hex, - QColor: QColor.getRgb, - QByteArray: QByteArray.toHex, - } - return ( - f"{type_map[type(data)](data)}" if type(data) in type_map else f"{data}" - ) +APP_SETTINGS = AppSettings() +_app_config = AppConfig() -def _to_type(data: object, data_type: type) -> object: - type_map = { - bool: lambda x: x.lower() == "true", - bytearray: bytearray.fromhex, - list: literal_eval, - tuple: literal_eval, - QColor: lambda x: QColor.fromRgb(*literal_eval(x)), - QByteArray: lambda x: QByteArray.fromHex(literal_eval(x)), - } - return ( - type_map[data_type](data) if data_type in type_map else data_type(data) - ) +def get_app_config() -> AppConfig: + return APP_SETTINGS.get_app_config() diff --git a/src/NanoVNASaver/NanoVNASaver.py b/src/NanoVNASaver/NanoVNASaver.py index 207adf82..5f6e6acb 100644 --- a/src/NanoVNASaver/NanoVNASaver.py +++ b/src/NanoVNASaver/NanoVNASaver.py @@ -54,7 +54,7 @@ from .Controls.MarkerControl import MarkerControl from .Controls.SerialControl import SerialControl from .Controls.SweepControl import SweepControl -from .Defaults import AppSettings, get_app_config, restore_config, store_config +from .Defaults import AppSettings, get_app_config from .Formatting import format_frequency, format_gain, format_vswr from .Hardware.Hardware import Interface from .Hardware.VNA import VNA @@ -93,13 +93,8 @@ def __init__(self) -> None: self.communicate = Communicate() self.s21att = 0.0 self.setWindowIcon(get_window_icon()) - self.settings = AppSettings( - QtCore.QSettings.Format.IniFormat, - QtCore.QSettings.Scope.UserScope, - "NanoVNASaver", - "NanoVNASaver", - ) - app_config = restore_config(self.settings) + self.settings = AppSettings() + app_config = self.settings.restore_config() self.threadpool = QtCore.QThreadPool() self.sweep = Sweep() self.worker = SweepWorker(self) @@ -694,7 +689,7 @@ def closeEvent(self, a0: QtGui.QCloseEvent) -> None: self.sweep_control.store_settings() - store_config(self.settings) + self.settings.store_config() # Dosconnect connected devices and release serial port self.serial_control.disconnect_device() diff --git a/tests/test_settings.py b/tests/test_settings.py index 81dc04ca..6bc31883 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -19,14 +19,7 @@ import unittest from dataclasses import dataclass, field -from NanoVNASaver.Defaults import ( - AppSettings, - GuiConfig, - QSettings, - get_app_config, - restore_config, - store_config, -) +from NanoVNASaver.Defaults import AppSettings, GuiConfig @dataclass @@ -45,14 +38,10 @@ class TestCases(unittest.TestCase): def setUp(self) -> None: self.settings_1 = AppSettings( - QSettings.Format.IniFormat, - QSettings.Scope.UserScope, "NanoVNASaver", "Test_1", ) self.settings_2 = AppSettings( - QSettings.Format.IniFormat, - QSettings.Scope.UserScope, "NanoVNASaver", "Test_2", ) @@ -66,8 +55,8 @@ def setUp(self) -> None: ) def test_store_dataclass(self): - self.settings_1.store_dataclass("Section1", self.config_1) - self.settings_1.store_dataclass("Section2", self.config_2) + self.settings_1._store_dataclass("Section1", self.config_1) + self.settings_1._store_dataclass("Section2", self.config_2) illegal_config = TConfig( my_int=4, my_float=3.0, @@ -76,11 +65,11 @@ def test_store_dataclass(self): my_list=(4, 5, 6), ) with self.assertRaises(TypeError): - self.settings_1.store_dataclass("SectionX", illegal_config) + self.settings_1._store_dataclass("SectionX", illegal_config) def test_restore_dataclass(self): - tc_1 = self.settings_1.restore_dataclass("Section1", self.config_1) - tc_2 = self.settings_1.restore_dataclass("Section2", self.config_2) + tc_1 = self.settings_1._restore_dataclass("Section1", self.config_1) + tc_2 = self.settings_1._restore_dataclass("Section2", self.config_2) self.assertNotEqual(tc_1, tc_2) self.assertEqual(tc_1, self.config_1) self.assertEqual(tc_2, self.config_2) @@ -93,15 +82,15 @@ def test_restore_dataclass(self): self.assertIsInstance(tc_2.my_float, float) def test_restore_empty(self): - tc_3 = self.settings_1.restore_dataclass("Section3", TConfig()) + tc_3 = self.settings_1._restore_dataclass("Section3", TConfig()) self.assertEqual(tc_3, TConfig()) def test_store(self): - tc_1 = get_app_config() + tc_1 = self.settings_2.get_app_config() tc_1.gui.dark_mode = not tc_1.gui.dark_mode - store_config(self.settings_2) - tc_2 = restore_config(self.settings_2) + self.settings_2.store_config() + tc_2 = self.settings_2.restore_config() print(f"\n{tc_1}\n{tc_2}\n") self.assertEqual(tc_1, tc_2) self.assertNotEqual(tc_2.gui, GuiConfig())