Skip to content

Commit 7f3e1c9

Browse files
committedMar 11, 2016
scanwidget: apply changes as of 10439cb
1 parent e4b854b commit 7f3e1c9

File tree

1 file changed

+47
-43
lines changed

1 file changed

+47
-43
lines changed
 

‎artiq/gui/scanwidget.py

+47-43
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
import logging
2+
13
from PyQt5 import QtGui, QtCore, QtWidgets
24
from numpy import linspace
35

46
from .ticker import Ticker
57

68

9+
logger = logging.getLogger(__name__)
10+
11+
712
class ScanAxis(QtWidgets.QWidget):
813
sigZoom = QtCore.pyqtSignal(float, int)
914
sigPoints = QtCore.pyqtSignal(int)
@@ -52,6 +57,10 @@ def wheelEvent(self, ev):
5257
if y:
5358
if ev.modifiers() & QtCore.Qt.ShiftModifier:
5459
# If shift+scroll, modify number of points.
60+
# TODO: This is not perfect. For high-resolution touchpads you
61+
# get many small events with y < 120 which should accumulate.
62+
# That would also match the wheel behavior of an integer
63+
# spinbox.
5564
z = int(y / 120.)
5665
self.sigPoints.emit(z)
5766
else:
@@ -319,14 +328,14 @@ class ScanProxy(QtCore.QObject):
319328
sigStopMoved = QtCore.pyqtSignal(float)
320329
sigNumPoints = QtCore.pyqtSignal(int)
321330

322-
def __init__(self, slider, axis, rangeFactor, dynamicRange):
331+
def __init__(self, slider, axis, zoomMargin, dynamicRange):
323332
QtCore.QObject.__init__(self)
324333
self.axis = axis
325334
self.slider = slider
326335
self.realStart = 0
327336
self.realStop = 0
328337
self.numPoints = 10
329-
self.rangeFactor = rangeFactor
338+
self.zoomMargin = zoomMargin
330339
self.dynamicRange = dynamicRange
331340

332341
# Transform that maps the spinboxes to a pixel position on the
@@ -335,21 +344,21 @@ def __init__(self, slider, axis, rangeFactor, dynamicRange):
335344
# Because the axis's width will change when placed within a layout,
336345
# the realToPixelTransform will initially be invalid. It will be set
337346
# properly during the first resizeEvent, with the below transform.
338-
self.realToPixelTransform = self.axis.width()/2, 1.
347+
self.realToPixelTransform = -self.axis.width()/2, 1.
339348
self.invalidOldSizeExpected = True
340349

341350
# pixel vals for sliders: 0 to slider_width - 1
342351
def realToPixel(self, val):
343352
a, b = self.realToPixelTransform
344-
rawVal = b*(val + a)
353+
rawVal = b*(val - a)
345354
# Clamp pixel values to 32 bits, b/c Qt will otherwise wrap values.
346355
rawVal = min(max(-(1 << 31), rawVal), (1 << 31) - 1)
347356
return rawVal
348357

349358
# Get a point from pixel units to what the sliders display.
350359
def pixelToReal(self, val):
351360
a, b = self.realToPixelTransform
352-
return val/b - a
361+
return val/b + a
353362

354363
def rangeToReal(self, val):
355364
pixelVal = self.slider.rangeValueToPixelPos(val)
@@ -387,26 +396,22 @@ def setNumPoints(self, val):
387396
def handleZoom(self, zoomFactor, mouseXPos):
388397
newScale = self.realToPixelTransform[1] * zoomFactor
389398
refReal = self.pixelToReal(mouseXPos)
390-
newLeft = mouseXPos/newScale - refReal
391-
if abs(newLeft*newScale) > self.dynamicRange:
399+
newLeft = refReal - mouseXPos/newScale
400+
newZero = newLeft*newScale + self.slider.effectiveWidth()/2
401+
if zoomFactor > 1 and abs(newZero) > self.dynamicRange:
392402
return
393403
self.realToPixelTransform = newLeft, newScale
394404
self.moveStop(self.realStop)
395405
self.moveStart(self.realStart)
396406

397-
def zoomToFit(self):
398-
currRangeReal = abs(self.realStop - self.realStart)
399-
# Slider closest to the left should be used to find the new axis left.
400-
if self.realStop < self.realStart:
401-
refSlider = self.realStop
402-
else:
403-
refSlider = self.realStart
404-
if self.rangeFactor <= 2 or currRangeReal == 0:
405-
return # Ill-formed snap range- do nothing
406-
proportion = self.rangeFactor/(self.rangeFactor - 2)
407-
newScale = self.slider.effectiveWidth()/(proportion*currRangeReal)
408-
newLeft = (self.slider.effectiveWidth()/(self.rangeFactor*newScale) -
409-
refSlider)
407+
def viewRange(self):
408+
newScale = self.slider.effectiveWidth()/abs(
409+
self.realStop - self.realStart)
410+
newScale *= 1 - 2*self.zoomMargin
411+
newCenter = (self.realStop + self.realStart)/2
412+
if newCenter:
413+
newScale = min(newScale, self.dynamicRange/abs(newCenter))
414+
newLeft = newCenter - self.slider.effectiveWidth()/2/newScale
410415
self.realToPixelTransform = newLeft, newScale
411416
self.moveStop(self.realStop)
412417
self.moveStart(self.realStart)
@@ -419,9 +424,9 @@ def zoomToFit(self):
419424
# This function handles handles the slider positions. Slider and axis
420425
# handle its own width changes; proxy watches for axis width resizeEvent to
421426
# alter mapping from real to pixel space.
422-
def zoomToFitInit(self):
427+
def viewRangeInit(self):
423428
currRangeReal = abs(self.realStop - self.realStart)
424-
if self.rangeFactor <= 2 or currRangeReal == 0:
429+
if currRangeReal == 0:
425430
self.moveStop(self.realStop)
426431
self.moveStart(self.realStart)
427432
# Ill-formed snap range- move the sliders anyway,
@@ -430,15 +435,15 @@ def zoomToFitInit(self):
430435
# This will force the sliders to have positions on the axis
431436
# which reflect the start/stop values currently set.
432437
else:
433-
self.zoomToFit()
438+
self.viewRange()
434439
# Notify spinboxes manually, since slider wasn't clicked and will
435440
# therefore not emit signals.
436441
self.sigStopMoved.emit(self.realStop)
437442
self.sigStartMoved.emit(self.realStart)
438443

439-
def fitToView(self):
440-
lowRange = 1.0/self.rangeFactor
441-
highRange = (self.rangeFactor - 1)/self.rangeFactor
444+
def snapRange(self):
445+
lowRange = self.zoomMargin
446+
highRange = 1 - self.zoomMargin
442447
newStart = self.pixelToReal(lowRange * self.slider.effectiveWidth())
443448
newStop = self.pixelToReal(highRange * self.slider.effectiveWidth())
444449
sliderRange = self.slider.maximum() - self.slider.minimum()
@@ -463,21 +468,21 @@ def eventFilter(self, obj, ev):
463468
newWidth = ev.size().width() - self.slider.handleWidth()
464469
# assert refRight > oldLeft
465470
newScale = newWidth/(refRight - oldLeft)
466-
self.realToPixelTransform = -oldLeft, newScale
471+
self.realToPixelTransform = oldLeft, newScale
467472
else:
468473
# TODO: self.axis.width() is invalid during object
469474
# construction. The width will change when placed in a
470475
# layout WITHOUT a resizeEvent. Why?
471476
oldLeft = -ev.size().width()/2
472477
newScale = 1.0
473-
self.realToPixelTransform = -oldLeft, newScale
478+
self.realToPixelTransform = oldLeft, newScale
474479
# We need to reinitialize the pixel transform b/c the old width
475480
# of the axis is no longer valid. When we have a valid transform,
476-
# we can then zoomToFit based on the desired real values.
481+
# we can then viewRange based on the desired real values.
477482
# The slider handle values are invalid before this point as well;
478483
# we set them to the correct value here, regardless of whether
479484
# the slider has already resized itsef or not.
480-
self.zoomToFitInit()
485+
self.viewRangeInit()
481486
self.invalidOldSizeExpected = False
482487
# assert self.pixelToReal(0) == oldLeft, \
483488
# "{}, {}".format(self.pixelToReal(0), oldLeft)
@@ -493,13 +498,12 @@ class ScanWidget(QtWidgets.QWidget):
493498
sigStopMoved = QtCore.pyqtSignal(float)
494499
sigNumChanged = QtCore.pyqtSignal(int)
495500

496-
def __init__(self, zoomFactor=1.05, rangeFactor=6, dynamicRange=1e8):
501+
def __init__(self, zoomFactor=1.05, zoomMargin=.1, dynamicRange=1e8):
497502
QtWidgets.QWidget.__init__(self)
498503
self.slider = slider = ScanSlider()
499504
self.axis = axis = ScanAxis(zoomFactor)
500-
self.proxy = ScanProxy(slider, axis, rangeFactor, dynamicRange)
505+
self.proxy = ScanProxy(slider, axis, zoomMargin, dynamicRange)
501506
axis.proxy = self.proxy
502-
axis.slider = slider
503507
slider.setMaximum(1023)
504508

505509
# Layout.
@@ -524,10 +528,10 @@ def __init__(self, zoomFactor=1.05, rangeFactor=6, dynamicRange=1e8):
524528
slider.installEventFilter(axis)
525529

526530
# Context menu entries
527-
self.zoomToFitAct = QtWidgets.QAction("&View Range", self)
528-
self.fitToViewAct = QtWidgets.QAction("&Snap Range", self)
529-
self.zoomToFitAct.triggered.connect(self.zoomToFit)
530-
self.fitToViewAct.triggered.connect(self.fitToView)
531+
self.viewRangeAct = QtWidgets.QAction("&View Range", self)
532+
self.snapRangeAct = QtWidgets.QAction("&Snap Range", self)
533+
self.viewRangeAct.triggered.connect(self.viewRange)
534+
self.snapRangeAct.triggered.connect(self.snapRange)
531535

532536
# Spinbox and button slots. Any time the spinboxes change, ScanWidget
533537
# mirrors it and passes the information to the proxy.
@@ -540,14 +544,14 @@ def setStart(self, val):
540544
def setNumPoints(self, val):
541545
self.proxy.setNumPoints(val)
542546

543-
def zoomToFit(self):
544-
self.proxy.zoomToFit()
547+
def viewRange(self):
548+
self.proxy.viewRange()
545549

546-
def fitToView(self):
547-
self.proxy.fitToView()
550+
def snapRange(self):
551+
self.proxy.snapRange()
548552

549553
def contextMenuEvent(self, ev):
550554
menu = QtWidgets.QMenu(self)
551-
menu.addAction(self.zoomToFitAct)
552-
menu.addAction(self.fitToViewAct)
555+
menu.addAction(self.viewRangeAct)
556+
menu.addAction(self.snapRangeAct)
553557
menu.exec(ev.globalPos())

0 commit comments

Comments
 (0)
Please sign in to comment.