Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 78f2706aa8d0
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5714ffe758f4
Choose a head ref
  • 2 commits
  • 1 file changed
  • 1 contributor

Commits on Mar 12, 2016

  1. Copy the full SHA
    5e7eff6 View commit details
  2. Copy the full SHA
    5714ffe View commit details
Showing with 55 additions and 79 deletions.
  1. +55 −79 artiq/gui/scanwidget.py
134 changes: 55 additions & 79 deletions artiq/gui/scanwidget.py
Original file line number Diff line number Diff line change
@@ -15,12 +15,17 @@ def __init__(self):
self.proxy = None
self.sizePolicy().setControlType(QtWidgets.QSizePolicy.ButtonBox)
self.ticker = Ticker()
self.setMinimumHeight(40)
qfm = QtGui.QFontMetrics(QtGui.QFont())
lineSpacing = qfm.lineSpacing()
descent = qfm.descent()
self.setMinimumHeight(2*lineSpacing + descent + 5 + 5)

def paintEvent(self, ev):
painter = QtGui.QPainter(self)
font = painter.font()
avgCharWidth = QtGui.QFontMetrics(font).averageCharWidth()
qfm = QtGui.QFontMetrics(painter.font())
avgCharWidth = qfm.averageCharWidth()
lineSpacing = qfm.lineSpacing()
descent = qfm.descent()
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# The center of the slider handles should reflect what's displayed
# on the spinboxes.
@@ -29,7 +34,7 @@ def paintEvent(self, ev):
realLeft = self.proxy.pixelToReal(0)
realRight = self.proxy.pixelToReal(self.width())
ticks, prefix, labels = self.ticker(realLeft, realRight)
painter.drawText(0, -25, prefix)
painter.drawText(0, -5-descent-lineSpacing, prefix)

pen = QtGui.QPen()
pen.setWidth(2)
@@ -38,7 +43,7 @@ def paintEvent(self, ev):
for t, l in zip(ticks, labels):
t = self.proxy.realToPixel(t)
painter.drawLine(t, 0, t, -5)
painter.drawText(t - len(l)/2*avgCharWidth, -10, l)
painter.drawText(t - len(l)/2*avgCharWidth, -5-descent, l)

sliderStartPixel = self.proxy.realToPixel(self.proxy.realStart)
sliderStopPixel = self.proxy.realToPixel(self.proxy.realStop)
@@ -288,16 +293,33 @@ def paintEvent(self, ev):

# real (Sliders) => pixel (one pixel movement of sliders would increment by X)
# => range (minimum granularity that sliders understand).
class ScanProxy(QtCore.QObject):
class ScanWidget(QtWidgets.QWidget):
sigStartMoved = QtCore.pyqtSignal(float)
sigStopMoved = QtCore.pyqtSignal(float)
sigNumChanged = QtCore.pyqtSignal(int)

def __init__(self, slider, axis, zoomMargin, dynamicRange, zoomFactor):
QtCore.QObject.__init__(self)
self.axis = axis
def __init__(self, zoomFactor=1.05, zoomMargin=.1, dynamicRange=1e9):
QtWidgets.QWidget.__init__(self)
self.slider = slider = ScanSlider()
self.axis = axis = ScanAxis()
axis.proxy = self
self.slider = slider

# Layout.
layout = QtWidgets.QVBoxLayout()
layout.setSpacing(0)
layout.addWidget(axis)
layout.addWidget(slider)
self.setLayout(layout)

# Context menu entries
self.menu = QtWidgets.QMenu(self)
viewRangeAct = QtWidgets.QAction("&View Range", self)
viewRangeAct.triggered.connect(self.viewRange)
self.menu.addAction(viewRangeAct)
snapRangeAct = QtWidgets.QAction("&Snap Range", self)
snapRangeAct.triggered.connect(self.snapRange)
self.menu.addAction(snapRangeAct)

self.realStart = -1.
self.realStop = 1.
self.numPoints = 11
@@ -320,6 +342,9 @@ def __init__(self, slider, axis, zoomMargin, dynamicRange, zoomFactor):
slider.sigStopMoved.connect(self.handleStopMoved)
slider.sigStartMoved.connect(self.handleStartMoved)

def contextMenuEvent(self, ev):
self.menu.popup(ev.globalPos())

# pixel vals for sliders: 0 to slider_width - 1
def realToPixel(self, val):
a, b = self.realToPixelTransform
@@ -341,32 +366,32 @@ def realToRange(self, val):
pixelVal = self.realToPixel(val)
return self.slider.pixelPosToRangeValue(pixelVal)

def moveStop(self, val):
def setStop(self, val):
sliderX = self.realToRange(val)
self.slider.setStopPosition(sliderX)
self.realStop = val
self.axis.update() # Number of points ticks changed positions.

def moveStart(self, val):
def setStart(self, val):
sliderX = self.realToRange(val)
self.slider.setStartPosition(sliderX)
self.realStart = val
self.axis.update()

def setNumPoints(self, val):
self.numPoints = val
self.axis.update()

def handleStopMoved(self, rangeVal):
# FIXME: this relies on the event being fed back and ending up calling
# moveStop()
# setStop()
self.sigStopMoved.emit(self.rangeToReal(rangeVal))

def handleStartMoved(self, rangeVal):
# FIXME: this relies on the event being fed back and ending up calling
# moveStart()
# setStart()
self.sigStartMoved.emit(self.rangeToReal(rangeVal))

def setNumPoints(self, val):
self.numPoints = val
self.axis.update()

def handleZoom(self, zoomFactor, mouseXPos):
newScale = self.realToPixelTransform[1] * zoomFactor
refReal = self.pixelToReal(mouseXPos)
@@ -375,8 +400,8 @@ def handleZoom(self, zoomFactor, mouseXPos):
if zoomFactor > 1 and abs(newZero) > self.dynamicRange:
return
self.realToPixelTransform = newLeft, newScale
self.moveStop(self.realStop)
self.moveStart(self.realStart)
self.setStop(self.realStop)
self.setStart(self.realStart)

def viewRange(self):
newScale = self.slider.effectiveWidth()/abs(
@@ -387,8 +412,8 @@ def viewRange(self):
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)
self.setStop(self.realStop)
self.setStart(self.realStart)
self.axis.update() # Axis normally takes care to update itself during
# zoom. In this code path however, the zoom didn't arrive via the axis
# widget, so we need to notify manually.
@@ -401,8 +426,8 @@ def viewRange(self):
def viewRangeInit(self):
currRangeReal = abs(self.realStop - self.realStart)
if currRangeReal == 0:
self.moveStop(self.realStop)
self.moveStart(self.realStart)
self.setStop(self.realStop)
self.setStart(self.realStart)
# Ill-formed snap range- move the sliders anyway,
# because we arrived here during widget
# initialization, where the slider positions are likely invalid.
@@ -426,7 +451,7 @@ def snapRange(self):
# consequence of ValueChanged signal in spinboxes. The slider widget
# has guards against recursive signals in setSpan().
# FIXME: this relies on the events being fed back and ending up
# calling moveStart() and moveStop()
# calling setStart() and setStop()
self.sigStopMoved.emit(newStop)
self.sigStartMoved.emit(newStart)

@@ -444,15 +469,18 @@ def wheelEvent(self, ev):
# calling setNumPoints()
self.sigNumChanged.emit(self.numPoints + z)
self.axis.update()
else:
ev.accept()
elif ev.modifiers() & QtCore.Qt.ControlModifier:
z = self.zoomFactor**(y / 120.)
# Remove the slider-handle shift correction, b/c none of the
# other widgets know about it. If we have the mouse directly
# over a tick during a zoom, it should appear as if we are
# doing zoom relative to the ticks which live in axis
# pixel-space, not slider pixel-space.
self.handleZoom(z, ev.x() - self.slider.handleWidth()/2)
ev.accept()
ev.accept()
else:
ev.ignore()

def eventFilter(self, obj, ev):
if ev.type() == QtCore.QEvent.Wheel:
@@ -490,55 +518,3 @@ def eventFilter(self, obj, ev):
# confident that the new slider position will still map to the
# same positions in the new axis-space.
return False


class ScanWidget(QtWidgets.QWidget):
sigStartMoved = QtCore.pyqtSignal(float)
sigStopMoved = QtCore.pyqtSignal(float)
sigNumChanged = QtCore.pyqtSignal(int)

def __init__(self, zoomFactor=1.05, zoomMargin=.1, dynamicRange=1e9):
QtWidgets.QWidget.__init__(self)
self.slider = slider = ScanSlider()
self.axis = axis = ScanAxis()
self.proxy = ScanProxy(slider, axis, zoomMargin, dynamicRange,
zoomFactor)

# Layout.
layout = QtWidgets.QVBoxLayout()
layout.setSpacing(0)
layout.addWidget(axis)
layout.addWidget(slider)
self.setLayout(layout)

# Connect signals (minus context menu)
self.proxy.sigStopMoved.connect(self.sigStopMoved)
self.proxy.sigStartMoved.connect(self.sigStartMoved)
self.proxy.sigNumChanged.connect(self.sigNumChanged)

# Context menu entries
self.menu = QtWidgets.QMenu(self)
viewRangeAct = QtWidgets.QAction("&View Range", self)
viewRangeAct.triggered.connect(self.viewRange)
self.menu.addAction(viewRangeAct)
snapRangeAct = QtWidgets.QAction("&Snap Range", self)
snapRangeAct.triggered.connect(self.snapRange)
self.menu.addAction(snapRangeAct)

def setStop(self, val):
self.proxy.moveStop(val)

def setStart(self, val):
self.proxy.moveStart(val)

def setNumPoints(self, val):
self.proxy.setNumPoints(val)

def viewRange(self):
self.proxy.viewRange()

def snapRange(self):
self.proxy.snapRange()

def contextMenuEvent(self, ev):
self.menu.popup(ev.globalPos())