Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/settings #778

Merged
merged 5 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/NanoVNASaver/Charts/Chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion src/NanoVNASaver/Controls/MarkerControl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand Down
25 changes: 18 additions & 7 deletions src/NanoVNASaver/Controls/SweepControl.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from NanoVNASaver import NanoVNASaver

from ..Defaults import SweepConfig, get_app_config
from ..Formatting import (
format_frequency_short,
format_frequency_sweep,
Expand All @@ -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)

Expand All @@ -48,39 +50,37 @@ 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)
self.input_start.textEdited.connect(self.update_center_span)
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)
self.input_center.textEdited.connect(self.update_start_end)

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)
Expand Down Expand Up @@ -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()
130 changes: 79 additions & 51 deletions src/NanoVNASaver/Defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -131,60 +138,25 @@ 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)


app_config = AppConfig()


def restore_config(settings: "AppSettings") -> AppConfig:
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)
return result


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)
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}"
)
# noinspection PyDataclass
class AppSettings(QSettings):

def __init__(self, organization: str = "NanoVNASaver", application: str = "NanoVNASaver") -> None:
super().__init__(
QSettings.Format.IniFormat,
QSettings.Scope.UserScope,
organization,
application)

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)
)
self._app_config = AppConfig()

def get_app_config(self) -> AppConfig:
return _app_config

# noinspection PyDataclass
class AppSettings(QSettings):
def store_dataclass(self, name: str, data: object) -> None:
def _store_dataclass(self, name: str, data: object) -> None:
assert is_dataclass(data)
self.beginGroup(name)
for field_it in fields(data):
Expand All @@ -200,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)
Expand All @@ -215,8 +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_SETTINGS = AppSettings()

_app_config = AppConfig()

def get_app_config() -> AppConfig:
return APP_SETTINGS.get_app_config()
22 changes: 8 additions & 14 deletions src/NanoVNASaver/NanoVNASaver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import contextlib
import logging
import sys
import threading
from time import localtime, strftime

Expand Down Expand Up @@ -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
from .Formatting import format_frequency, format_gain, format_vswr
from .Hardware.Hardware import Interface
from .Hardware.VNA import VNA
Expand Down Expand Up @@ -94,14 +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",
)
logger.info("Settings from: %s", self.settings.fileName())
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)
Expand Down Expand Up @@ -492,8 +485,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)

Expand Down Expand Up @@ -690,17 +681,20 @@ 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)

self.sweep_control.store_settings()

self.settings.store_config()

# Dosconnect connected devices and release serial port
self.serial_control.disconnect_device()

a0.accept()
sys.exit()

def changeFont(self, font: QtGui.QFont) -> None:
qf_new = QtGui.QFontMetricsF(font)
Expand Down
Loading
Loading