Share waveform data and plot navigation and retain processing results

This commit is contained in:
2026-09-27 02:47:24 +03:00
parent 513e79b127
commit caf30f2ed6
5 changed files with 534 additions and 31 deletions

View File

@@ -0,0 +1,112 @@
"""Common keyboard and wheel gestures for all plot canvases (Qt 5/6)."""
try:
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QScrollArea
except ImportError:
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QScrollArea
NAVIGATION_HINT = ('Колесо и +/− — масштаб X; Ctrl + колесо и Ctrl + +/− — масштаб Y. '
'ЛКМ на свободном поле — перемещение; ←/→ — перемещение по X; Home/End — начало/конец.')
class PlotNavigation:
def setup_navigation(self):
self.setFocusPolicy(Qt.StrongFocus)
self.setToolTip(NAVIGATION_HINT)
def keyPressEvent(self, event):
key = event.key()
modifiers = event.modifiers()
if modifiers & (Qt.AltModifier | Qt.MetaModifier):
return super().keyPressEvent(event)
if key in (Qt.Key_Plus, Qt.Key_Equal, Qt.Key_Minus):
self._navigation_zoom(1.25 if key != Qt.Key_Minus else .8,
bool(modifiers & Qt.ControlModifier))
elif key in (Qt.Key_Left, Qt.Key_Right) and not (modifiers & Qt.ControlModifier):
self._navigation_pan(-.1 if key == Qt.Key_Left else .1)
elif key in (Qt.Key_Home, Qt.Key_End):
self._navigation_edge(key == Qt.Key_End)
else:
return super().keyPressEvent(event)
event.accept()
def wheelEvent(self, event):
position = event.position() if hasattr(event, 'position') else event.posF()
rect = self.plot_rect() if hasattr(self, 'plot_rect') else self.rect()
if not rect.contains(position.toPoint()):
event.ignore()
return
delta = event.angleDelta()
ticks = delta.y() or delta.x()
if not ticks:
pixels = event.pixelDelta()
ticks = (pixels.y() or pixels.x()) * 2
if ticks:
self.setFocus(Qt.MouseFocusReason)
self._navigation_zoom(1.25 ** max(-8, min(8, ticks / 120)),
bool(event.modifiers() & Qt.ControlModifier), position)
event.accept()
class ScrollPlotNavigation(PlotNavigation):
"""Canvas navigation for waveforms whose viewport is a QScrollArea."""
def scroll_area(self):
parent = self.parentWidget()
while parent is not None and not isinstance(parent, QScrollArea):
parent = parent.parentWidget()
return parent
def _navigation_zoom(self, factor, vertical, position=None):
scroll = self.scroll_area()
size = self.height() if vertical else self.width()
if position is not None:
anchor = position.y() if vertical else position.x()
elif scroll is not None:
bar = scroll.verticalScrollBar() if vertical else scroll.horizontalScrollBar()
anchor = bar.value() + bar.pageStep() / 2
else:
anchor = size / 2
self.zoom_dragged.emit('y' if vertical else 'x', factor, anchor / max(1, size))
def _navigation_pan(self, fraction):
scroll = self.scroll_area()
if scroll is not None:
bar = scroll.horizontalScrollBar()
bar.setValue(round(bar.value() + fraction * bar.pageStep()))
self.navigation_panned()
def _navigation_edge(self, end):
scroll = self.scroll_area()
if scroll is not None:
bar = scroll.horizontalScrollBar()
bar.setValue(bar.maximum() if end else bar.minimum())
self.navigation_panned()
def navigation_panned(self):
pass
def begin_pan(self, event):
self.setFocus(Qt.MouseFocusReason)
self._pan_position = event.globalPosition() if hasattr(event, 'globalPosition') else event.globalPos()
self.setCursor(Qt.ClosedHandCursor)
def move_pan(self, event):
previous = getattr(self, '_pan_position', None)
if previous is None:
return
position = event.globalPosition() if hasattr(event, 'globalPosition') else event.globalPos()
delta = position - previous
self._pan_position = position
scroll = self.scroll_area()
if scroll is not None:
for bar, amount in ((scroll.horizontalScrollBar(), delta.x()), (scroll.verticalScrollBar(), delta.y())):
bar.setValue(round(bar.value() - amount))
if delta.x():
self.navigation_panned()
def end_pan(self, event):
self.move_pan(event)
self._pan_position = None
self.unsetCursor()