From 2821f4d08cfbe78b9e49de5a374aac4fb2f3b085 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sat, 28 May 2022 21:48:13 +0200 Subject: [PATCH] enable to zoom out (not perfect yet) --- NanoVNASaver/Charts/Chart.py | 38 ++++++++++- NanoVNASaver/Charts/Frequency.py | 57 ++++------------ NanoVNASaver/Charts/TDR.py | 110 +++++++------------------------ 3 files changed, 73 insertions(+), 132 deletions(-) diff --git a/NanoVNASaver/Charts/Chart.py b/NanoVNASaver/Charts/Chart.py index 312176e..fc5e34a 100644 --- a/NanoVNASaver/Charts/Chart.py +++ b/NanoVNASaver/Charts/Chart.py @@ -219,6 +219,37 @@ class Chart(QtWidgets.QWidget): self.dragbox.pos_start = (0, 0) self.update() + + def wheelEvent(self, a0: QtGui.QWheelEvent) -> None: + delta = a0.angleDelta().y() + if not delta or (not self.data and not self.reference): + a0.ignore() + return + modifiers = a0.modifiers() + + zoom_x = modifiers != QtCore.Qt.ShiftModifier + zoom_y = modifiers != QtCore.Qt.ControlModifier + rate = -delta / 120 + # zooming in 10% increments and 9% complementary + divisor = 10 if delta > 0 else 9 + + factor_x = rate * self.dim.width / divisor if zoom_x else 0 + factor_y = rate * self.dim.height / divisor if zoom_y else 0 + + abs_x = max(0, a0.x() - self.leftMargin) + abs_y = max(0, a0.y() - self.topMargin) + + ratio_x = abs_x / self.dim.width + ratio_y = abs_y / self.dim.height + + self.zoomTo( + int(self.leftMargin + ratio_x * factor_x), + int(self.topMargin + ratio_y * factor_y), + int(self.leftMargin + self.dim.width - (1 - ratio_x) * factor_x), + int(self.topMargin + self.dim.height - (1 - ratio_y) * factor_y) + ) + a0.accept() + def zoomTo(self, x1, y1, x2, y2): raise NotImplementedError() @@ -266,9 +297,12 @@ class Chart(QtWidgets.QWidget): self.swrMarkers.clear() self.update() - def drawMarker(self, x, y, qp: QtGui.QPainter, color: QtGui.QColor, number=0): + @staticmethod + def drawMarker(x: int, y: int, + qp: QtGui.QPainter, color: QtGui.QColor, + number: int=0): cmarker = ChartMarker(qp) - cmarker.draw(x, y, color, str(number)) + cmarker.draw(x, y, color, f"{number}") def drawTitle(self, qp: QtGui.QPainter, position: QtCore.QPoint = None): qp.setPen(Chart.color.text) diff --git a/NanoVNASaver/Charts/Frequency.py b/NanoVNASaver/Charts/Frequency.py index 107c5f2..bc4d3c3 100644 --- a/NanoVNASaver/Charts/Frequency.py +++ b/NanoVNASaver/Charts/Frequency.py @@ -237,7 +237,8 @@ class FrequencyChart(Chart): self.logarithmicY = logarithmic and self.logarithmicYAllowed() self.update() - def logarithmicYAllowed(self) -> bool: + @staticmethod + def logarithmicYAllowed() -> bool: return False def setMinimumFrequency(self): @@ -380,38 +381,6 @@ class FrequencyChart(Chart): val = -1 * ((absy / self.dim.height * self.span) - self.maxValue) return [val * 10e11] - def wheelEvent(self, a0: QtGui.QWheelEvent) -> None: - if ((len(self.data) == 0 and len(self.reference) == 0) or - a0.angleDelta().y() == 0): - a0.ignore() - return - do_zoom_x = do_zoom_y = True - if a0.modifiers() == QtCore.Qt.ShiftModifier: - do_zoom_x = False - if a0.modifiers() == QtCore.Qt.ControlModifier: - do_zoom_y = False - self._wheel_zomm( - a0, do_zoom_x, do_zoom_y, - math.copysign(1, a0.angleDelta().y())) - - def _wheel_zomm(self, a0, do_zoom_x, do_zoom_y, sign: int=1): - # Zoom in - a0.accept() - # Center of zoom = a0.x(), a0.y() - # We zoom in by 1/10 of the width/height. - rate = sign * a0.angleDelta().y() / 120 - zoomx = rate * self.dim.width / 10 if do_zoom_x else 0 - zoomy = rate * self.dim.height / 10 if do_zoom_y else 0 - absx = max(0, a0.x() - self.leftMargin) - absy = max(0, a0.y() - self.topMargin) - ratiox = absx / self.dim.width - ratioy = absy / self.dim.height - p1x = int(self.leftMargin + ratiox * zoomx) - p1y = int(self.topMargin + ratioy * zoomy) - p2x = int(self.leftMargin + self.dim.width - (1 - ratiox) * zoomx) - p2y = int(self.topMargin + self.dim.height - (1 - ratioy) * zoomy) - self.zoomTo(p1x, p1y, p2x, p2y) - def zoomTo(self, x1, y1, x2, y2): val1 = self.valueAtPosition(y1) val2 = self.valueAtPosition(y2) @@ -481,14 +450,12 @@ class FrequencyChart(Chart): self.drawDragbog(qp) qp.end() + def _data_oob(self, data: list[Datapoint]) -> bool: + return (data[0].freq > self.fstop or self.data[-1].freq < self.fstart) + def _check_frequency_boundaries(self, qp: QtGui.QPainter): - if (len(self.data) > 0 and - (self.data[0].freq > self.fstop or - self.data[len(self.data) - 1].freq < self.fstart) - and - (len(self.reference) == 0 or - self.reference[0].freq > self.fstop or - self.reference[len(self.reference) - 1].freq < self.fstart)): + if (self.data and self._data_oob(self.data) and + (not self.reference or self._data_oob(self.reference))): # Data outside frequency range qp.setBackgroundMode(QtCore.Qt.OpaqueMode) qp.setBackground(Chart.color.background) @@ -707,16 +674,16 @@ class FrequencyChart(Chart): self.topMargin + self.dim.height]) else: return x, y + da = p2 - p1 db = p4 - p3 dp = p1 - p3 dap = np.array([-da[1], da[0]]) denom = np.dot(dap, db) - if denom != 0: - num = np.dot(dap, dp) - result = (num / denom.astype(float)) * db + p3 - return result[0], result[1] - return x, y + + return (((np.dot(dap, dp) / denom.astype(float)) * db + p3)[:2] + if denom + else (x, y)) def copy(self): new_chart = super().copy() diff --git a/NanoVNASaver/Charts/TDR.py b/NanoVNASaver/Charts/TDR.py index bf2a90c..3fabb82 100644 --- a/NanoVNASaver/Charts/TDR.py +++ b/NanoVNASaver/Charts/TDR.py @@ -290,7 +290,7 @@ class TDRChart(Chart): ticks = math.floor((self.width() - self.leftMargin) / 100) self.drawTitle(qp) - if len(self.tdrWindow.td) > 0: + if self.tdrWindow.td: if self.fixedSpan: max_length = max(0.1, self.maxDisplayLength) max_index = np.searchsorted(self.tdrWindow.distance_axis, max_length * 2) @@ -405,10 +405,12 @@ class TDRChart(Chart): if self.dragbox.state and self.dragbox.pos[0] != -1: dashed_pen = QtGui.QPen(Chart.color.foreground, 1, QtCore.Qt.DashLine) qp.setPen(dashed_pen) - top_left = QtCore.QPoint(self.dragbox.pos_start[0], self.dragbox.stateStart[1]) - bottom_right = QtCore.QPoint(self.dragbox.pos[0], self.dragbox.stateCurrent[1]) - rect = QtCore.QRect(top_left, bottom_right) - qp.drawRect(rect) + qp.drawRect( + QtCore.QRect( + QtCore.QPoint(*self.dragbox.pos_start), + QtCore.QPoint(*self.dragbox.pos) + ) + ) qp.end() @@ -431,24 +433,24 @@ class TDRChart(Chart): return 0 def lengthAtPosition(self, x, limit=True): - if len(self.tdrWindow.td) > 0: - width = self.width() - self.leftMargin - self.rightMargin - absx = x - self.leftMargin - if self.fixedSpan: - max_length = self.maxDisplayLength - min_length = self.minDisplayLength - x_step = (max_length - min_length) / width - else: - min_length = 0 - max_length = self.tdrWindow.distance_axis[ - math.ceil(len(self.tdrWindow.distance_axis) / 2)] / 2 - x_step = max_length / width - if limit and absx < 0: - return min_length - if limit and absx > width: - return max_length - return absx * x_step + min_length - return 0 + if not self.tdrWindow.td: + return 0 + width = self.width() - self.leftMargin - self.rightMargin + absx = x - self.leftMargin + if self.fixedSpan: + max_length = self.maxDisplayLength + min_length = self.minDisplayLength + x_step = (max_length - min_length) / width + else: + min_length = 0 + max_length = self.tdrWindow.distance_axis[ + math.ceil(len(self.tdrWindow.distance_axis) / 2)] / 2 + x_step = max_length / width + if limit and absx < 0: + return min_length + if limit and absx > width: + return max_length + return absx * x_step + min_length def zoomTo(self, x1, y1, x2, y2): logger.debug("Zoom to (x,y) by (x,y): (%d, %d) by (%d, %d)", x1, y1, x2, y2) @@ -470,68 +472,6 @@ class TDRChart(Chart): self.update() - def wheelEvent(self, a0: QtGui.QWheelEvent) -> None: - if len(self.tdrWindow.td) == 0: - a0.ignore() - return - chart_height = self.dim.height - chart_width = self.dim.width - do_zoom_x = do_zoom_y = True - if a0.modifiers() == QtCore.Qt.ShiftModifier: - do_zoom_x = False - if a0.modifiers() == QtCore.Qt.ControlModifier: - do_zoom_y = False - if a0.angleDelta().y() > 0: - # Zoom in - a0.accept() - # Center of zoom = a0.x(), a0.y() - # We zoom in by 1/10 of the width/height. - rate = a0.angleDelta().y() / 120 - if do_zoom_x: - zoomx = rate * chart_width / 10 - else: - zoomx = 0 - if do_zoom_y: - zoomy = rate * chart_height / 10 - else: - zoomy = 0 - absx = max(0, a0.x() - self.leftMargin) - absy = max(0, a0.y() - self.topMargin) - ratiox = absx/chart_width - ratioy = absy/chart_height - # TODO: Change zoom to center on the mouse if possible, - # or extend box to the side that has room if not. - p1x = int(self.leftMargin + ratiox * zoomx) - p1y = int(self.topMargin + ratioy * zoomy) - p2x = int(self.leftMargin + chart_width - (1 - ratiox) * zoomx) - p2y = int(self.topMargin + chart_height - (1 - ratioy) * zoomy) - self.zoomTo(p1x, p1y, p2x, p2y) - elif a0.angleDelta().y() < 0: - # Zoom out - a0.accept() - # Center of zoom = a0.x(), a0.y() - # We zoom out by 1/9 of the width/height, to match zoom in. - rate = -a0.angleDelta().y() / 120 - if do_zoom_x: - zoomx = rate * chart_width / 9 - else: - zoomx = 0 - if do_zoom_y: - zoomy = rate * chart_height / 9 - else: - zoomy = 0 - absx = max(0, a0.x() - self.leftMargin) - absy = max(0, a0.y() - self.topMargin) - ratiox = absx/chart_width - ratioy = absy/chart_height - p1x = int(self.leftMargin - ratiox * zoomx) - p1y = int(self.topMargin - ratioy * zoomy) - p2x = int(self.leftMargin + chart_width + (1 - ratiox) * zoomx) - p2y = int(self.topMargin + chart_height + (1 - ratioy) * zoomy) - self.zoomTo(p1x, p1y, p2x, p2y) - else: - a0.ignore() - def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: super().resizeEvent(a0) self.dim.width = self.width() - self.leftMargin - self.rightMargin