From 5b21315a1196444d2c853dcb4dd9c1690b395165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Mon, 13 Mar 2023 12:08:33 +0100 Subject: [PATCH] PyQt6 updates --- README.rst | 11 +- debug.sh | 1 + .../Analysis/ResonanceAnalysis.py | 2 - src/NanoVNASaver/Charts/Permeability.py | 4 +- src/NanoVNASaver/Charts/Phase.py | 10 +- src/NanoVNASaver/Charts/TDR.py | 110 +++++++++--------- src/NanoVNASaver/Marker/Values.py | 4 +- src/NanoVNASaver/Marker/Widget.py | 5 +- src/NanoVNASaver/__init__.py | 17 +-- 9 files changed, 80 insertions(+), 84 deletions(-) diff --git a/README.rst b/README.rst index db411cb..fef29b6 100644 --- a/README.rst +++ b/README.rst @@ -39,7 +39,8 @@ points, and generally display and analyze the resulting data. * Copyright 2019, 2020 Rune B. Broberg * Copyright 2020ff NanoVNA-Saver Authors -It's written in **Python 3** using **PyQt6** and **scipy**. +It's developed in **Python 3 (>=3.8)** using **PyQt6**, **numpy** and +**scipy**. Introduction @@ -88,8 +89,6 @@ Screenshot Running the application ----------------------- -The software was written in Python on Windows, using Pycharm, and the modules -PyQT5, numpy, scipy and pyserial. Main development is currently done on Linux (Mint 21 "Vanessa" Cinnamon) Installation @@ -187,10 +186,13 @@ for the plot (right click on the plot). Latest Changes ^^^^^^^^^^^^^^ +* Using PyQt6 +* Moved to PyScaffold project structure + + Changes in 0.5.5 ^^^^^^^^^^^^^^^^ - * Measuring inductor core permeability * Bugfixes for calibration data loading and saving * Let V2 Devices more time for usb-serial setup @@ -199,7 +201,6 @@ Changes in 0.5.5 Changes in 0.5.4 ^^^^^^^^^^^^^^^^ - * Bugfixes for Python3.11 compatability * Bugfix for Python3.8 compatability * use math instead of table for log step calculation diff --git a/debug.sh b/debug.sh index 9385eb2..0fc802e 100755 --- a/debug.sh +++ b/debug.sh @@ -1,2 +1,3 @@ #!/bin/sh +export PYTHONPATH="src" exec python -m debugpy --listen 5678 --wait-for-client $@ diff --git a/src/NanoVNASaver/Analysis/ResonanceAnalysis.py b/src/NanoVNASaver/Analysis/ResonanceAnalysis.py index d078a5f..4a6068d 100644 --- a/src/NanoVNASaver/Analysis/ResonanceAnalysis.py +++ b/src/NanoVNASaver/Analysis/ResonanceAnalysis.py @@ -102,8 +102,6 @@ class ResonanceAnalysis(Analysis): self.layout.addRow(QtWidgets.QLabel("No resonance found")) return - self - self.do_resonance_analysis() def do_resonance_analysis(self): diff --git a/src/NanoVNASaver/Charts/Permeability.py b/src/NanoVNASaver/Charts/Permeability.py index 4b02858..aadda3e 100644 --- a/src/NanoVNASaver/Charts/Permeability.py +++ b/src/NanoVNASaver/Charts/Permeability.py @@ -144,7 +144,7 @@ class PermeabilityChart(FrequencyChart): primary_pen = pen secondary_pen = QtGui.QPen(Chart.color.sweep_secondary) - if len(self.data) > 0: + if self.data: c = QtGui.QColor(Chart.color.sweep) c.setAlpha(255) pen = QtGui.QPen(c) @@ -215,7 +215,7 @@ class PermeabilityChart(FrequencyChart): line_pen.setColor(Chart.color.reference) secondary_pen.setColor(Chart.color.reference_secondary) qp.setPen(primary_pen) - if len(self.reference) > 0: + if self.reference: c = QtGui.QColor(Chart.color.reference) c.setAlpha(255) pen = QtGui.QPen(c) diff --git a/src/NanoVNASaver/Charts/Phase.py b/src/NanoVNASaver/Charts/Phase.py index 18676c3..d743670 100644 --- a/src/NanoVNASaver/Charts/Phase.py +++ b/src/NanoVNASaver/Charts/Phase.py @@ -22,7 +22,7 @@ import logging from typing import List import numpy as np -from PyQt6 import QtWidgets, QtGui +from PyQt6.QtGui import QAction, QPainter, QPen from NanoVNASaver.RFTools import Datapoint from NanoVNASaver.Charts.Chart import Chart @@ -47,7 +47,7 @@ class PhaseChart(FrequencyChart): self.maxDisplayValue = 180 self.y_menu.addSeparator() - self.action_unwrap = QtGui.QAction("Unwrap") + self.action_unwrap = QAction("Unwrap") self.action_unwrap.setCheckable(True) self.action_unwrap.triggered.connect( lambda: self.setUnwrap(self.action_unwrap.isChecked()) @@ -64,7 +64,7 @@ class PhaseChart(FrequencyChart): self.unwrap = unwrap self.update() - def drawValues(self, qp: QtGui.QPainter): + def drawValues(self, qp: QPainter): if len(self.data) == 0 and len(self.reference) == 0: return @@ -102,7 +102,7 @@ class PhaseChart(FrequencyChart): (self.maxAngle - angle) / self.span * self.dim.height ) if angle not in [minAngle, maxAngle]: - qp.setPen(QtGui.QPen(Chart.color.text)) + qp.setPen(QPen(Chart.color.text)) if angle != 0: digits = max( 0, min(2, math.floor(3 - math.log10(abs(angle)))) @@ -115,7 +115,7 @@ class PhaseChart(FrequencyChart): else: anglestr = "0" qp.drawText(3, y + 3, f"{anglestr}°") - qp.setPen(QtGui.QPen(Chart.color.foreground)) + qp.setPen(QPen(Chart.color.foreground)) qp.drawLine( self.leftMargin - 5, y, self.leftMargin + self.dim.width, y ) diff --git a/src/NanoVNASaver/Charts/TDR.py b/src/NanoVNASaver/Charts/TDR.py index f66abe9..d7878e2 100644 --- a/src/NanoVNASaver/Charts/TDR.py +++ b/src/NanoVNASaver/Charts/TDR.py @@ -20,8 +20,18 @@ import math import logging import numpy as np -from PyQt6 import QtWidgets, QtGui, QtCore -from PyQt6.QtCore import Qt +from PyQt6.QtCore import QPoint, QRect, Qt +from PyQt6.QtGui import ( + QAction, + QActionGroup, + QMouseEvent, + QPalette, + QPainter, + QPaintEvent, + QPen, + QResizeEvent, +) +from PyQt6.QtWidgets import QInputDialog, QMenu, QSizePolicy from NanoVNASaver.Charts.Chart import Chart @@ -48,32 +58,32 @@ class TDRChart(Chart): self.setMinimumSize(300, 300) self.setSizePolicy( - QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Policy.MinimumExpanding, - QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QSizePolicy( + QSizePolicy.Policy.MinimumExpanding, + QSizePolicy.Policy.MinimumExpanding, ) ) - pal = QtGui.QPalette() - pal.setColor(QtGui.QPalette.ColorRole.Window, Chart.color.background) + pal = QPalette() + pal.setColor(QPalette.ColorRole.Window, Chart.color.background) self.setPalette(pal) self.setAutoFillBackground(True) self.setContextMenuPolicy(Qt.ContextMenuPolicy.DefaultContextMenu) - self.menu = QtWidgets.QMenu() + self.menu = QMenu() - self.reset = QtGui.QAction("Reset") + self.reset = QAction("Reset") self.reset.triggered.connect(self.resetDisplayLimits) self.menu.addAction(self.reset) - self.x_menu = QtWidgets.QMenu("Length axis") - self.mode_group = QtGui.QActionGroup(self.x_menu) - self.action_automatic = QtGui.QAction("Automatic") + self.x_menu = QMenu("Length axis") + self.mode_group = QActionGroup(self.x_menu) + self.action_automatic = QAction("Automatic") self.action_automatic.setCheckable(True) self.action_automatic.setChecked(True) self.action_automatic.changed.connect( lambda: self.setFixedSpan(self.action_fixed_span.isChecked()) ) - self.action_fixed_span = QtGui.QAction("Fixed span") + self.action_fixed_span = QAction("Fixed span") self.action_fixed_span.setCheckable(True) self.action_fixed_span.changed.connect( lambda: self.setFixedSpan(self.action_fixed_span.isChecked()) @@ -84,28 +94,26 @@ class TDRChart(Chart): self.x_menu.addAction(self.action_fixed_span) self.x_menu.addSeparator() - self.action_set_fixed_start = QtGui.QAction( + self.action_set_fixed_start = QAction( f"Start ({self.minDisplayLength})" ) self.action_set_fixed_start.triggered.connect(self.setMinimumLength) - self.action_set_fixed_stop = QtGui.QAction( - f"Stop ({self.maxDisplayLength})" - ) + self.action_set_fixed_stop = QAction(f"Stop ({self.maxDisplayLength})") self.action_set_fixed_stop.triggered.connect(self.setMaximumLength) self.x_menu.addAction(self.action_set_fixed_start) self.x_menu.addAction(self.action_set_fixed_stop) - self.y_menu = QtWidgets.QMenu("Impedance axis") - self.y_mode_group = QtGui.QActionGroup(self.y_menu) - self.y_action_automatic = QtGui.QAction("Automatic") + self.y_menu = QMenu("Impedance axis") + self.y_mode_group = QActionGroup(self.y_menu) + self.y_action_automatic = QAction("Automatic") self.y_action_automatic.setCheckable(True) self.y_action_automatic.setChecked(True) self.y_action_automatic.changed.connect( lambda: self.setFixedValues(self.y_action_fixed.isChecked()) ) - self.y_action_fixed = QtGui.QAction("Fixed") + self.y_action_fixed = QAction("Fixed") self.y_action_fixed.setCheckable(True) self.y_action_fixed.changed.connect( lambda: self.setFixedValues(self.y_action_fixed.isChecked()) @@ -116,14 +124,14 @@ class TDRChart(Chart): self.y_menu.addAction(self.y_action_fixed) self.y_menu.addSeparator() - self.y_action_set_fixed_maximum = QtGui.QAction( + self.y_action_set_fixed_maximum = QAction( f"Maximum ({self.maxImpedance})" ) self.y_action_set_fixed_maximum.triggered.connect( self.setMaximumImpedance ) - self.y_action_set_fixed_minimum = QtGui.QAction( + self.y_action_set_fixed_minimum = QAction( f"Minimum ({self.minImpedance})" ) self.y_action_set_fixed_minimum.triggered.connect( @@ -137,7 +145,7 @@ class TDRChart(Chart): self.menu.addMenu(self.y_menu) self.menu.addSeparator() self.menu.addAction(self.action_save_screenshot) - self.action_popout = QtGui.QAction("Popout chart") + self.action_popout = QAction("Popout chart") self.action_popout.triggered.connect( lambda: self.popoutRequested.emit(self) ) @@ -177,7 +185,7 @@ class TDRChart(Chart): self.update() def setMinimumLength(self): - min_val, selected = QtWidgets.QInputDialog.getDouble( + min_val, selected = QInputDialog.getDouble( self, "Start length (m)", "Set start length (m)", @@ -193,7 +201,7 @@ class TDRChart(Chart): self.update() def setMaximumLength(self): - max_val, selected = QtWidgets.QInputDialog.getDouble( + max_val, selected = QInputDialog.getDouble( self, "Stop length (m)", "Set stop length (m)", @@ -213,7 +221,7 @@ class TDRChart(Chart): self.update() def setMinimumImpedance(self): - min_val, selected = QtWidgets.QInputDialog.getDouble( + min_val, selected = QInputDialog.getDouble( self, "Minimum impedance (\N{OHM SIGN})", "Set minimum impedance (\N{OHM SIGN})", @@ -229,7 +237,7 @@ class TDRChart(Chart): self.update() def setMaximumImpedance(self): - max_val, selected = QtWidgets.QInputDialog.getDouble( + max_val, selected = QInputDialog.getDouble( self, "Maximum impedance (\N{OHM SIGN})", "Set maximum impedance (\N{OHM SIGN})", @@ -256,7 +264,7 @@ class TDRChart(Chart): self.tdrWindow.updated.connect(new_chart.update) return new_chart - def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: + def mouseMoveEvent(self, a0: QMouseEvent) -> None: if a0.buttons() == Qt.MouseButton.RightButton: a0.ignore() return @@ -310,12 +318,12 @@ class TDRChart(Chart): def _draw_ticks(self, height, width, x_step, min_index): ticks = (self.width() - self.leftMargin) // 100 - qp = QtGui.QPainter(self) + qp = QPainter(self) for i in range(ticks): x = self.leftMargin + round((i + 1) * width / ticks) - qp.setPen(QtGui.QPen(Chart.color.foreground)) + qp.setPen(QPen(Chart.color.foreground)) qp.drawLine(x, self.topMargin, x, self.topMargin + height) - qp.setPen(QtGui.QPen(Chart.color.text)) + qp.setPen(QPen(Chart.color.text)) distance = ( self.tdrWindow.distance_axis[ min_index + int((x - self.leftMargin) * x_step) - 1 @@ -325,15 +333,15 @@ class TDRChart(Chart): qp.drawText( x - 15, self.topMargin + height + 15, f"{round(distance, 1)}m" ) - qp.setPen(QtGui.QPen(Chart.color.text)) + qp.setPen(QPen(Chart.color.text)) qp.drawText( self.leftMargin - 10, self.topMargin + height + 15, - str(round(self.tdrWindow.distance_axis[min_index] / 2, 1)) + "m", + f"{str(round(self.tdrWindow.distance_axis[min_index] / 2, 1))}m", ) def _draw_y_ticks(self, height, width, min_impedance, max_impedance): - qp = QtGui.QPainter(self) + qp = QPainter(self) y_step = (max_impedance - min_impedance) / height y_ticks = math.floor(height / 60) y_tick_step = height / y_ticks @@ -350,10 +358,10 @@ class TDRChart(Chart): ) def _draw_max_point(self, height, x_step, y_step, min_index): - qp = QtGui.QPainter(self) + qp = QPainter(self) id_max = np.argmax(self.tdrWindow.td) - max_point = QtCore.QPoint( + max_point = QPoint( self.leftMargin + int((id_max - min_index) / x_step), (self.topMargin + height) - int(self.tdrWindow.td[id_max] / y_step), ) @@ -368,8 +376,8 @@ class TDRChart(Chart): ) def _draw_marker(self, height, x_step, y_step, min_index): - qp = QtGui.QPainter(self) - marker_point = QtCore.QPoint( + qp = QPainter(self) + marker_point = QPoint( self.leftMargin + int((self.markerLocation - min_index) / x_step), (self.topMargin + height) - int(self.tdrWindow.td[self.markerLocation] / y_step), @@ -415,8 +423,8 @@ class TDRChart(Chart): self._draw_ticks(height, width, x_step, min_index) self._draw_y_ticks(height, width, min_impedance, max_impedance) - qp = QtGui.QPainter(self) - pen = QtGui.QPen(Chart.color.sweep) + qp = QPainter(self) + pen = QPen(Chart.color.sweep) pen.setWidth(self.dim.point) qp.setPen(pen) @@ -443,15 +451,15 @@ class TDRChart(Chart): if self.markerLocation != -1: self._draw_marker(height, x_step, y_step, min_index) - def paintEvent(self, _: QtGui.QPaintEvent) -> None: - qp = QtGui.QPainter(self) - qp.setPen(QtGui.QPen(Chart.color.text)) + def paintEvent(self, _: QPaintEvent) -> None: + qp = QPainter(self) + qp.setPen(QPen(Chart.color.text)) qp.drawText(3, 15, self.name) width = self.width() - self.leftMargin - self.rightMargin height = self.height() - self.bottomMargin - self.topMargin - qp.setPen(QtGui.QPen(Chart.color.foreground)) + qp.setPen(QPen(Chart.color.foreground)) qp.drawLine( self.leftMargin - 5, self.height() - self.bottomMargin, @@ -471,14 +479,12 @@ class TDRChart(Chart): self._draw_graph(height, width) if self.dragbox.state and self.dragbox.pos[0] != -1: - dashed_pen = QtGui.QPen( - Chart.color.foreground, 1, QtCore.Qt.DashLine - ) + dashed_pen = QPen(Chart.color.foreground, 1, Qt.DashLine) qp.setPen(dashed_pen) qp.drawRect( - QtCore.QRect( - QtCore.QPoint(*self.dragbox.pos_start), - QtCore.QPoint(*self.dragbox.pos), + QRect( + QPoint(*self.dragbox.pos_start), + QPoint(*self.dragbox.pos), ) ) @@ -548,7 +554,7 @@ class TDRChart(Chart): self.update() - def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: + def resizeEvent(self, a0: QResizeEvent) -> None: super().resizeEvent(a0) self.dim.width = self.width() - self.leftMargin - self.rightMargin self.dim.height = self.height() - self.bottomMargin - self.topMargin diff --git a/src/NanoVNASaver/Marker/Values.py b/src/NanoVNASaver/Marker/Values.py index 97b0a81..81d96c2 100644 --- a/src/NanoVNASaver/Marker/Values.py +++ b/src/NanoVNASaver/Marker/Values.py @@ -101,6 +101,6 @@ class Value: ] self.freq = s11[1].freq - self.s11 = s11[index - 1 : index + 2] + self.s11 = s11[index - 1: index + 2] if s21: - self.s21 = s21[index - 1 : index + 2] + self.s21 = s21[index - 1: index + 2] diff --git a/src/NanoVNASaver/Marker/Widget.py b/src/NanoVNASaver/Marker/Widget.py index af518c3..44a0696 100644 --- a/src/NanoVNASaver/Marker/Widget.py +++ b/src/NanoVNASaver/Marker/Widget.py @@ -232,11 +232,11 @@ class Marker(QtCore.QObject, Value): if color.isValid(): self.color = color p = self.btnColorPicker.palette() - # TODO: p.setColor(QtGui.QPalette.ButtonText, self.color) + p.setColor(QtGui.QPalette.ColorRole.ButtonText, self.color) self.btnColorPicker.setPalette(p) + # TODO: fix Stylesheet if self.coloredText: color_string = QtCore.QVariant(color) - # TODO: color_string.convert(str) self.group_box.setStyleSheet( f"QGroupBox {{ color: {color_string.value()}; " f"font-size: {self._size_str()}}};" @@ -377,7 +377,6 @@ class Marker(QtCore.QObject, Value): self.label["s21polar"].setText( f"{str(round(abs(_s21.z), 2))}∠{format_phase(_s21.phase)}" ) - self.label["s21magshunt"].setText( format_magnitude(abs(_s21.shuntImpedance())) ) diff --git a/src/NanoVNASaver/__init__.py b/src/NanoVNASaver/__init__.py index 73f2a09..8c289c0 100644 --- a/src/NanoVNASaver/__init__.py +++ b/src/NanoVNASaver/__init__.py @@ -1,16 +1,7 @@ -import sys - -if sys.version_info[:2] >= (3, 8): - # TODO: Import directly (no need for conditional) when `python_requires = >= 3.8` - from importlib.metadata import ( - PackageNotFoundError, - version, - ) # pragma: no cover -else: - from importlib_metadata import ( - PackageNotFoundError, - version, - ) # pragma: no cover +from importlib.metadata import ( + PackageNotFoundError, + version, +) try: # Change here if project is renamed and does not equal the package name