scanwidget: apply changes as of 10439cb

This commit is contained in:
Robert Jördens 2016-03-11 18:29:21 +01:00
parent e4b854b8bf
commit 7f3e1c989d
1 changed files with 47 additions and 43 deletions

View File

@ -1,9 +1,14 @@
import logging
from PyQt5 import QtGui, QtCore, QtWidgets
from numpy import linspace
from .ticker import Ticker
logger = logging.getLogger(__name__)
class ScanAxis(QtWidgets.QWidget):
sigZoom = QtCore.pyqtSignal(float, int)
sigPoints = QtCore.pyqtSignal(int)
@ -52,6 +57,10 @@ class ScanAxis(QtWidgets.QWidget):
if y:
if ev.modifiers() & QtCore.Qt.ShiftModifier:
# If shift+scroll, modify number of points.
# TODO: This is not perfect. For high-resolution touchpads you
# get many small events with y < 120 which should accumulate.
# That would also match the wheel behavior of an integer
# spinbox.
z = int(y / 120.)
self.sigPoints.emit(z)
else:
@ -319,14 +328,14 @@ class ScanProxy(QtCore.QObject):
sigStopMoved = QtCore.pyqtSignal(float)
sigNumPoints = QtCore.pyqtSignal(int)
def __init__(self, slider, axis, rangeFactor, dynamicRange):
def __init__(self, slider, axis, zoomMargin, dynamicRange):
QtCore.QObject.__init__(self)
self.axis = axis
self.slider = slider
self.realStart = 0
self.realStop = 0
self.numPoints = 10
self.rangeFactor = rangeFactor
self.zoomMargin = zoomMargin
self.dynamicRange = dynamicRange
# Transform that maps the spinboxes to a pixel position on the
@ -335,13 +344,13 @@ class ScanProxy(QtCore.QObject):
# Because the axis's width will change when placed within a layout,
# the realToPixelTransform will initially be invalid. It will be set
# properly during the first resizeEvent, with the below transform.
self.realToPixelTransform = self.axis.width()/2, 1.
self.realToPixelTransform = -self.axis.width()/2, 1.
self.invalidOldSizeExpected = True
# pixel vals for sliders: 0 to slider_width - 1
def realToPixel(self, val):
a, b = self.realToPixelTransform
rawVal = b*(val + a)
rawVal = b*(val - a)
# Clamp pixel values to 32 bits, b/c Qt will otherwise wrap values.
rawVal = min(max(-(1 << 31), rawVal), (1 << 31) - 1)
return rawVal
@ -349,7 +358,7 @@ class ScanProxy(QtCore.QObject):
# Get a point from pixel units to what the sliders display.
def pixelToReal(self, val):
a, b = self.realToPixelTransform
return val/b - a
return val/b + a
def rangeToReal(self, val):
pixelVal = self.slider.rangeValueToPixelPos(val)
@ -387,26 +396,22 @@ class ScanProxy(QtCore.QObject):
def handleZoom(self, zoomFactor, mouseXPos):
newScale = self.realToPixelTransform[1] * zoomFactor
refReal = self.pixelToReal(mouseXPos)
newLeft = mouseXPos/newScale - refReal
if abs(newLeft*newScale) > self.dynamicRange:
newLeft = refReal - mouseXPos/newScale
newZero = newLeft*newScale + self.slider.effectiveWidth()/2
if zoomFactor > 1 and abs(newZero) > self.dynamicRange:
return
self.realToPixelTransform = newLeft, newScale
self.moveStop(self.realStop)
self.moveStart(self.realStart)
def zoomToFit(self):
currRangeReal = abs(self.realStop - self.realStart)
# Slider closest to the left should be used to find the new axis left.
if self.realStop < self.realStart:
refSlider = self.realStop
else:
refSlider = self.realStart
if self.rangeFactor <= 2 or currRangeReal == 0:
return # Ill-formed snap range- do nothing
proportion = self.rangeFactor/(self.rangeFactor - 2)
newScale = self.slider.effectiveWidth()/(proportion*currRangeReal)
newLeft = (self.slider.effectiveWidth()/(self.rangeFactor*newScale) -
refSlider)
def viewRange(self):
newScale = self.slider.effectiveWidth()/abs(
self.realStop - self.realStart)
newScale *= 1 - 2*self.zoomMargin
newCenter = (self.realStop + self.realStart)/2
if newCenter:
newScale = min(newScale, self.dynamicRange/abs(newCenter))
newLeft = newCenter - self.slider.effectiveWidth()/2/newScale
self.realToPixelTransform = newLeft, newScale
self.moveStop(self.realStop)
self.moveStart(self.realStart)
@ -419,9 +424,9 @@ class ScanProxy(QtCore.QObject):
# This function handles handles the slider positions. Slider and axis
# handle its own width changes; proxy watches for axis width resizeEvent to
# alter mapping from real to pixel space.
def zoomToFitInit(self):
def viewRangeInit(self):
currRangeReal = abs(self.realStop - self.realStart)
if self.rangeFactor <= 2 or currRangeReal == 0:
if currRangeReal == 0:
self.moveStop(self.realStop)
self.moveStart(self.realStart)
# Ill-formed snap range- move the sliders anyway,
@ -430,15 +435,15 @@ class ScanProxy(QtCore.QObject):
# This will force the sliders to have positions on the axis
# which reflect the start/stop values currently set.
else:
self.zoomToFit()
self.viewRange()
# Notify spinboxes manually, since slider wasn't clicked and will
# therefore not emit signals.
self.sigStopMoved.emit(self.realStop)
self.sigStartMoved.emit(self.realStart)
def fitToView(self):
lowRange = 1.0/self.rangeFactor
highRange = (self.rangeFactor - 1)/self.rangeFactor
def snapRange(self):
lowRange = self.zoomMargin
highRange = 1 - self.zoomMargin
newStart = self.pixelToReal(lowRange * self.slider.effectiveWidth())
newStop = self.pixelToReal(highRange * self.slider.effectiveWidth())
sliderRange = self.slider.maximum() - self.slider.minimum()
@ -463,21 +468,21 @@ class ScanProxy(QtCore.QObject):
newWidth = ev.size().width() - self.slider.handleWidth()
# assert refRight > oldLeft
newScale = newWidth/(refRight - oldLeft)
self.realToPixelTransform = -oldLeft, newScale
self.realToPixelTransform = oldLeft, newScale
else:
# TODO: self.axis.width() is invalid during object
# construction. The width will change when placed in a
# layout WITHOUT a resizeEvent. Why?
oldLeft = -ev.size().width()/2
newScale = 1.0
self.realToPixelTransform = -oldLeft, newScale
self.realToPixelTransform = oldLeft, newScale
# We need to reinitialize the pixel transform b/c the old width
# of the axis is no longer valid. When we have a valid transform,
# we can then zoomToFit based on the desired real values.
# we can then viewRange based on the desired real values.
# The slider handle values are invalid before this point as well;
# we set them to the correct value here, regardless of whether
# the slider has already resized itsef or not.
self.zoomToFitInit()
self.viewRangeInit()
self.invalidOldSizeExpected = False
# assert self.pixelToReal(0) == oldLeft, \
# "{}, {}".format(self.pixelToReal(0), oldLeft)
@ -493,13 +498,12 @@ class ScanWidget(QtWidgets.QWidget):
sigStopMoved = QtCore.pyqtSignal(float)
sigNumChanged = QtCore.pyqtSignal(int)
def __init__(self, zoomFactor=1.05, rangeFactor=6, dynamicRange=1e8):
def __init__(self, zoomFactor=1.05, zoomMargin=.1, dynamicRange=1e8):
QtWidgets.QWidget.__init__(self)
self.slider = slider = ScanSlider()
self.axis = axis = ScanAxis(zoomFactor)
self.proxy = ScanProxy(slider, axis, rangeFactor, dynamicRange)
self.proxy = ScanProxy(slider, axis, zoomMargin, dynamicRange)
axis.proxy = self.proxy
axis.slider = slider
slider.setMaximum(1023)
# Layout.
@ -524,10 +528,10 @@ class ScanWidget(QtWidgets.QWidget):
slider.installEventFilter(axis)
# Context menu entries
self.zoomToFitAct = QtWidgets.QAction("&View Range", self)
self.fitToViewAct = QtWidgets.QAction("&Snap Range", self)
self.zoomToFitAct.triggered.connect(self.zoomToFit)
self.fitToViewAct.triggered.connect(self.fitToView)
self.viewRangeAct = QtWidgets.QAction("&View Range", self)
self.snapRangeAct = QtWidgets.QAction("&Snap Range", self)
self.viewRangeAct.triggered.connect(self.viewRange)
self.snapRangeAct.triggered.connect(self.snapRange)
# Spinbox and button slots. Any time the spinboxes change, ScanWidget
# mirrors it and passes the information to the proxy.
@ -540,14 +544,14 @@ class ScanWidget(QtWidgets.QWidget):
def setNumPoints(self, val):
self.proxy.setNumPoints(val)
def zoomToFit(self):
self.proxy.zoomToFit()
def viewRange(self):
self.proxy.viewRange()
def fitToView(self):
self.proxy.fitToView()
def snapRange(self):
self.proxy.snapRange()
def contextMenuEvent(self, ev):
menu = QtWidgets.QMenu(self)
menu.addAction(self.zoomToFitAct)
menu.addAction(self.fitToViewAct)
menu.addAction(self.viewRangeAct)
menu.addAction(self.snapRangeAct)
menu.exec(ev.globalPos())