kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Feature/refactor settings (#507)
* fixed Default module * fixed QByteArray parsing * updated changelog * refactored chartspull/509/head
rodzic
7b9d803b35
commit
01c83cd2f0
|
@ -1,6 +1,15 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
0.5.0 pre
|
||||
---------
|
||||
|
||||
- Fix crash on open in use serial device
|
||||
- Use a Defaults module for all settings -
|
||||
ignores old .ini settings
|
||||
- Refactoring and unifying Chart classes
|
||||
- No more automatic update checks (more privacy)
|
||||
|
||||
0.4.0
|
||||
-----
|
||||
|
||||
|
|
|
@ -19,13 +19,15 @@
|
|||
|
||||
import dataclasses as DC
|
||||
import logging
|
||||
import json
|
||||
|
||||
from PyQt5.QtCore import QSettings
|
||||
from ast import literal_eval
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QSettings, QByteArray
|
||||
from PyQt5.QtGui import QColor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@DC.dataclass
|
||||
|
@ -33,12 +35,22 @@ class GUI:
|
|||
window_height: int = 950
|
||||
window_width: int = 1433
|
||||
font_size: int = 8
|
||||
custom_colors: bool = False
|
||||
dark_mode: bool = False
|
||||
# TODO: implement QByteArray
|
||||
splitter_sizes: bytearray = DC.field(default_factory=bytearray)
|
||||
splitter_sizes: QByteArray = DC.field(default_factory=QByteArray)
|
||||
markers_hidden: bool = False
|
||||
|
||||
|
||||
@DC.dataclass
|
||||
class ChartsSelected:
|
||||
chart_00: str = 'S11 Smith Chart'
|
||||
chart_01: str = 'S11 Return Loss'
|
||||
chart_02: str = 'None'
|
||||
chart_10: str = 'S21 Polar Plot'
|
||||
chart_11: str = 'S21 Gain'
|
||||
chart_12: str = 'None'
|
||||
|
||||
|
||||
@DC.dataclass
|
||||
class Chart:
|
||||
point_size: int = 2
|
||||
|
@ -50,16 +62,51 @@ class Chart:
|
|||
marker_at_tip: bool = False
|
||||
marker_size: int = 8
|
||||
returnloss_is_positive: bool = False
|
||||
show_bands: bool = False
|
||||
vswr_lines: list = DC.field(default_factory=lambda: [])
|
||||
|
||||
@DC.dataclass
|
||||
class ChartColors: # pylint: disable=too-many-instance-attributes
|
||||
background: QColor = QColor(QtCore.Qt.white)
|
||||
foreground: QColor = QColor(QtCore.Qt.lightGray)
|
||||
reference: QColor = QColor(0, 0, 255, 64)
|
||||
reference_secondary: QColor = QColor(0, 0, 192, 48)
|
||||
sweep: QColor = QColor(QtCore.Qt.darkYellow)
|
||||
sweep_secondary: QColor = QColor(QtCore.Qt.darkMagenta)
|
||||
swr: QColor = QColor(255, 0, 0, 128)
|
||||
text: QColor = QColor(QtCore.Qt.black)
|
||||
bands: QColor = QColor(128, 128, 128, 48)
|
||||
|
||||
|
||||
@DC.dataclass
|
||||
class Markers:
|
||||
active_labels: list = DC.field(default_factory=lambda: [
|
||||
"actualfreq", "impedance", "serr", "serl", "serc", "parr", "parlc",
|
||||
"vswr", "returnloss", "s11q", "s11phase", "s21gain", "s21phase",
|
||||
])
|
||||
colored_names: bool = True
|
||||
color_0: QColor = QColor(QtCore.Qt.darkGray)
|
||||
color_1: QColor = QColor(255, 0, 0)
|
||||
color_2: QColor = QColor(0, 255, 0)
|
||||
color_3: QColor = QColor(0, 0, 255)
|
||||
color_4: QColor = QColor(0, 255, 255)
|
||||
color_5: QColor = QColor(255, 0, 255)
|
||||
color_6: QColor = QColor(255, 255, 0)
|
||||
color_7: QColor = QColor(QtCore.Qt.lightGray)
|
||||
|
||||
|
||||
@DC.dataclass
|
||||
class CFG:
|
||||
gui: object = GUI()
|
||||
charts_selected: object = ChartsSelected()
|
||||
chart: object = Chart()
|
||||
chart_colors: object = ChartColors()
|
||||
markers: object = Markers()
|
||||
|
||||
|
||||
cfg = CFG()
|
||||
|
||||
|
||||
def restore(settings: 'AppSettings') -> CFG:
|
||||
result = CFG()
|
||||
for field in DC.fields(result):
|
||||
|
@ -70,15 +117,42 @@ def restore(settings: 'AppSettings') -> CFG:
|
|||
return result
|
||||
|
||||
|
||||
def store(settings: 'AppSettings', data: CFG) -> None:
|
||||
def store(settings: 'AppSettings', data: CFG = None) -> None:
|
||||
data = data or cfg
|
||||
logger.debug("storing\n(\n%s\n)", data)
|
||||
assert isinstance(data, CFG)
|
||||
for field in DC.fields(data):
|
||||
data_class = getattr(data, field.name)
|
||||
data_class = getattr(data, field.name)
|
||||
assert DC.is_dataclass(data_class)
|
||||
settings.store_dataclass(field.name.upper(), data_class)
|
||||
|
||||
|
||||
def from_type(data) -> str:
|
||||
type_map = {
|
||||
bytearray: lambda x: x.hex(),
|
||||
QColor: lambda x: x.getRgb(),
|
||||
QByteArray: lambda x: x.toHex(),
|
||||
}
|
||||
if type(data) in type_map:
|
||||
return str(type_map[type(data)](data))
|
||||
return str(data)
|
||||
|
||||
|
||||
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)),
|
||||
}
|
||||
if data_type in type_map:
|
||||
return type_map[data_type](data)
|
||||
return data_type(data)
|
||||
|
||||
|
||||
# noinspection PyDataclass
|
||||
class AppSettings(QSettings):
|
||||
def store_dataclass(self, name: str, data: object) -> None:
|
||||
assert DC.is_dataclass(data)
|
||||
|
@ -87,16 +161,11 @@ class AppSettings(QSettings):
|
|||
value = getattr(data, field.name)
|
||||
try:
|
||||
assert isinstance(value, field.type)
|
||||
except AssertionError:
|
||||
logger.error("%s: %s is not a %s", name, field.name,
|
||||
field.type)
|
||||
continue
|
||||
if field.type not in (int, float, str, bool):
|
||||
try:
|
||||
value = json.dumps(value)
|
||||
except TypeError:
|
||||
value = field.type(value).hex()
|
||||
self.setValue(field.name, value)
|
||||
except AssertionError as exc:
|
||||
logger.error("%s: %s of type %s is not a %s",
|
||||
name, field.name, type(value), field.type)
|
||||
raise TypeError from exc
|
||||
self.setValue(field.name, from_type(value))
|
||||
self.endGroup()
|
||||
|
||||
def restore_dataclass(self, name: str, data: object) -> object:
|
||||
|
@ -105,21 +174,14 @@ class AppSettings(QSettings):
|
|||
result = DC.replace(data)
|
||||
self.beginGroup(name)
|
||||
for field in DC.fields(data):
|
||||
value = None
|
||||
if field.type in (int, float, str, bool):
|
||||
value = self.value(field.name,
|
||||
type=field.type,
|
||||
defaultValue=field.default)
|
||||
else:
|
||||
default = getattr(data, field.name)
|
||||
try:
|
||||
value = json.loads(
|
||||
self.value(field.name, type=str,
|
||||
defaultValue=json.dumps(default)))
|
||||
except TypeError:
|
||||
value = self.value(field.name)
|
||||
value = bytes.fromhex(value) if value is str else default
|
||||
setattr(result, field.name, field.type(value))
|
||||
default = getattr(data, field.name)
|
||||
value = self.value(field.name, type=str, defaultValue="")
|
||||
if not value:
|
||||
setattr(result, field.name, default)
|
||||
continue
|
||||
try:
|
||||
setattr(result, field.name, to_type(value, field.type))
|
||||
except TypeError:
|
||||
setattr(result, field.name, default)
|
||||
self.endGroup()
|
||||
|
||||
return result
|
||||
|
|
14
README.md
14
README.md
|
@ -17,6 +17,14 @@ points, and generally display and analyze the resulting data.
|
|||
Latest Changes
|
||||
--------------
|
||||
|
||||
### Changes in 0.5.0 pre
|
||||
|
||||
- Fix crash on open in use serial device
|
||||
- Use a Defaults module for all settings -
|
||||
ignores old .ini settings
|
||||
- Refactoring and unifying Chart classes
|
||||
- No more automatic update checks (more privacy)
|
||||
|
||||
### Changes in 0.4.0
|
||||
|
||||
- PA0JOZ Enhanced Response Correction
|
||||
|
@ -36,12 +44,6 @@ Latest Changes
|
|||
- Reference plane applied after calibration
|
||||
- Calibration fixes by DiSlord
|
||||
|
||||
### Changes in v0.3.9
|
||||
|
||||
- TX Power on V2
|
||||
- New analysis
|
||||
- Magnitude Z Chart
|
||||
- VSWR Chart improvements
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
|
Ładowanie…
Reference in New Issue