Feature/linting 220514 (#503)

* Unified Chart Code
* New Defauls class for persistance of settings - fixes #491
* Removed non-interactive update checks
pull/504/head
Holger Müller 2022-05-24 17:05:59 +02:00 zatwierdzone przez GitHub
rodzic 6aa7aaa051
commit 3f4a262abe
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
14 zmienionych plików z 385 dodań i 642 usunięć

Wyświetl plik

@ -16,16 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import math
import logging
from decimal import InvalidOperation
from typing import List
from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@ -34,264 +25,7 @@ logger = logging.getLogger(__name__)
class CapacitanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 45
self.dim.width = 250
self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
self.minValue = -1
self.maxValue = 1
self.span = 1
self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (F)")
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(Chart.color.sweep)
pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(Chart.color.sweep)
line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
if len(self.data) > 0:
fstart = self.data[0].freq
fstop = self.data[len(self.data)-1].freq
else:
fstart = self.reference[0].freq
fstop = self.reference[len(self.reference) - 1].freq
self.fstart = fstart
self.fstop = fstop
else:
fstart = self.fstart = self.minFrequency
fstop = self.fstop = self.maxFrequency
# Draw bands if required
if self.bands.enabled:
self.drawBands(qp, fstart, fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue / 10e11
minValue = self.minDisplayValue / 10e11
self.maxValue = maxValue
self.minValue = minValue
else:
# Find scaling
minValue = 1
maxValue = -1
for d in self.data:
val = d.capacitiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < self.fstart or d.freq > self.fstop:
continue
val = d.capacitiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
self.maxValue = maxValue
self.minValue = minValue
span = maxValue - minValue
if span == 0:
logger.info("Span is zero for CapacitanceChart, setting to a small value.")
span = 1e-15
self.span = span
target_ticks = math.floor(self.dim.height / 60)
fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
try:
y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
qp.setPen(Chart.color.text)
if val != minValue:
valstr = str(Value(val, fmt=fmt))
qp.drawText(3, y + 3, valstr)
except (ValueError, InvalidOperation) as exc:
logger.debug("Drawing wrent wrong: %s", exc)
y = self.topMargin
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
self.leftMargin + self.dim.width, self.topMargin)
qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
self.drawData(qp, self.data, Chart.color.sweep)
self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
try:
return (
self.topMargin +
round((self.maxValue - d.capacitiveEquivalent()) /
self.span * self.dim.height))
except ValueError:
return self.topMargin
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
new_chart: CapacitanceChart = super().copy()
new_chart.span = self.span
return new_chart
class InductanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 30
self.dim.width = 250
self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
self.minValue = -1
self.maxValue = 1
self.span = 1
self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (H)")
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(Chart.color.sweep)
pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(Chart.color.sweep)
line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
if len(self.data) > 0:
fstart = self.data[0].freq
fstop = self.data[len(self.data)-1].freq
else:
fstart = self.reference[0].freq
fstop = self.reference[len(self.reference) - 1].freq
self.fstart = fstart
self.fstop = fstop
else:
fstart = self.fstart = self.minFrequency
fstop = self.fstop = self.maxFrequency
# Draw bands if required
if self.bands.enabled:
self.drawBands(qp, fstart, fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue / 10e11
minValue = self.minDisplayValue / 10e11
self.maxValue = maxValue
self.minValue = minValue
else:
# Find scaling
minValue = 1
maxValue = -1
for d in self.data:
val = d.inductiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < self.fstart or d.freq > self.fstop:
continue
val = d.inductiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
self.maxValue = maxValue
self.minValue = minValue
span = maxValue - minValue
if span == 0:
logger.info("Span is zero for CapacitanceChart, setting to a small value.")
span = 1e-15
self.span = span
target_ticks = math.floor(self.dim.height / 60)
fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
qp.setPen(Chart.color.text)
if val != minValue:
valstr = str(Value(val, fmt=fmt))
qp.drawText(3, y + 3, valstr)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
self.leftMargin + self.dim.width, self.topMargin)
qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
self.drawData(qp, self.data, Chart.color.sweep)
self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
return (self.topMargin +
round((self.maxValue - d.inductiveEquivalent()) /
self.span * self.dim.height))
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
new_chart: InductanceChart = super().copy()
new_chart.span = self.span
return new_chart
self.name_unit = "F"
self.value_function = lambda x: x.capacitiveEquivalent()

Wyświetl plik

@ -24,6 +24,7 @@ from typing import List, Set, Tuple, ClassVar, Any
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import pyqtSignal
from NanoVNASaver import Defaults
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.Marker import Marker
@ -64,23 +65,15 @@ class ChartFlags:
draw_lines: bool = False
is_popout: bool = False
@dataclass
class ChartMarkerConfig:
draw_label: bool = False
fill: bool = False
at_tip: bool = False
size: int = 3
class ChartMarker(QtWidgets.QWidget):
cfg: ClassVar[ChartMarkerConfig] = ChartMarkerConfig()
def __init__(self, qp: QtGui.QPaintDevice):
super().__init__()
self.qp = qp
def draw(self, x: int, y: int, color: QtGui.QColor, text: str = ""):
offset = self.cfg.size // 2
if self.cfg.at_tip:
offset = Defaults.cfg.chart.marker_size // 2
if Defaults.cfg.chart.marker_at_tip:
y -= offset
pen = QtGui.QPen(color)
self.qp.setPen(pen)
@ -90,12 +83,12 @@ class ChartMarker(QtWidgets.QWidget):
qpp.lineTo(x + offset, y - offset)
qpp.lineTo(x, y + offset)
if self.cfg.fill:
if Defaults.cfg.chart.marker_filled:
self.qp.fillPath(qpp, color)
else:
self.qp.drawPath(qpp)
if text and self.cfg.draw_label:
if text and Defaults.cfg.chart.marker_label:
text_width = self.qp.fontMetrics().horizontalAdvance(text)
self.qp.drawText(x - text_width // 2, y - 3 - offset, text)
@ -104,7 +97,6 @@ class Chart(QtWidgets.QWidget):
bands: ClassVar[Any] = None
popoutRequested: ClassVar[Any] = pyqtSignal(object)
color: ClassVar[ChartColors] = ChartColors()
marker_cfg: ClassVar[ChartMarkerConfig] = ChartMarkerConfig()
def __init__(self, name):
super().__init__()
@ -160,7 +152,7 @@ class Chart(QtWidgets.QWidget):
self.update()
def setMarkerSize(self, size):
ChartMarker.cfg.size = size
Defaults.cfg.chart.marker_size = size
self.update()
def setSweepTitle(self, title):
@ -249,7 +241,6 @@ class Chart(QtWidgets.QWidget):
new_chart.reference = self.reference
new_chart.dim = replace(self.dim)
new_chart.flag = replace(self.flag)
new_chart.marker_cfg = replace(self.marker_cfg)
new_chart.markers = self.markers
new_chart.swrMarkers = self.swrMarkers
new_chart.bands = self.bands

Wyświetl plik

@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -35,30 +35,22 @@ logger = logging.getLogger(__name__)
class FrequencyChart(Chart):
fstart = 0
fstop = 0
maxFrequency = 100000000
minFrequency = 1000000
# TODO: use unscaled values instead of unit dependend ones
minDisplayValue = -1
maxDisplayValue = 1
fixedSpan = False
fixedValues = False
logarithmicX = False
logarithmicY = False
leftMargin = 30
rightMargin = 20
bottomMargin = 20
topMargin = 30
def __init__(self, name):
super().__init__(name)
self.maxFrequency = 100000000
self.minFrequency = 1000000
self.fixedSpan = False
self.fixedValues = False
self.logarithmicX = False
self.logarithmicY = False
self.leftMargin = 30
self.rightMargin = 20
self.bottomMargin = 20
self.topMargin = 30
self.dim.width = 250
self.dim.height = 250
self.fstart = 0
@ -67,6 +59,10 @@ class FrequencyChart(Chart):
self.name_unit = ""
self.value_function = lambda x: 0.0
# TODO: use unscaled values instead of unit dependend ones
self.minDisplayValue = -1
self.maxDisplayValue = 1
self.minValue = -1
self.maxValue = 1
self.span = 1
@ -189,17 +185,15 @@ class FrequencyChart(Chart):
def _set_start_stop(self):
if self.fixedSpan:
fstart = self.minFrequency
fstop = self.maxFrequency
else:
if len(self.data) > 0:
fstart = self.data[0].freq
fstop = self.data[len(self.data) - 1].freq
else:
fstart = self.reference[0].freq
fstop = self.reference[len(self.reference) - 1].freq
self.fstart = fstart
self.fstop = fstop
self.fstart = self.minFrequency
self.fstop = self.maxFrequency
return
if self.data:
self.fstart = self.data[0].freq
self.fstop = self.data[len(self.data) - 1].freq
return
self.fstart = self.reference[0].freq
self.fstop = self.reference[len(self.reference) - 1].freq
def contextMenuEvent(self, event):
self.action_set_fixed_start.setText(
@ -336,10 +330,13 @@ class FrequencyChart(Chart):
return math.floor(self.width() / 2)
def getYPosition(self, d: Datapoint) -> int:
return (
self.topMargin +
round((self.maxValue - d.capacitiveEquivalent()) /
self.span * self.dim.height))
try:
return (
self.topMargin +
round((self.maxValue - self.value_function(d) /
self.span * self.dim.height)))
except ValueError:
return self.topMargin
def frequencyAtPosition(self, x, limit=True) -> int:
"""
@ -352,20 +349,21 @@ class FrequencyChart(Chart):
and the value is before or after the chart,
returns minimum or maximum frequencies.
"""
if self.fstop - self.fstart > 0:
absx = x - self.leftMargin
if limit and absx < 0:
if self.fstop - self.fstart <= 0:
return -1
absx = x - self.leftMargin
if limit:
if absx < 0:
return self.fstart
if limit and absx > self.dim.width:
if absx > self.dim.width:
return self.fstop
if self.logarithmicX:
span = math.log(self.fstop) - math.log(self.fstart)
step = span / self.dim.width
return round(math.exp(math.log(self.fstart) + absx * step))
span = self.fstop - self.fstart
if self.logarithmicX:
span = math.log(self.fstop) - math.log(self.fstart)
step = span / self.dim.width
return round(self.fstart + absx * step)
return -1
return round(math.exp(math.log(self.fstart) + absx * step))
span = self.fstop - self.fstart
step = span / self.dim.width
return round(self.fstart + absx * step)
def valueAtPosition(self, y) -> List[float]:
"""
@ -574,26 +572,21 @@ class FrequencyChart(Chart):
self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def _find_scaling(self) -> Tuple[int, int]:
def _find_scaling(self) -> Tuple[float, float]:
min_value = self.minDisplayValue / 10e11
max_value = self.maxDisplayValue / 10e11
if self.fixedValues:
return (self.minDisplayValue / 10e11,
self.maxDisplayValue / 10e11)
min_value = 1
max_value = -1
return (min_value, max_value)
for d in self.data:
val = self.value_function(d)
if val > max_value:
max_value = val
if val < min_value:
min_value = val
min_value = min(min_value, val)
max_value = max(max_value, val)
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < self.fstart or d.freq > self.fstop:
continue
val = self.value_function(d)
if val > max_value:
max_value = val
if val < min_value:
min_value = val
min_value = min(min_value, val)
max_value = max(max_value, val)
return (min_value, max_value)
def drawFrequencyTicks(self, qp):

Wyświetl plik

@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020 NanoVNA-Saver Authors
# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -16,15 +16,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import math
import logging
from typing import List
from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.Charts.Frequency import FrequencyChart
logger = logging.getLogger(__name__)
@ -33,127 +26,7 @@ logger = logging.getLogger(__name__)
class InductanceChart(FrequencyChart):
def __init__(self, name=""):
super().__init__(name)
self.leftMargin = 45
self.dim.width = 250
self.dim.height = 250
self.minDisplayValue = 0
self.maxDisplayValue = 100
self.minValue = -1
self.maxValue = 1
self.span = 1
self.setMinimumSize(self.dim.width + self.rightMargin + self.leftMargin,
self.dim.height + self.topMargin + self.bottomMargin)
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
self.setPalette(pal)
self.setAutoFillBackground(True)
def drawChart(self, qp: QtGui.QPainter):
qp.setPen(QtGui.QPen(Chart.color.text))
qp.drawText(3, 15, self.name + " (H)")
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.dim.height+5)
qp.drawLine(self.leftMargin-5, self.topMargin+self.dim.height,
self.leftMargin+self.dim.width, self.topMargin + self.dim.height)
self.drawTitle(qp)
def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(Chart.color.sweep)
pen.setWidth(self.dim.point)
line_pen = QtGui.QPen(Chart.color.sweep)
line_pen.setWidth(self.dim.line)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
if len(self.data) > 0:
fstart = self.data[0].freq
fstop = self.data[len(self.data)-1].freq
else:
fstart = self.reference[0].freq
fstop = self.reference[len(self.reference) - 1].freq
self.fstart = fstart
self.fstop = fstop
else:
fstart = self.fstart = self.minFrequency
fstop = self.fstop = self.maxFrequency
# Draw bands if required
if self.bands.enabled:
self.drawBands(qp, fstart, fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue / 10e11
minValue = self.minDisplayValue / 10e11
self.maxValue = maxValue
self.minValue = minValue
else:
# Find scaling
minValue = 1
maxValue = -1
for d in self.data:
val = d.inductiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < self.fstart or d.freq > self.fstop:
continue
val = d.inductiveEquivalent()
if val > maxValue:
maxValue = val
if val < minValue:
minValue = val
self.maxValue = maxValue
self.minValue = minValue
span = maxValue - minValue
if span == 0:
logger.info("Span is zero for CapacitanceChart, setting to a small value.")
span = 1e-15
self.span = span
target_ticks = math.floor(self.dim.height / 60)
fmt = Format(max_nr_digits=3)
for i in range(target_ticks):
val = minValue + (i / target_ticks) * span
y = self.topMargin + round((self.maxValue - val) / self.span * self.dim.height)
qp.setPen(Chart.color.text)
if val != minValue:
valstr = str(Value(val, fmt=fmt))
qp.drawText(3, y + 3, valstr)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, self.topMargin,
self.leftMargin + self.dim.width, self.topMargin)
qp.setPen(Chart.color.text)
qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt)))
qp.drawText(3, self.dim.height+self.topMargin, str(Value(minValue, fmt=fmt)))
self.drawFrequencyTicks(qp)
self.drawData(qp, self.data, Chart.color.sweep)
self.drawData(qp, self.reference, Chart.color.reference)
self.drawMarkers(qp)
def getYPosition(self, d: Datapoint) -> int:
return (self.topMargin +
round((self.maxValue - d.inductiveEquivalent()) /
self.span * self.dim.height))
def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
return [val * 10e11]
def copy(self):
new_chart: InductanceChart = super().copy()
new_chart.span = self.span
return new_chart
self.name_unit = "H"
self.value_function = lambda x: x.inductiveEquivalent()

Wyświetl plik

@ -23,7 +23,8 @@ from typing import List
from PyQt5 import QtGui
from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.SITools import Format, Value
from NanoVNASaver.SITools import (
Format, Value, round_ceil, round_floor)
from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.Charts.Frequency import FrequencyChart
from NanoVNASaver.Charts.LogMag import LogMagChart
@ -54,48 +55,34 @@ class MagnitudeZChart(FrequencyChart):
self.drawBands(qp, self.fstart, self.fstop)
if self.fixedValues:
maxValue = self.maxDisplayValue
minValue = self.minDisplayValue
self.maxValue = maxValue
if self.logarithmicY and minValue <= 0:
self.minValue = 0.01
else:
self.minValue = minValue
self.maxValue = self.maxDisplayValue
self.minValue = max(
self.minDisplayValue, 0.01) if self.logarithmicY else self.minDisplayValue
else:
# Find scaling
minValue = 100
maxValue = 0
self.minValue = 100
self.maxValue = 0
for d in self.data:
mag = self.magnitude(d)
if math.isinf(mag): # Avoid infinite scales
if math.isinf(mag): # Avoid infinite scales
continue
if mag > maxValue:
maxValue = mag
if mag < minValue:
minValue = mag
self.maxValue = max(self.maxValue, mag)
self.minValue = min(self.minValue, mag)
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < self.fstart or d.freq > self.fstop:
continue
mag = self.magnitude(d)
if math.isinf(mag): # Avoid infinite scales
if math.isinf(mag): # Avoid infinite scales
continue
if mag > maxValue:
maxValue = mag
if mag < minValue:
minValue = mag
self.maxValue = max(self.maxValue, mag)
self.minValue = min(self.minValue, mag)
minValue = 10*math.floor(minValue/10)
if self.logarithmicY and minValue <= 0:
minValue = 0.01
self.minValue = minValue
self.minValue = round_floor(self.minValue, 2)
if self.logarithmicY and self.minValue <= 0:
self.minValue = 0.01
self.maxValue = round_ceil(self.maxValue, 2)
maxValue = 10*math.ceil(maxValue/10)
self.maxValue = maxValue
span = maxValue-minValue
if span == 0:
span = 0.01
self.span = span
self.span = (self.maxValue - self.minValue) or 0.01
# We want one horizontal tick per 50 pixels, at most
horizontal_ticks = math.floor(self.dim.height/50)

Wyświetl plik

@ -21,6 +21,7 @@ import logging
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QCheckBox
from NanoVNASaver import Defaults
from NanoVNASaver.Marker import Marker
from NanoVNASaver.Controls.Control import Control
@ -28,13 +29,21 @@ from NanoVNASaver.Controls.Control import Control
logger = logging.getLogger(__name__)
class ShowButton(QtWidgets.QPushButton):
def setText(self, text: str=''):
if not text:
text = ("Show data"
if Defaults.cfg.gui.markers_hidden else "Hide data")
super().setText(text)
self.setToolTip("Toggle visibility of marker readings area")
class MarkerControl(Control):
def __init__(self, app: QtWidgets.QWidget):
super().__init__(app, "Markers")
marker_count = max(self.app.settings.value("MarkerCount", 3, int), 1)
for i in range(marker_count):
for i in range(Defaults.cfg.chart.marker_count):
marker = Marker("", self.app.settings)
# marker.setFixedHeight(20)
marker.updated.connect(self.app.markerUpdated)
@ -56,12 +65,9 @@ class MarkerControl(Control):
self.layout.addRow(layout2)
self.showMarkerButton = QtWidgets.QPushButton()
self.showMarkerButton = ShowButton()
self.showMarkerButton.setFixedHeight(20)
if self.app.marker_frame.isHidden():
self.showMarkerButton.setText("Show data")
else:
self.showMarkerButton.setText("Hide data")
self.showMarkerButton.setText()
self.showMarkerButton.clicked.connect(self.toggle_frame)
lock_radiobutton = QtWidgets.QRadioButton("Locked")
@ -76,10 +82,10 @@ class MarkerControl(Control):
def toggle_frame(self):
def settings(hidden: bool):
self.app.marker_frame.setHidden(not hidden)
self.app.settings.setValue("MarkersVisible", hidden)
self.showMarkerButton.setText(
"Hide data" if hidden else "Show data")
Defaults.cfg.gui.markers_hidden = not hidden
self.app.marker_frame.setHidden(
Defaults.cfg.gui.markers_hidden)
self.showMarkerButton.setText()
self.showMarkerButton.repaint()
settings(self.app.marker_frame.isHidden())

Wyświetl plik

@ -0,0 +1,125 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import dataclasses as DC
import logging
import json
from PyQt5.QtCore import QSettings
logger = logging.getLogger(__name__)
# pylint: disable=too-few-public-methods
# pylint: disable=too-many-instance-attributes
@DC.dataclass
class GUI:
window_height: int = 950
window_width: int = 1433
font_size: int = 8
dark_mode: bool = False
# TODO: implement QByteArray
splitter_sizes: bytearray = DC.field(default_factory=bytearray)
markers_hidden: bool = False
@DC.dataclass
class Chart:
point_size: int = 2
show_lines: bool = False
line_thickness: int = 1
marker_count: int = 3
marker_label: bool = False
marker_filled: bool = False
marker_at_tip: bool = False
marker_size: int = 8
returnloss_is_positive: bool = False
@DC.dataclass
class CFG:
gui: object = GUI()
chart: object = Chart()
cfg = CFG()
def restore(settings: 'AppSettings') -> CFG:
result = CFG()
for field in DC.fields(result):
value = settings.restore_dataclass(field.name.upper(),
getattr(result, field.name))
setattr(result, field.name, value)
logger.debug("restored\n(\n%s\n)", result)
return result
def store(settings: 'AppSettings', data: CFG) -> None:
logger.debug("storing\n(\n%s\n)", data)
assert isinstance(data, CFG)
for field in DC.fields(data):
data_class = getattr(data, field.name)
assert DC.is_dataclass(data_class)
settings.store_dataclass(field.name.upper(), data_class)
class AppSettings(QSettings):
def store_dataclass(self, name: str, data: object) -> None:
assert DC.is_dataclass(data)
self.beginGroup(name)
for field in DC.fields(data):
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)
self.endGroup()
def restore_dataclass(self, name: str, data: object) -> object:
assert DC.is_dataclass(data)
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))
self.endGroup()
return result

Wyświetl plik

@ -24,6 +24,7 @@ from time import strftime, localtime
from PyQt5 import QtWidgets, QtCore, QtGui
from NanoVNASaver import Defaults
from .Windows import (
AboutWindow, AnalysisWindow, CalibrationWindow,
DeviceSettingsWindow, DisplaySettingsWindow, SweepSettingsWindow,
@ -69,10 +70,11 @@ class NanoVNASaver(QtWidgets.QWidget):
else:
self.icon = QtGui.QIcon("icon_48x48.png")
self.setWindowIcon(self.icon)
self.settings = QtCore.QSettings(QtCore.QSettings.IniFormat,
self.settings = Defaults.AppSettings(QtCore.QSettings.IniFormat,
QtCore.QSettings.UserScope,
"NanoVNASaver", "NanoVNASaver")
logger.info("Settings from: %s", self.settings.fileName())
Defaults.cfg = Defaults.restore(self.settings)
self.threadpool = QtCore.QThreadPool()
self.sweep = Sweep()
self.worker = SweepWorker(self)
@ -121,9 +123,8 @@ class NanoVNASaver(QtWidgets.QWidget):
outer.addWidget(scrollarea)
self.setLayout(outer)
scrollarea.setWidgetResizable(True)
window_width = self.settings.value("WindowWidth", 1350, type=int)
window_height = self.settings.value("WindowHeight", 950, type=int)
self.resize(window_width, window_height)
self.resize(Defaults.cfg.gui.window_width,
Defaults.cfg.gui.window_height)
scrollarea.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
@ -203,18 +204,14 @@ class NanoVNASaver(QtWidgets.QWidget):
left_column = QtWidgets.QVBoxLayout()
right_column = QtWidgets.QVBoxLayout()
right_column.addLayout(self.charts_layout)
self.marker_frame.setHidden(
not self.settings.value("MarkersVisible", True, bool))
self.marker_frame.setHidden(Defaults.cfg.gui.markers_hidden)
chart_widget = QtWidgets.QWidget()
chart_widget.setLayout(right_column)
self.splitter = QtWidgets.QSplitter()
self.splitter.addWidget(self.marker_frame)
self.splitter.addWidget(chart_widget)
try:
self.splitter.restoreState(self.settings.value("SplitterSizes"))
except TypeError:
pass
self.splitter.restoreState(Defaults.cfg.gui.splitter_sizes)
layout.addLayout(left_column)
layout.addWidget(self.splitter, 2)
@ -638,17 +635,18 @@ class NanoVNASaver(QtWidgets.QWidget):
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
self.worker.stopped = True
self.settings.setValue("MarkerCount", Marker.count())
for marker in self.markers:
marker.update_settings()
self.settings.setValue("WindowHeight", self.height())
self.settings.setValue("WindowWidth", self.width())
self.settings.setValue("SplitterSizes", self.splitter.saveState())
self.settings.sync()
self.bands.saveSettings()
self.threadpool.waitForDone(2500)
Defaults.cfg.chart.marker_count = Marker.count()
Defaults.cfg.gui.window_width = self.width()
Defaults.cfg.gui.window_height = self.height()
Defaults.cfg.gui.splitter_sizes = bytearray(self.splitter.saveState())
Defaults.store(self.settings, Defaults.cfg)
a0.accept()
sys.exit()

Wyświetl plik

@ -34,6 +34,13 @@ def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real:
return rmax
return value
def round_ceil(value: Real, digits: int=0) -> Real:
factor = 10 ** digits
return factor * math.ceil(value / factor)
def round_floor(value: Real, digits: int=0) -> Real:
factor = 10 ** digits
return factor * math.floor(value / factor)
class Format(NamedTuple):
max_nr_digits: int = 6
@ -74,8 +81,8 @@ class Value:
self._value = decimal.Decimal(value, context=Value.CTX)
def __repr__(self) -> str:
return (f"{self.__class__.__name__}(" + repr(self._value) +
f", '{self._unit}', {self.fmt})")
return (f"{self.__class__.__name__}("
f"{repr(self._value)}, '{self._unit}', {self.fmt})")
def __str__(self) -> str:
fmt = self.fmt
@ -100,10 +107,10 @@ class Value:
max_digits = fmt.max_nr_digits + (
(1 if not fmt.fix_decimals and abs(real) < 10 else 0) +
(1 if not fmt.fix_decimals and abs(real) < 100 else 0))
formstr = "." + str(max_digits - 3) + "f"
formstr = f".{max_digits - 3}f"
if self.fmt.allways_signed:
formstr = "+" + formstr
formstr = f"+{formstr}"
result = format(real, formstr)
if float(result) == 0.0:

Wyświetl plik

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import contextlib
import logging
from time import strftime, localtime
from urllib import request, error
@ -54,7 +55,7 @@ class AboutWindow(QtWidgets.QWidget):
layout.addWidget(QtWidgets.QLabel(""))
layout.addWidget(QtWidgets.QLabel(
"\N{COPYRIGHT SIGN} Copyright 2019, 2020 Rune B. Broberg\n"
"\N{COPYRIGHT SIGN} Copyright 2020 NanoVNA-Saver Authors"
"\N{COPYRIGHT SIGN} Copyright 2020ff NanoVNA-Saver Authors"
))
layout.addWidget(QtWidgets.QLabel(
"This program comes with ABSOLUTELY NO WARRANTY"))
@ -79,21 +80,6 @@ class AboutWindow(QtWidgets.QWidget):
btn_check_version.clicked.connect(self.findUpdates)
self.updateLabel = QtWidgets.QLabel("Last checked: ")
self.updateCheckBox = QtWidgets.QCheckBox(
"Check for updates on startup")
self.updateCheckBox.toggled.connect(self.updateSettings)
check_for_updates = self.app.settings.value(
"CheckForUpdates", "Ask")
if check_for_updates == "Yes":
self.updateCheckBox.setChecked(True)
self.findUpdates(automatic=True)
elif check_for_updates == "No":
self.updateCheckBox.setChecked(False)
else:
logger.debug("Starting timer")
QtCore.QTimer.singleShot(2000, self.askAboutUpdates)
update_hbox = QtWidgets.QHBoxLayout()
update_hbox.addWidget(btn_check_version)
@ -101,7 +87,6 @@ class AboutWindow(QtWidgets.QWidget):
update_hbox.addLayout(update_form)
update_hbox.addStretch()
update_form.addRow(self.updateLabel)
update_form.addRow(self.updateCheckBox)
layout.addLayout(update_hbox)
layout.addStretch()
@ -115,39 +100,10 @@ class AboutWindow(QtWidgets.QWidget):
self.updateLabels()
def updateLabels(self):
try:
with contextlib.suppress(IOError, AttributeError):
self.versionLabel.setText(
f"NanoVNA Firmware Version: {self.app.vna.name} "
f"v{self.app.vna.version}")
except (IOError, AttributeError):
pass
def updateSettings(self):
if self.updateCheckBox.isChecked():
self.app.settings.setValue("CheckForUpdates", "Yes")
else:
self.app.settings.setValue("CheckForUpdates", "No")
def askAboutUpdates(self):
logger.debug("Asking about automatic update checks")
selection = QtWidgets.QMessageBox.question(
self.app,
"Enable checking for updates?",
"Would you like NanoVNA-Saver to"
" check for updates automatically?")
if selection == QtWidgets.QMessageBox.Yes:
self.updateCheckBox.setChecked(True)
self.app.settings.setValue("CheckForUpdates", "Yes")
self.findUpdates()
elif selection == QtWidgets.QMessageBox.No:
self.updateCheckBox.setChecked(False)
self.app.settings.setValue("CheckForUpdates", "No")
QtWidgets.QMessageBox.information(
self.app,
"Checking for updates disabled",
'You can check for updates using the "About" window.')
else:
self.app.settings.setValue("CheckForUpdates", "Ask")
def findUpdates(self, automatic=False):
latest_version = Version()

Wyświetl plik

@ -2,7 +2,7 @@
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -21,8 +21,9 @@ from typing import List
from PyQt5 import QtWidgets, QtCore, QtGui
from NanoVNASaver import Defaults
from NanoVNASaver.Charts.Chart import (
Chart, ChartColors, ChartMarker, ChartMarkerConfig)
Chart, ChartColors)
from NanoVNASaver.Windows.Bands import BandsWindow
from NanoVNASaver.Windows.MarkerSettings import MarkerSettingsWindow
from NanoVNASaver.Marker import Marker
@ -37,7 +38,6 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.app = app
self.setWindowTitle("Display settings")
self.setWindowIcon(self.app.icon)
self.marker_cfg = ChartMarkerConfig()
self.marker_window = MarkerSettingsWindow(self.app)
self.callback_params = {}
@ -61,10 +61,10 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
display_options_layout.addRow("Return loss is:", self.returnloss_is_negative)
display_options_layout.addRow("", self.returnloss_is_positive)
if self.app.settings.value("ReturnLossPositive", False, bool):
self.returnloss_is_positive.setChecked(True)
else:
self.returnloss_is_negative.setChecked(True)
self.returnloss_is_positive.setChecked(
Defaults.cfg.chart.returnloss_is_positive)
self.returnloss_is_negative.setChecked(
not Defaults.cfg.chart.returnloss_is_positive)
self.returnloss_is_positive.toggled.connect(self.changeReturnLoss)
self.changeReturnLoss()
@ -83,7 +83,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.pointSizeInput = QtWidgets.QSpinBox()
self.pointSizeInput.setMinimumHeight(20)
pointsize = self.app.settings.value("PointSize", 2, int)
pointsize = Defaults.cfg.chart.point_size
self.pointSizeInput.setValue(pointsize)
self.changePointSize(pointsize)
self.pointSizeInput.setMinimum(1)
@ -95,7 +95,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.lineThicknessInput = QtWidgets.QSpinBox()
self.lineThicknessInput.setMinimumHeight(20)
linethickness = self.app.settings.value("LineThickness", 1, int)
linethickness = Defaults.cfg.chart.line_thickness
self.lineThicknessInput.setValue(linethickness)
self.changeLineThickness(linethickness)
self.lineThicknessInput.setMinimum(1)
@ -107,10 +107,9 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.markerSizeInput = QtWidgets.QSpinBox()
self.markerSizeInput.setMinimumHeight(20)
markersize = self.app.settings.value("MarkerSize", 6, int)
markersize = Defaults.cfg.chart.marker_size
self.markerSizeInput.setValue(markersize)
self.markerSizeInput.setMinimum(4)
self.markerSizeInput.setMinimum(4)
self.markerSizeInput.setMaximum(20)
self.markerSizeInput.setSingleStep(2)
self.markerSizeInput.setSuffix(" px")
@ -137,10 +136,8 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
display_options_layout.addRow("Data point is:", self.marker_at_center)
display_options_layout.addRow("", self.marker_at_tip)
if self.app.settings.value("MarkerAtTip", False, bool):
self.marker_at_tip.setChecked(True)
else:
self.marker_at_center.setChecked(True)
self.marker_at_tip.setChecked(Defaults.cfg.chart.marker_at_tip)
self.marker_at_center.setChecked(not Defaults.cfg.chart.marker_at_tip)
self.marker_at_tip.toggled.connect(self.changeMarkerAtTip)
self.changeMarkerAtTip()
@ -162,10 +159,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.font_dropdown = QtWidgets.QComboBox()
self.font_dropdown.setMinimumHeight(20)
self.font_dropdown.addItems(["7", "8", "9", "10", "11", "12"])
font_size = self.app.settings.value("FontSize",
defaultValue="8",
type=str)
self.font_dropdown.setCurrentText(font_size)
self.font_dropdown.setCurrentText(str(Defaults.cfg.gui.font_size))
self.changeFont()
self.font_dropdown.currentTextChanged.connect(self.changeFont)
@ -196,11 +190,8 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.vswrMarkers: List[float] = self.app.settings.value("VSWRMarkers", [], float)
if isinstance(self.vswrMarkers, float):
if self.vswrMarkers == 0:
self.vswrMarkers = []
else:
# Single values from the .ini become floats rather than lists. Convert them.
self.vswrMarkers = [self.vswrMarkers]
# Single values from the .ini become floats rather than lists. Convert them.
self.vswrMarkers = [] if self.vswrMarkers == 0.0 else [self.vswrMarkers]
vswr_marker_layout.addRow(
"VSWR Markers",self.color_picker("VSWRColor", "swr"))
@ -209,7 +200,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
self.vswr_marker_dropdown.setMinimumHeight(20)
vswr_marker_layout.addRow(self.vswr_marker_dropdown)
if len(self.vswrMarkers) == 0:
if not self.vswrMarkers:
self.vswr_marker_dropdown.addItem("None")
else:
for m in self.vswrMarkers:
@ -262,10 +253,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
# "S21 Phase",
# "None"]
selections = []
for c in self.app.selectable_charts:
selections.append(c.name)
selections = [c.name for c in self.app.selectable_charts]
selections.append("None")
chart00_selection = QtWidgets.QComboBox()
@ -364,14 +352,11 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
"VSWRColor", defaultValue=ChartColors.swr,
type=QtGui.QColor)
self.dark_mode_option.setChecked(
self.app.settings.value("DarkMode", False, bool))
self.show_lines_option.setChecked(
self.app.settings.value("ShowLines", False, bool))
self.dark_mode_option.setChecked(Defaults.cfg.gui.dark_mode)
self.show_lines_option.setChecked(Defaults.cfg.chart.show_lines)
self.show_marker_number_option.setChecked(
self.app.settings.value("ShowMarkerNumbers", ChartMarkerConfig.draw_label, bool))
self.filled_marker_option.setChecked(
self.app.settings.value("FilledMarkers", ChartMarkerConfig.fill, bool))
Defaults.cfg.chart.marker_label)
self.filled_marker_option.setChecked(Defaults.cfg.chart.marker_filled)
if self.app.settings.value("UseCustomColors",
defaultValue=False, type=bool):
@ -450,8 +435,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
def changeReturnLoss(self):
state = self.returnloss_is_positive.isChecked()
self.app.settings.setValue("ReturnLossPositive", state)
Defaults.cfg.chart.returnloss_is_positive = bool(state)
for m in self.app.markers:
m.returnloss_is_positive = state
m.updateLabels(self.app.data.s11, self.app.data.s21)
@ -462,56 +446,51 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
def changeShowLines(self):
state = self.show_lines_option.isChecked()
self.app.settings.setValue("ShowLines", state)
Defaults.cfg.chart.show_lines = bool(state)
for c in self.app.subscribing_charts:
c.setDrawLines(state)
def changeShowMarkerNumber(self):
state = self.show_marker_number_option.isChecked()
self.app.settings.setValue("ShowMarkerNumbers", state)
ChartMarker.cfg.draw_label = state
Defaults.cfg.chart.marker_label = bool(
self.show_marker_number_option.isChecked())
self.updateCharts()
def changeFilledMarkers(self):
state = self.filled_marker_option.isChecked()
self.app.settings.setValue("FilledMarkers", state)
ChartMarker.cfg.fill = state
Defaults.cfg.chart.marker_filled = bool(
self.filled_marker_option.isChecked())
self.updateCharts()
def changeMarkerAtTip(self):
state = self.marker_at_tip.isChecked()
self.app.settings.setValue("MarkerAtTip", state)
ChartMarker.cfg.at_tip = state
Defaults.cfg.chart.marker_at_tip = bool(
self.marker_at_tip.isChecked())
self.updateCharts()
def changePointSize(self, size: int):
self.app.settings.setValue("PointSize", size)
Defaults.cfg.chart.point_size = size
for c in self.app.subscribing_charts:
c.setPointSize(size)
def changeLineThickness(self, size: int):
self.app.settings.setValue("LineThickness", size)
Defaults.cfg.chart.line_thickness = int(size)
for c in self.app.subscribing_charts:
c.setLineThickness(size)
def changeMarkerSize(self, size: int):
self.app.settings.setValue("MarkerSize", size)
ChartMarker.cfg.size = size
Defaults.cfg.chart.marker_size = size
self.markerSizeInput.setValue(size)
self.updateCharts()
def changeDarkMode(self):
state = self.dark_mode_option.isChecked()
self.app.settings.setValue("DarkMode", state)
Defaults.cfg.gui.dark_mode = bool(state)
Chart.color.foreground = QtGui.QColor(QtCore.Qt.lightGray)
if state:
Chart.color.background = QtGui.QColor(QtCore.Qt.black)
Chart.color.text = QtGui.QColor(QtCore.Qt.white)
Chart.color.swr = Chart.color.swr
else:
Chart.color.background = QtGui.QColor(QtCore.Qt.white)
Chart.color.text = QtGui.QColor(QtCore.Qt.black)
Chart.color.swr = Chart.color.swr
Chart.color.swr = Chart.color.swr
self.updateCharts()
def changeSetting(self, setting: str, value: str):
@ -547,11 +526,11 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
c.update()
def changeFont(self):
font_size = self.font_dropdown.currentText()
self.app.settings.setValue("FontSize", font_size)
font_size = int(self.font_dropdown.currentText())
Defaults.cfg.gui.font_size = font_size
app: QtWidgets.QApplication = QtWidgets.QApplication.instance()
font = app.font()
font.setPointSize(int(font_size))
font.setPointSize(font_size)
app.setFont(font)
self.app.changeFont(font)
@ -628,3 +607,4 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
def updateCharts(self):
for c in self.app.subscribing_charts:
c.update()
Defaults.store(self.app.settings, Defaults.cfg)

2
debug.sh 100755
Wyświetl plik

@ -0,0 +1,2 @@
#!/bin/sh
exec python -m debugpy --listen 5678 --wait-for-client $@

Wyświetl plik

@ -15,10 +15,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
try:
from contextlib import suppress
with suppress(ImportError):
# pylint: disable=no-name-in-module,import-error,unused-import
# pyright: reportMissingImports=false
import pkg_resources.py2_warn
except ImportError:
pass
from NanoVNASaver.__main__ import main
if __name__ == '__main__':

Wyświetl plik

@ -0,0 +1,88 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import unittest
from dataclasses import dataclass, field
import NanoVNASaver.Defaults as CFG
@dataclass
class TConfig:
my_int: int = 3
my_float: float = 3.14
my_str: str = "Hello World"
my_bool: bool = True
my_list: list = field(default_factory=lambda: [1, 2, 3])
my_bytearray: bytearray = field(default_factory=lambda: bytearray((1,2,3)))
class TestCases(unittest.TestCase):
def setUp(self) -> None:
self.settings_1 = CFG.AppSettings(
CFG.QSettings.IniFormat,
CFG.QSettings.UserScope,
"NanoVNASaver", "Test_1")
self.settings_2 = CFG.AppSettings(
CFG.QSettings.IniFormat,
CFG.QSettings.UserScope,
"NanoVNASaver", "Test_2")
self.config_1 = TConfig()
self.config_2 = TConfig(
my_int=4,
my_float=3.0,
my_str="Goodbye World",
my_bool=False,
my_list=[4, 5, 6])
def test_store_dataclass(self):
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, my_str="Goodbye World",
my_bool="False", my_list=(4, 5, 6))
with self.assertRaises(AssertionError):
self.settings_1.store_dataclass("SectionX", illegal_config)
def test_restore_dataclass(self):
tc_1 = self.settings_1.restore_dataclass("Section1", TConfig())
tc_2 = self.settings_1.restore_dataclass("Section2", TConfig())
self.assertNotEqual(tc_1, tc_2)
self.assertEqual(tc_1, self.config_1)
self.assertEqual(tc_2, self.config_2)
self.assertEqual(tc_2.my_int, 4)
self.assertEqual(tc_2.my_float, 3.0)
self.assertEqual(tc_2.my_str, "Goodbye World")
self.assertEqual(tc_2.my_bool, False)
self.assertEqual(tc_2.my_list, [4, 5, 6])
self.assertIsInstance(tc_2.my_int, int)
self.assertIsInstance(tc_2.my_float, float)
def test_restore_empty(self):
tc_3 = self.settings_1.restore_dataclass("Section3", TConfig())
self.assertEqual(tc_3, TConfig())
def test_store(self):
tc_1 = CFG.CFG()
tc_1.gui.dark_mode = not tc_1.gui.dark_mode
CFG.store(self.settings_2, tc_1)
tc_2 = CFG.restore(self.settings_2)
print(f"\n{tc_1}\n{tc_2}\n")
self.assertEqual(tc_1, tc_2)
self.assertNotEqual(tc_2.gui, CFG.GUI())