Initial commit (again)

This commit is contained in:
Luke Campagnola 2010-03-22 01:48:52 -04:00
commit 3d84aefbc4
24 changed files with 8108 additions and 0 deletions

413
GraphicsView.py Normal file
View File

@ -0,0 +1,413 @@
# -*- coding: utf-8 -*-
"""
GraphicsView.py - Extension of QGraphicsView
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
#from numpy import vstack
#import time
from Point import *
#from vector import *
class GraphicsView(QtGui.QGraphicsView):
def __init__(self, *args):
"""Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the
viewed coordinate range. Also automatically creates a QGraphicsScene and a central QGraphicsWidget
that is automatically scaled to the full view geometry.
By default, the view coordinate system matches the widget's pixel coordinates and
automatically updates when the view is resized. This can be overridden by setting
autoPixelRange=False. The exact visible range can be set with setRange().
The view can be panned using the middle mouse button and scaled using the right mouse button if
enabled via enableMouse()."""
QtGui.QGraphicsView.__init__(self, *args)
self.setViewport(QtOpenGL.QGLWidget())
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(0,0,0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active,QtGui.QPalette.Base,brush)
brush = QtGui.QBrush(QtGui.QColor(0,0,0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive,QtGui.QPalette.Base,brush)
brush = QtGui.QBrush(QtGui.QColor(244,244,244))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled,QtGui.QPalette.Base,brush)
self.setPalette(palette)
self.setProperty("cursor",QtCore.QVariant(QtCore.Qt.ArrowCursor))
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setFrameShape(QtGui.QFrame.NoFrame)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
#self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate)
self.setSceneRect(QtCore.QRectF(-1e10, -1e10, 2e10, 2e10))
#self.setInteractive(False)
self.lockedViewports = []
self.lastMousePos = None
#self.setMouseTracking(False)
self.aspectLocked = False
self.yInverted = True
self.range = QtCore.QRectF(0, 0, 1, 1)
self.autoPixelRange = True
self.currentItem = None
self.clearMouse()
self.updateMatrix()
self.sceneObj = QtGui.QGraphicsScene()
self.setScene(self.sceneObj)
self.centralWidget = None
self.setCentralItem(QtGui.QGraphicsWidget())
self.mouseEnabled = False
self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False)
self.clickAccepted = False
def setCentralItem(self, item):
if self.centralWidget is not None:
self.scene().removeItem(self.centralWidget)
self.centralWidget = item
self.sceneObj.addItem(item)
def addItem(self, *args):
return self.scene().addItem(*args)
def enableMouse(self, b=True):
self.mouseEnabled = b
self.autoPixelRange = (not b)
def clearMouse(self):
self.mouseTrail = []
self.lastButtonReleased = None
def resizeEvent(self, ev):
if self.autoPixelRange:
self.range = QtCore.QRectF(0, 0, self.size().width(), self.size().height())
self.setRange(self.range, padding=0, disableAutoPixel=False)
self.updateMatrix()
def updateMatrix(self, propagate=True):
#print "udpateMatrix:"
translate = Point(self.range.center())
if self.range.width() == 0 or self.range.height() == 0:
return
scale = Point(self.size().width()/self.range.width(), self.size().height()/self.range.height())
m = QtGui.QMatrix()
## First center the viewport at 0
self.resetMatrix()
center = self.viewportTransform().inverted()[0].map(Point(self.width()/2., self.height()/2.))
if self.yInverted:
m.translate(center.x(), center.y())
#print " inverted; translate", center.x(), center.y()
else:
m.translate(center.x(), -center.y())
#print " not inverted; translate", center.x(), -center.y()
## Now scale and translate properly
if self.aspectLocked:
scale = Point(scale.min())
if not self.yInverted:
scale = scale * Point(1, -1)
m.scale(scale[0], scale[1])
#print " scale:", scale
st = translate
m.translate(-st[0], -st[1])
#print " translate:", st
self.setMatrix(m)
self.currentScale = scale
if propagate:
for v in self.lockedViewports:
v.setXRange(self.range, padding=0)
def visibleRange(self):
r = QtCore.QRectF(self.rect())
return self.viewportTransform().inverted()[0].mapRect(r)
def translate(self, dx, dy):
self.range.adjust(dx, dy, dx, dy)
self.updateMatrix()
def scale(self, sx, sy, center=None):
scale = [sx, sy]
if self.aspectLocked:
scale[0] = scale[1]
#adj = (self.range.width()*0.5*(1.0-(1.0/scale[0])), self.range.height()*0.5*(1.0-(1.0/scale[1])))
#print "======\n", scale, adj
#print self.range
#self.range.adjust(adj[0], adj[1], -adj[0], -adj[1])
#print self.range
if self.scaleCenter:
center = None
if center is None:
center = self.range.center()
w = self.range.width() / scale[0]
h = self.range.height() / scale[1]
self.range = QtCore.QRectF(center.x() - (center.x()-self.range.left()) / scale[0], center.y() - (center.y()-self.range.top()) /scale[1], w, h)
self.updateMatrix()
def setRange(self, newRect=None, padding=0.05, lockAspect=None, propagate=True, disableAutoPixel=True):
if disableAutoPixel:
self.autoPixelRange=False
if newRect is None:
newRect = self.visibleRange()
padding = 0
padding = Point(padding)
newRect = QtCore.QRectF(newRect)
pw = newRect.width() * padding[0]
ph = newRect.height() * padding[1]
self.range = newRect.adjusted(-pw, -ph, pw, ph)
#print "New Range:", self.range
self.centralWidget.setGeometry(self.range)
self.updateMatrix(propagate)
self.emit(QtCore.SIGNAL('viewChanged'), self.range)
def lockXRange(self, v1):
if not v1 in self.lockedViewports:
self.lockedViewports.append(v1)
def setXRange(self, r, padding=0.05):
r1 = QtCore.QRectF(self.range)
r1.setLeft(r.left())
r1.setRight(r.right())
self.setRange(r1, padding=[padding, 0], propagate=False)
def setYRange(self, r, padding=0.05):
r1 = QtCore.QRectF(self.range)
r1.setTop(r.top())
r1.setBottom(r.bottom())
self.setRange(r1, padding=[0, padding], propagate=False)
def invertY(self, invert=True):
#if self.yInverted != invert:
#self.scale[1] *= -1.
self.yInverted = invert
self.updateMatrix()
def wheelEvent(self, ev):
if not self.mouseEnabled:
return
QtGui.QGraphicsView.wheelEvent(self, ev)
sc = 1.001 ** ev.delta()
#self.scale *= sc
#self.updateMatrix()
self.scale(sc, sc)
def setAspectLocked(self, s):
self.aspectLocked = s
#def mouseDoubleClickEvent(self, ev):
#QtGui.QGraphicsView.mouseDoubleClickEvent(self, ev)
#pass
### This function is here because interactive mode is disabled due to bugs.
#def graphicsSceneEvent(self, ev, pev=None, fev=None):
#ev1 = GraphicsSceneMouseEvent()
#ev1.setPos(QtCore.QPointF(ev.pos().x(), ev.pos().y()))
#ev1.setButtons(ev.buttons())
#ev1.setButton(ev.button())
#ev1.setModifiers(ev.modifiers())
#ev1.setScenePos(self.mapToScene(QtCore.QPoint(ev.pos())))
#if pev is not None:
#ev1.setLastPos(pev.pos())
#ev1.setLastScenePos(pev.scenePos())
#ev1.setLastScreenPos(pev.screenPos())
#if fev is not None:
#ev1.setButtonDownPos(fev.pos())
#ev1.setButtonDownScenePos(fev.scenePos())
#ev1.setButtonDownScreenPos(fev.screenPos())
#return ev1
def mousePressEvent(self, ev):
QtGui.QGraphicsView.mousePressEvent(self, ev)
print "Press over:"
for i in self.items(ev.pos()):
print i.zValue(), int(i.acceptedMouseButtons()), i, i.scenePos()
print "Event accepted:", ev.isAccepted()
print "Grabber:", self.scene().mouseGrabberItem()
if not self.mouseEnabled:
return
self.lastMousePos = Point(ev.pos())
self.mousePressPos = ev.pos()
self.clickAccepted = ev.isAccepted()
if not self.clickAccepted:
self.scene().clearSelection()
return ## Everything below disabled for now..
#self.currentItem = None
#maxZ = None
#for i in self.items(ev.pos()):
#if maxZ is None or maxZ < i.zValue():
#self.currentItem = i
#maxZ = i.zValue()
#print "make event"
#self.pev = self.graphicsSceneEvent(ev)
#self.fev = self.pev
#if self.currentItem is not None:
#self.currentItem.mousePressEvent(self.pev)
##self.clearMouse()
##self.mouseTrail.append(Point(self.mapToScene(ev.pos())))
#self.emit(QtCore.SIGNAL("mousePressed(PyQt_PyObject)"), self.mouseTrail)
def mouseReleaseEvent(self, ev):
QtGui.QGraphicsView.mouseReleaseEvent(self, ev)
if not self.mouseEnabled:
return
#self.mouseTrail.append(Point(self.mapToScene(ev.pos())))
self.emit(QtCore.SIGNAL("mouseReleased"), ev)
self.lastButtonReleased = ev.button()
return ## Everything below disabled for now..
##self.mouseTrail.append(Point(self.mapToScene(ev.pos())))
#self.emit(QtCore.SIGNAL("mouseReleased(PyQt_PyObject)"), self.mouseTrail)
#if self.currentItem is not None:
#pev = self.graphicsSceneEvent(ev, self.pev, self.fev)
#self.pev = pev
#self.currentItem.mouseReleaseEvent(pev)
#self.currentItem = None
def mouseMoveEvent(self, ev):
QtGui.QGraphicsView.mouseMoveEvent(self, ev)
if not self.mouseEnabled:
return
self.emit(QtCore.SIGNAL("sceneMouseMoved(PyQt_PyObject)"), self.mapToScene(ev.pos()))
#print "moved. Grabber:", self.scene().mouseGrabberItem()
if self.lastMousePos is None:
self.lastMousePos = Point(ev.pos())
if self.clickAccepted: ## Ignore event if an item in the scene has already claimed it.
return
delta = Point(ev.pos()) - self.lastMousePos
self.lastMousePos = Point(ev.pos())
if ev.buttons() == QtCore.Qt.RightButton:
delta = Point(clip(delta[0], -50, 50), clip(-delta[1], -50, 50))
scale = 1.01 ** delta
#if self.yInverted:
#scale[0] = 1. / scale[0]
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
elif ev.buttons() in [QtCore.Qt.MidButton, QtCore.Qt.LeftButton]: ## Allow panning by left or mid button.
tr = -delta / self.currentScale
self.translate(tr[0], tr[1])
self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
#return ## Everything below disabled for now..
##self.mouseTrail.append(Point(self.mapToScene(ev.pos())))
#if self.currentItem is not None:
#pev = self.graphicsSceneEvent(ev, self.pev, self.fev)
#self.pev = pev
#self.currentItem.mouseMoveEvent(pev)
def writeSvg(self, fileName=None):
if fileName is None:
fileName = str(QtGui.QFileDialog.getSaveFileName())
self.svg = QtSvg.QSvgGenerator()
self.svg.setFileName(fileName)
self.svg.setSize(self.size())
self.svg.setResolution(600)
painter = QtGui.QPainter(self.svg)
self.render(painter)
def writeImage(self, fileName=None):
if fileName is None:
fileName = str(QtGui.QFileDialog.getSaveFileName())
self.png = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
painter = QtGui.QPainter(self.png)
rh = self.renderHints()
self.setRenderHints(QtGui.QPainter.Antialiasing)
self.render(painter)
self.setRenderHints(rh)
self.png.save(fileName)
#def getFreehandLine(self):
## Wait for click
#self.clearMouse()
#while self.lastButtonReleased != QtCore.Qt.LeftButton:
#QtGui.qApp.sendPostedEvents()
#QtGui.qApp.processEvents()
#time.sleep(0.01)
#fl = vstack(self.mouseTrail)
#return fl
#def getClick(self):
#fl = self.getFreehandLine()
#return fl[-1]
#class GraphicsSceneMouseEvent(QtGui.QGraphicsSceneMouseEvent):
#"""Stand-in class for QGraphicsSceneMouseEvent"""
#def __init__(self):
#QtGui.QGraphicsSceneMouseEvent.__init__(self)
#def setPos(self, p):
#self.vpos = p
#def setButtons(self, p):
#self.vbuttons = p
#def setButton(self, p):
#self.vbutton = p
#def setModifiers(self, p):
#self.vmodifiers = p
#def setScenePos(self, p):
#self.vscenePos = p
#def setLastPos(self, p):
#self.vlastPos = p
#def setLastScenePos(self, p):
#self.vlastScenePos = p
#def setLastScreenPos(self, p):
#self.vlastScreenPos = p
#def setButtonDownPos(self, p):
#self.vbuttonDownPos = p
#def setButtonDownScenePos(self, p):
#self.vbuttonDownScenePos = p
#def setButtonDownScreenPos(self, p):
#self.vbuttonDownScreenPos = p
#def pos(self):
#return self.vpos
#def buttons(self):
#return self.vbuttons
#def button(self):
#return self.vbutton
#def modifiers(self):
#return self.vmodifiers
#def scenePos(self):
#return self.vscenePos
#def lastPos(self):
#return self.vlastPos
#def lastScenePos(self):
#return self.vlastScenePos
#def lastScreenPos(self):
#return self.vlastScreenPos
#def buttonDownPos(self):
#return self.vbuttonDownPos
#def buttonDownScenePos(self):
#return self.vbuttonDownScenePos
#def buttonDownScreenPos(self):
#return self.vbuttonDownScreenPos

275
ImageView.py Normal file
View File

@ -0,0 +1,275 @@
# -*- coding: utf-8 -*-
"""
ImageView.py - Widget for basic image dispay and analysis
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
Widget used for displaying 2D or 3D data. Features:
- float or int (including 16-bit int) image display via ImageItem
- zoom/pan via GraphicsView
- black/white level controls
- time slider for 3D data sets
- ROI plotting
- Image normalization through a variety of methods
"""
from ImageViewTemplate import *
from graphicsItems import *
from widgets import ROI
from PyQt4 import QtCore, QtGui
class PlotROI(ROI):
def __init__(self, size):
ROI.__init__(self, pos=[0,0], size=size, scaleSnap=True, translateSnap=True)
self.addScaleHandle([1, 1], [0, 0])
class ImageView(QtGui.QWidget):
def __init__(self, parent=None, name="ImageView", *args):
QtGui.QWidget.__init__(self, parent, *args)
self.levelMax = 4096
self.levelMin = 0
self.name = name
self.image = None
self.imageDisp = None
self.ui = Ui_Form()
self.ui.setupUi(self)
self.scene = self.ui.graphicsView.sceneObj
self.ui.graphicsView.enableMouse(True)
self.ui.graphicsView.autoPixelRange = False
self.ui.graphicsView.setAspectLocked(True)
self.ui.graphicsView.invertY()
self.ui.graphicsView.enableMouse()
self.imageItem = ImageItem()
self.scene.addItem(self.imageItem)
self.currentIndex = 0
self.ui.normGroup.hide()
self.roi = PlotROI(10)
self.roi.setZValue(20)
self.scene.addItem(self.roi)
self.roi.hide()
self.ui.roiPlot.hide()
self.roiCurve = self.ui.roiPlot.plot()
self.roiTimeLine = InfiniteLine(self.ui.roiPlot, 0)
self.roiTimeLine.setPen(QtGui.QPen(QtGui.QColor(255, 255, 0, 200)))
self.ui.roiPlot.addItem(self.roiTimeLine)
self.normLines = []
for i in [0,1]:
l = InfiniteLine(self.ui.roiPlot, 0)
l.setPen(QtGui.QPen(QtGui.QColor(0, 100, 200, 200)))
self.ui.roiPlot.addItem(l)
self.normLines.append(l)
l.hide()
for fn in ['addItem']:
setattr(self, fn, getattr(self.ui.graphicsView, fn))
QtCore.QObject.connect(self.ui.timeSlider, QtCore.SIGNAL('valueChanged(int)'), self.timeChanged)
QtCore.QObject.connect(self.ui.whiteSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage)
QtCore.QObject.connect(self.ui.blackSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage)
QtCore.QObject.connect(self.ui.roiBtn, QtCore.SIGNAL('clicked()'), self.roiClicked)
self.roi.connect(QtCore.SIGNAL('regionChanged'), self.roiChanged)
QtCore.QObject.connect(self.ui.normBtn, QtCore.SIGNAL('toggled(bool)'), self.normToggled)
QtCore.QObject.connect(self.ui.normDivideRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normSubtractRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normOffRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normROICheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normFrameCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normTimeRangeCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normStartSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateNorm)
QtCore.QObject.connect(self.ui.normStopSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateNorm)
self.ui.roiPlot.registerPlot(self.name + '_ROI')
def updateNorm(self):
for l, sl in zip(self.normLines, [self.ui.normStartSlider, self.ui.normStopSlider]):
if self.ui.normTimeRangeCheck.isChecked():
l.show()
else:
l.hide()
i, t = self.timeIndex(sl)
l.setPos(t)
self.imageDisp = None
self.updateImage()
self.roiChanged()
def normToggled(self, b):
self.ui.normGroup.setVisible(b)
def roiClicked(self):
if self.ui.roiBtn.isChecked():
self.roi.show()
self.ui.roiPlot.show()
self.roiChanged()
else:
self.roi.hide()
self.ui.roiPlot.hide()
def roiChanged(self):
if self.image is None:
return
image = self.getProcessedImage()
if image.ndim == 2:
axes = (0, 1)
elif image.ndim == 3:
axes = (1, 2)
else:
return
data = self.roi.getArrayRegion(image.view(ndarray), self.imageItem, axes)
if data is not None:
while data.ndim > 1:
data = data.mean(axis=1)
self.roiCurve.setData(y=data, x=self.tVals)
#self.ui.roiPlot.replot()
def setImage(self, img, autoRange=True, autoLevels=True, levels=None):
self.image = img
if hasattr(img, 'xvals'):
self.tVals = img.xvals(0)
else:
self.tVals = arange(img.shape[0])
self.ui.timeSlider.setValue(0)
#self.ui.normStartSlider.setValue(0)
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
if img.ndim == 2:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
elif img.ndim == 3:
if img.shape[2] <= 3:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
else:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
elif img.ndim == 4:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
self.imageDisp = None
if autoRange:
self.autoRange()
if autoLevels:
self.autoLevels()
if levels is not None:
self.levelMax = levels[1]
self.levelMin = levels[0]
self.updateImage()
if self.ui.roiBtn.isChecked():
self.roiChanged()
def autoLevels(self):
image = self.getProcessedImage()
self.ui.whiteSlider.setValue(self.ui.whiteSlider.maximum())
self.ui.blackSlider.setValue(0)
self.imageItem.setLevels(white=self.whiteLevel(), black=self.blackLevel())
def autoRange(self):
image = self.getProcessedImage()
self.ui.graphicsView.setRange(QtCore.QRectF(0, 0, image.shape[self.axes['x']], image.shape[self.axes['y']]), padding=0., lockAspect=True)
def getProcessedImage(self):
if self.imageDisp is None:
image = self.normalize(self.image)
self.imageDisp = image
self.levelMax = float(image.max())
self.levelMin = float(image.min())
return self.imageDisp
def normalize(self, image):
if self.ui.normOffRadio.isChecked():
return image
div = self.ui.normDivideRadio.isChecked()
norm = image.copy()
#if div:
#norm = ones(image.shape)
#else:
#norm = zeros(image.shape)
if div:
norm = norm.astype(float32)
if self.ui.normTimeRangeCheck.isChecked() and image.ndim == 3:
(sind, start) = self.timeIndex(self.ui.normStartSlider)
(eind, end) = self.timeIndex(self.ui.normStopSlider)
#print start, end, sind, eind
n = image[sind:eind+1].mean(axis=0)
n.shape = (1,) + n.shape
if div:
norm /= n
else:
norm -= n
if self.ui.normFrameCheck.isChecked() and image.ndim == 3:
n = image.mean(axis=1).mean(axis=1)
n.shape = n.shape + (1, 1)
if div:
norm /= n
else:
norm -= n
return norm
def timeChanged(self):
(ind, time) = self.timeIndex(self.ui.timeSlider)
if ind != self.currentIndex:
self.currentIndex = ind
self.updateImage()
self.roiTimeLine.setPos(time)
#self.ui.roiPlot.replot()
self.emit(QtCore.SIGNAL('timeChanged'), ind, time)
def updateImage(self):
## Redraw image on screen
if self.image is None:
return
image = self.getProcessedImage()
#print "update:", image.ndim, image.max(), image.min(), self.blackLevel(), self.whiteLevel()
if self.axes['t'] is None:
self.ui.timeSlider.hide()
self.imageItem.updateImage(image, white=self.whiteLevel(), black=self.blackLevel())
else:
self.ui.timeSlider.show()
self.imageItem.updateImage(image[self.currentIndex], white=self.whiteLevel(), black=self.blackLevel())
def timeIndex(self, slider):
"""Return the time and frame index indicated by a slider"""
if self.image is None:
return (0,0)
v = slider.value()
vmax = slider.maximum()
f = float(v) / vmax
t = 0.0
#xv = self.image.xvals('Time')
xv = self.tVals
if xv is None:
ind = int(f * self.image.shape[0])
else:
if len(xv) < 2:
return (0,0)
totTime = xv[-1] + (xv[-1]-xv[-2])
t = f * totTime
inds = argwhere(xv < t)
if len(inds) < 1:
return (0,t)
ind = inds[-1,0]
#print ind
return ind, t
def whiteLevel(self):
return self.levelMin + (self.levelMax-self.levelMin) * self.ui.whiteSlider.value() / self.ui.whiteSlider.maximum()
def blackLevel(self):
return self.levelMin + ((self.levelMax-self.levelMin) / self.ui.blackSlider.maximum()) * self.ui.blackSlider.value()

188
ImageViewTemplate.py Normal file
View File

@ -0,0 +1,188 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ImageViewTemplate.ui'
#
# Created: Fri Nov 20 08:22:10 2009
# by: PyQt4 UI code generator 4.6
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(757, 495)
self.verticalLayout = QtGui.QVBoxLayout(Form)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setMargin(0)
self.verticalLayout.setObjectName("verticalLayout")
self.splitter = QtGui.QSplitter(Form)
self.splitter.setOrientation(QtCore.Qt.Vertical)
self.splitter.setObjectName("splitter")
self.layoutWidget = QtGui.QWidget(self.splitter)
self.layoutWidget.setObjectName("layoutWidget")
self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
self.gridLayout.setSpacing(0)
self.gridLayout.setObjectName("gridLayout")
self.graphicsView = GraphicsView(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
self.graphicsView.setSizePolicy(sizePolicy)
self.graphicsView.setObjectName("graphicsView")
self.gridLayout.addWidget(self.graphicsView, 0, 0, 3, 1)
self.blackSlider = QtGui.QSlider(self.layoutWidget)
self.blackSlider.setMaximum(4096)
self.blackSlider.setOrientation(QtCore.Qt.Vertical)
self.blackSlider.setInvertedAppearance(False)
self.blackSlider.setInvertedControls(False)
self.blackSlider.setTickPosition(QtGui.QSlider.TicksBelow)
self.blackSlider.setTickInterval(410)
self.blackSlider.setObjectName("blackSlider")
self.gridLayout.addWidget(self.blackSlider, 0, 1, 1, 1)
self.whiteSlider = QtGui.QSlider(self.layoutWidget)
self.whiteSlider.setMaximum(4096)
self.whiteSlider.setProperty("value", 4096)
self.whiteSlider.setOrientation(QtCore.Qt.Vertical)
self.whiteSlider.setObjectName("whiteSlider")
self.gridLayout.addWidget(self.whiteSlider, 0, 2, 1, 2)
self.label = QtGui.QLabel(self.layoutWidget)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 1, 1, 1, 1)
self.label_2 = QtGui.QLabel(self.layoutWidget)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 1, 2, 1, 1)
self.roiBtn = QtGui.QPushButton(self.layoutWidget)
self.roiBtn.setMaximumSize(QtCore.QSize(40, 16777215))
self.roiBtn.setCheckable(True)
self.roiBtn.setObjectName("roiBtn")
self.gridLayout.addWidget(self.roiBtn, 2, 1, 1, 3)
self.timeSlider = QtGui.QSlider(self.layoutWidget)
self.timeSlider.setMaximum(65535)
self.timeSlider.setOrientation(QtCore.Qt.Horizontal)
self.timeSlider.setObjectName("timeSlider")
self.gridLayout.addWidget(self.timeSlider, 4, 0, 1, 1)
self.normBtn = QtGui.QPushButton(self.layoutWidget)
self.normBtn.setMaximumSize(QtCore.QSize(50, 16777215))
self.normBtn.setCheckable(True)
self.normBtn.setObjectName("normBtn")
self.gridLayout.addWidget(self.normBtn, 4, 1, 1, 2)
self.normGroup = QtGui.QGroupBox(self.layoutWidget)
self.normGroup.setObjectName("normGroup")
self.gridLayout_2 = QtGui.QGridLayout(self.normGroup)
self.gridLayout_2.setMargin(0)
self.gridLayout_2.setSpacing(0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.normSubtractRadio = QtGui.QRadioButton(self.normGroup)
self.normSubtractRadio.setObjectName("normSubtractRadio")
self.gridLayout_2.addWidget(self.normSubtractRadio, 0, 2, 1, 1)
self.normDivideRadio = QtGui.QRadioButton(self.normGroup)
self.normDivideRadio.setChecked(False)
self.normDivideRadio.setObjectName("normDivideRadio")
self.gridLayout_2.addWidget(self.normDivideRadio, 0, 1, 1, 1)
self.label_5 = QtGui.QLabel(self.normGroup)
font = QtGui.QFont()
font.setWeight(75)
font.setBold(True)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.gridLayout_2.addWidget(self.label_5, 0, 0, 1, 1)
self.label_3 = QtGui.QLabel(self.normGroup)
font = QtGui.QFont()
font.setWeight(75)
font.setBold(True)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
self.label_4 = QtGui.QLabel(self.normGroup)
font = QtGui.QFont()
font.setWeight(75)
font.setBold(True)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 4, 0, 1, 1)
self.normROICheck = QtGui.QCheckBox(self.normGroup)
self.normROICheck.setObjectName("normROICheck")
self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1)
self.normStartSlider = QtGui.QSlider(self.normGroup)
self.normStartSlider.setMaximum(65535)
self.normStartSlider.setOrientation(QtCore.Qt.Horizontal)
self.normStartSlider.setObjectName("normStartSlider")
self.gridLayout_2.addWidget(self.normStartSlider, 2, 0, 1, 6)
self.normStopSlider = QtGui.QSlider(self.normGroup)
self.normStopSlider.setMaximum(65535)
self.normStopSlider.setOrientation(QtCore.Qt.Horizontal)
self.normStopSlider.setObjectName("normStopSlider")
self.gridLayout_2.addWidget(self.normStopSlider, 3, 0, 1, 6)
self.normXBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normXBlurSpin.setObjectName("normXBlurSpin")
self.gridLayout_2.addWidget(self.normXBlurSpin, 4, 2, 1, 1)
self.label_8 = QtGui.QLabel(self.normGroup)
self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_8.setObjectName("label_8")
self.gridLayout_2.addWidget(self.label_8, 4, 1, 1, 1)
self.label_9 = QtGui.QLabel(self.normGroup)
self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_9.setObjectName("label_9")
self.gridLayout_2.addWidget(self.label_9, 4, 3, 1, 1)
self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normYBlurSpin.setObjectName("normYBlurSpin")
self.gridLayout_2.addWidget(self.normYBlurSpin, 4, 4, 1, 1)
self.label_10 = QtGui.QLabel(self.normGroup)
self.label_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_10.setObjectName("label_10")
self.gridLayout_2.addWidget(self.label_10, 4, 5, 1, 1)
self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normTBlurSpin.setObjectName("normTBlurSpin")
self.gridLayout_2.addWidget(self.normTBlurSpin, 4, 6, 1, 1)
self.normStopLabel = QtGui.QLabel(self.normGroup)
self.normStopLabel.setObjectName("normStopLabel")
self.gridLayout_2.addWidget(self.normStopLabel, 3, 6, 1, 1)
self.normStartLabel = QtGui.QLabel(self.normGroup)
self.normStartLabel.setObjectName("normStartLabel")
self.gridLayout_2.addWidget(self.normStartLabel, 2, 6, 1, 1)
self.normOffRadio = QtGui.QRadioButton(self.normGroup)
self.normOffRadio.setChecked(True)
self.normOffRadio.setObjectName("normOffRadio")
self.gridLayout_2.addWidget(self.normOffRadio, 0, 3, 1, 1)
self.normTimeRangeCheck = QtGui.QCheckBox(self.normGroup)
self.normTimeRangeCheck.setObjectName("normTimeRangeCheck")
self.gridLayout_2.addWidget(self.normTimeRangeCheck, 1, 3, 1, 1)
self.normFrameCheck = QtGui.QCheckBox(self.normGroup)
self.normFrameCheck.setObjectName("normFrameCheck")
self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
self.gridLayout.addWidget(self.normGroup, 5, 0, 1, 4)
self.roiPlot = PlotWidget(self.splitter)
self.roiPlot.setMinimumSize(QtCore.QSize(0, 40))
self.roiPlot.setObjectName("roiPlot")
self.verticalLayout.addWidget(self.splitter)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Form", "B", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("Form", "W", None, QtGui.QApplication.UnicodeUTF8))
self.roiBtn.setText(QtGui.QApplication.translate("Form", "ROI", None, QtGui.QApplication.UnicodeUTF8))
self.normBtn.setText(QtGui.QApplication.translate("Form", "Norm", None, QtGui.QApplication.UnicodeUTF8))
self.normGroup.setTitle(QtGui.QApplication.translate("Form", "Normalization", None, QtGui.QApplication.UnicodeUTF8))
self.normSubtractRadio.setText(QtGui.QApplication.translate("Form", "Subtract", None, QtGui.QApplication.UnicodeUTF8))
self.normDivideRadio.setText(QtGui.QApplication.translate("Form", "Divide", None, QtGui.QApplication.UnicodeUTF8))
self.label_5.setText(QtGui.QApplication.translate("Form", "Operation:", None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate("Form", "Mean:", None, QtGui.QApplication.UnicodeUTF8))
self.label_4.setText(QtGui.QApplication.translate("Form", "Blur:", None, QtGui.QApplication.UnicodeUTF8))
self.normROICheck.setText(QtGui.QApplication.translate("Form", "ROI", None, QtGui.QApplication.UnicodeUTF8))
self.label_8.setText(QtGui.QApplication.translate("Form", "X", None, QtGui.QApplication.UnicodeUTF8))
self.label_9.setText(QtGui.QApplication.translate("Form", "Y", None, QtGui.QApplication.UnicodeUTF8))
self.label_10.setText(QtGui.QApplication.translate("Form", "T", None, QtGui.QApplication.UnicodeUTF8))
self.normStopLabel.setText(QtGui.QApplication.translate("Form", "Stop", None, QtGui.QApplication.UnicodeUTF8))
self.normStartLabel.setText(QtGui.QApplication.translate("Form", "Start", None, QtGui.QApplication.UnicodeUTF8))
self.normOffRadio.setText(QtGui.QApplication.translate("Form", "Off", None, QtGui.QApplication.UnicodeUTF8))
self.normTimeRangeCheck.setText(QtGui.QApplication.translate("Form", "Time range", None, QtGui.QApplication.UnicodeUTF8))
self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8))
from GraphicsView import GraphicsView
from PlotWidget import PlotWidget

341
ImageViewTemplate.ui Normal file
View File

@ -0,0 +1,341 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>757</width>
<height>495</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0" rowspan="3">
<widget class="GraphicsView" name="graphicsView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<zorder>normGroup</zorder>
<zorder>normGroup</zorder>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="blackSlider">
<property name="maximum">
<number>4096</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>410</number>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QSlider" name="whiteSlider">
<property name="maximum">
<number>4096</number>
</property>
<property name="value">
<number>4096</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>B</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>W</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QPushButton" name="roiBtn">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>ROI</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QSlider" name="timeSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QPushButton" name="normBtn">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Norm</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="4">
<widget class="QGroupBox" name="normGroup">
<property name="title">
<string>Normalization</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QRadioButton" name="normSubtractRadio">
<property name="text">
<string>Subtract</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="normDivideRadio">
<property name="text">
<string>Divide</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Operation:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Mean:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Blur:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="normROICheck">
<property name="text">
<string>ROI</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="6">
<widget class="QSlider" name="normStartSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="6">
<widget class="QSlider" name="normStopSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QDoubleSpinBox" name="normXBlurSpin"/>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>X</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Y</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QDoubleSpinBox" name="normYBlurSpin"/>
</item>
<item row="4" column="5">
<widget class="QLabel" name="label_10">
<property name="text">
<string>T</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="6">
<widget class="QLabel" name="normStopLabel">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QLabel" name="normStartLabel">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QRadioButton" name="normOffRadio">
<property name="text">
<string>Off</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="normTimeRangeCheck">
<property name="text">
<string>Time range</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="normFrameCheck">
<property name="text">
<string>Frame</string>
</property>
</widget>
</item>
<item row="4" column="6">
<widget class="QDoubleSpinBox" name="normTBlurSpin"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="PlotWidget" name="roiPlot" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>GraphicsView</class>
<extends>QWidget</extends>
<header>GraphicsView</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PlotWidget</class>
<extends>QWidget</extends>
<header>PlotWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

66
MultiPlotItem.py Normal file
View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""
MultiPlotItem.py - Graphics item used for displaying an array of PlotItems
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from numpy import ndarray
from graphicsItems import *
from PlotItem import *
try:
from metaarray import *
HAVE_METAARRAY = True
except:
raise
HAVE_METAARRAY = False
class MultiPlotItem(QtGui.QGraphicsWidget):
def __init__(self, parent=None):
QtGui.QGraphicsWidget.__init__(self, parent)
self.layout = QtGui.QGraphicsGridLayout()
self.layout.setContentsMargins(1,1,1,1)
self.setLayout(self.layout)
self.layout.setHorizontalSpacing(0)
self.layout.setVerticalSpacing(4)
self.plots = []
def plot(self, data):
#self.layout.clear()
self.plots = []
if HAVE_METAARRAY and isinstance(data, MetaArray):
if data.ndim != 2:
raise Exception("MultiPlot currently only accepts 2D MetaArray.")
ic = data.infoCopy()
ax = 0
for i in [0, 1]:
if 'cols' in ic[i]:
ax = i
break
#print "Plotting using axis %d as columns (%d plots)" % (ax, data.shape[ax])
for i in range(data.shape[ax]):
pi = PlotItem()
sl = [slice(None)] * 2
sl[ax] = i
pi.plot(data[tuple(sl)])
self.layout.addItem(pi, i, 0)
self.plots.append((pi, i, 0))
title = None
units = None
info = ic[ax]['cols'][i]
if 'title' in info:
title = info['title']
elif 'name' in info:
title = info['name']
if 'units' in info:
units = info['units']
pi.setLabel('left', text=title, units=units)
else:
raise Exception("Data type %s not (yet?) supported for MultiPlot." % type(data))

39
MultiPlotWidget.py Normal file
View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""
MultiPlotWidget.py - Convenience class--GraphicsView widget displaying a MultiPlotItem
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from GraphicsView import *
from MultiPlotItem import *
import exceptions
class MultiPlotWidget(GraphicsView):
"""Widget implementing a graphicsView with a single PlotItem inside."""
def __init__(self, parent=None):
GraphicsView.__init__(self, parent)
self.enableMouse(False)
self.mPlotItem = MultiPlotItem()
self.setCentralItem(self.mPlotItem)
## Explicitly wrap methods from mPlotItem
#for m in ['setData']:
#setattr(self, m, getattr(self.mPlotItem, m))
def __getattr__(self, attr): ## implicitly wrap methods from plotItem
if hasattr(self.mPlotItem, attr):
m = getattr(self.mPlotItem, attr)
if hasattr(m, '__call__'):
return m
raise exceptions.NameError(attr)
def widgetGroupInterface(self):
return (None, MultiPlotWidget.saveState, MultiPlotWidget.restoreState)
def saveState(self):
return {}
#return self.plotItem.saveState()
def restoreState(self, state):
pass
#return self.plotItem.restoreState(state)

2099
PIL_Fix/Image.py Executable file

File diff suppressed because it is too large Load Diff

11
PIL_Fix/README Normal file
View File

@ -0,0 +1,11 @@
The file Image.py is a drop-in replacement for the same file in PIL 1.1.6.
It adds support for reading 16-bit TIFF files and converting then to numpy arrays.
(I submitted the changes to the PIL folks long ago, but to my knowledge the code
is not being used by them.)
To use, copy this file into
/usr/lib/python2.6/dist-packages/PIL/
or
C:\Python26\lib\site-packages\PIL\
..or wherever your system keeps its python modules.

957
PlotItem.py Normal file
View File

@ -0,0 +1,957 @@
# -*- coding: utf-8 -*-
"""
PlotItem.py - Graphics item implementing a scalable ViewBox with plotting powers.
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
This class is one of the workhorses of pyqtgraph. It implements a graphics item with
plots, labels, and scales which can be viewed inside a QGraphicsScene. If you want
a widget that can be added to your GUI, see PlotWidget instead.
This class is very heavily featured:
- Automatically creates and manages PlotCurveItems
- Fast display and update of plots
- Manages zoom/pan ViewBox, scale, and label elements
- Automatic scaling when data changes
- Control panel with a huge feature set including averaging, decimation,
display, power spectrum, svg/png export, plot linking, and more.
"""
from graphicsItems import *
from plotConfigTemplate import *
from PyQt4 import QtGui, QtCore, QtSvg
import weakref
try:
from WidgetGroup import *
HAVE_WIDGETGROUP = True
except:
HAVE_WIDGETGROUP = False
try:
from metaarray import *
HAVE_METAARRAY = True
except:
HAVE_METAARRAY = False
class PlotItem(QtGui.QGraphicsWidget):
"""Plot graphics item that can be added to any graphics scene. Implements axis titles, scales, interactive viewbox."""
lastFileDir = None
managers = {}
def __init__(self, parent=None):
QtGui.QGraphicsWidget.__init__(self, parent)
## Set up control buttons
self.ctrlBtn = QtGui.QToolButton()
self.ctrlBtn.setText('?')
self.autoBtn = QtGui.QToolButton()
self.autoBtn.setText('A')
self.autoBtn.hide()
for b in [self.ctrlBtn, self.autoBtn]:
proxy = QtGui.QGraphicsProxyWidget(self)
proxy.setWidget(b)
b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
self.layout = QtGui.QGraphicsGridLayout()
self.layout.setContentsMargins(1,1,1,1)
self.setLayout(self.layout)
self.layout.setHorizontalSpacing(0)
self.layout.setVerticalSpacing(0)
self.vb = ViewBox()
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
self.layout.addItem(self.vb, 2, 1)
self.alpha = 1.0
self.autoAlpha = True
self.spectrumMode = False
self.autoScale = [True, True]
## Create and place scale items
self.scales = {
'top': {'item': ScaleItem(orientation='top', linkView=self.vb), 'pos': (1, 1)},
'bottom': {'item': ScaleItem(orientation='bottom', linkView=self.vb), 'pos': (3, 1)},
'left': {'item': ScaleItem(orientation='left', linkView=self.vb), 'pos': (2, 0)},
'right': {'item': ScaleItem(orientation='right', linkView=self.vb), 'pos': (2, 2)}
}
for k in self.scales:
self.layout.addItem(self.scales[k]['item'], *self.scales[k]['pos'])
## Create and place label items
#self.labels = {
#'title': {'item': LabelItem('title', size='11pt'), 'pos': (0, 2), 'text': ''},
#'top': {'item': LabelItem('top'), 'pos': (1, 2), 'text': '', 'units': '', 'unitPrefix': ''},
#'bottom': {'item': LabelItem('bottom'), 'pos': (5, 2), 'text': '', 'units': '', 'unitPrefix': ''},
#'left': {'item': LabelItem('left'), 'pos': (3, 0), 'text': '', 'units': '', 'unitPrefix': ''},
#'right': {'item': LabelItem('right'), 'pos': (3, 4), 'text': '', 'units': '', 'unitPrefix': ''}
#}
#self.labels['left']['item'].setAngle(-90)
#self.labels['right']['item'].setAngle(-90)
#for k in self.labels:
#self.layout.addItem(self.labels[k]['item'], *self.labels[k]['pos'])
self.titleLabel = LabelItem('', size='11pt')
self.layout.addItem(self.titleLabel, 0, 1)
self.setTitle(None) ## hide
for i in range(4):
self.layout.setRowPreferredHeight(i, 0)
self.layout.setRowMinimumHeight(i, 0)
self.layout.setRowSpacing(i, 0)
self.layout.setRowStretchFactor(i, 1)
for i in range(3):
self.layout.setColumnPreferredWidth(i, 0)
self.layout.setColumnMinimumWidth(i, 0)
self.layout.setColumnSpacing(i, 0)
self.layout.setColumnStretchFactor(i, 1)
self.layout.setRowStretchFactor(2, 100)
self.layout.setColumnStretchFactor(1, 100)
## Wrap a few methods from viewBox
for m in ['setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect']:
setattr(self, m, getattr(self.vb, m))
self.items = []
self.curves = []
self.paramList = {}
self.avgCurves = {}
### Set up context menu
w = QtGui.QWidget()
self.ctrl = c = Ui_Form()
c.setupUi(w)
dv = QtGui.QDoubleValidator(self)
self.ctrlMenu = QtGui.QMenu()
ac = QtGui.QWidgetAction(self)
ac.setDefaultWidget(w)
self.ctrlMenu.addAction(ac)
if HAVE_WIDGETGROUP:
self.stateGroup = WidgetGroup(self.ctrlMenu)
self.fileDialog = None
self.xLinkPlot = None
self.yLinkPlot = None
self.linksBlocked = False
#self.ctrlBtn.setFixedWidth(60)
self.setAcceptHoverEvents(True)
## Connect control widgets
QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
#QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
#QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)
QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
#QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
#QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
#QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
#QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
self.xLinkPlot = None
self.yLinkPlot = None
self.linksBlocked = False
self.manager = None
#self.showLabel('right', False)
#self.showLabel('top', False)
#self.showLabel('title', False)
#self.showLabel('left', False)
#self.showLabel('bottom', False)
self.showScale('right', False)
self.showScale('top', False)
self.showScale('left', True)
self.showScale('bottom', True)
def __del__(self):
if self.manager is not None:
self.manager.removeWidget(self.name)
def registerPlot(self, name):
self.name = name
win = str(self.window())
#print "register", name, win
if win not in PlotItem.managers:
PlotItem.managers[win] = PlotWidgetManager()
self.manager = PlotItem.managers[win]
self.manager.addWidget(self, name)
QtCore.QObject.connect(self.manager, QtCore.SIGNAL('widgetListChanged'), self.updatePlotList)
self.updatePlotList()
def updatePlotList(self):
"""Update the list of all plotWidgets in the "link" combos"""
#print "update plot list", self
try:
for sc in [self.ctrl.xLinkCombo, self.ctrl.yLinkCombo]:
current = str(sc.currentText())
sc.clear()
sc.addItem("")
if self.manager is not None:
for w in self.manager.listWidgets():
#print w
if w == self.name:
continue
sc.addItem(w)
except:
import gc
refs= gc.get_referrers(self)
print " error during update. Referrers are:", refs
raise
def viewGeometry(self):
"""return the screen geometry of the viewbox"""
v = self.scene().views()[0]
b = self.vb.mapRectToScene(self.vb.boundingRect())
wr = v.mapFromScene(b).boundingRect()
pos = v.pos()
wr.adjust(v.x(), v.y(), v.x(), v.y())
return wr
def viewChanged(self, *args):
self.emit(QtCore.SIGNAL('viewChanged'), *args)
def blockLink(self, b):
self.linksBlocked = b
def xLinkComboChanged(self):
self.setXLink(str(self.ctrl.xLinkCombo.currentText()))
def yLinkComboChanged(self):
self.setYLink(str(self.ctrl.yLinkCombo.currentText()))
def setXLink(self, plotName=None):
if self.manager is None:
return
if self.xLinkPlot is not None:
self.manager.unlinkX(self, self.xLinkPlot)
plot = self.manager.getWidget(plotName)
self.xLinkPlot = plot
if plot is not None:
self.setManualXScale()
self.manager.linkX(self, plot)
def setYLink(self, plotName=None):
if self.manager is None:
return
if self.yLinkPlot is not None:
self.manager.unlinkY(self, self.yLinkPlot)
plot = self.manager.getWidget(plotName)
self.yLinkPlot = plot
if plot is not None:
self.setManualYScale()
self.manager.linkY(self, plot)
def linkXChanged(self, plot):
#print "update from", plot
if self.linksBlocked:
return
pr = plot.vb.viewRect()
pg = plot.viewGeometry()
if pg is None:
#print " return early"
return
sg = self.viewGeometry()
upp = float(pr.width()) / pg.width()
x1 = pr.left() + (sg.x()-pg.x()) * upp
x2 = x1 + sg.width() * upp
plot.blockLink(True)
self.setXRange(x1, x2, padding=0)
plot.blockLink(False)
self.replot()
def linkYChanged(self, plot):
if self.linksBlocked:
return
pr = plot.vb.viewRect()
pg = plot.vb.boundingRect()
sg = self.vb.boundingRect()
upp = float(pr.height()) / pg.height()
y1 = pr.bottom() + (sg.y()-pg.y()) * upp
y2 = y1 + sg.height() * upp
plot.blockLink(True)
self.setYRange(y1, y2, padding=0)
plot.blockLink(False)
self.replot()
def avgToggled(self, b):
if b:
self.recomputeAverages()
for k in self.avgCurves:
self.avgCurves[k][1].setVisible(b)
def avgParamListClicked(self, item):
name = str(item.text())
self.paramList[name] = (item.checkState() == QtCore.Qt.Checked)
self.recomputeAverages()
def recomputeAverages(self):
for k in self.avgCurves:
self.removeItem(self.avgCurves[k][1])
#Qwt.QwtPlotCurve.detach(self.avgCurves[k][1])
self.avgCurves = {}
for c in self.curves:
self.addAvgCurve(c)
self.replot()
def addAvgCurve(self, curve):
"""Add a single curve into the pool of curves averaged together"""
### First determine the key of the curve to which this new data should be averaged
remKeys = []
addKeys = []
for i in range(self.ctrl.avgParamList.count()):
item = self.ctrl.avgParamList.item(i)
if item.checkState() == QtCore.Qt.Checked:
remKeys.append(str(item.text()))
else:
addKeys.append(str(item.text()))
if len(remKeys) < 1: ## In this case, there would be 1 average plot for each data plot; not useful.
return
p = curve.meta().copy()
for k in p:
if type(k) is tuple:
p['.'.join(k)] = p[k]
del p[k]
for rk in remKeys:
if rk in p:
del p[rk]
for ak in addKeys:
if ak not in p:
p[ak] = None
key = tuple(p.items())
### Create a new curve if needed
if key not in self.avgCurves:
plot = PlotCurveItem()
plot.setPen(mkPen([0, 200, 0]))
plot.setShadowPen(mkPen([0, 0, 0, 100], 3))
plot.setAlpha(1.0, False)
plot.setZValue(100)
self.addItem(plot)
#Qwt.QwtPlotCurve.attach(plot, self)
self.avgCurves[key] = [0, plot]
self.avgCurves[key][0] += 1
(n, plot) = self.avgCurves[key]
### Average data together
(x, y) = curve.getData()
if plot.yData is not None:
newData = plot.yData * (n-1) / float(n) + y * 1.0 / float(n)
plot.setData(plot.xData, newData)
else:
plot.setData(x, y)
def mouseCheckChanged(self):
state = [self.ctrl.xMouseCheck.isChecked(), self.ctrl.yMouseCheck.isChecked()]
self.vb.setMouseEnabled(*state)
def xRangeChanged(self, _, range):
self.ctrl.xMinText.setText('%0.5g' % range[0])
self.ctrl.xMaxText.setText('%0.5g' % range[1])
## automatically change unit scale
maxVal = max(abs(range[0]), abs(range[1]))
(scale, prefix) = siScale(maxVal)
#for l in ['top', 'bottom']:
#if self.getLabel(l).isVisible():
#self.setLabel(l, unitPrefix=prefix)
#self.getScale(l).setScale(scale)
#else:
#self.setLabel(l, unitPrefix='')
#self.getScale(l).setScale(1.0)
self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
def yRangeChanged(self, _, range):
self.ctrl.yMinText.setText('%0.5g' % range[0])
self.ctrl.yMaxText.setText('%0.5g' % range[1])
## automatically change unit scale
maxVal = max(abs(range[0]), abs(range[1]))
(scale, prefix) = siScale(maxVal)
#for l in ['left', 'right']:
#if self.getLabel(l).isVisible():
#self.setLabel(l, unitPrefix=prefix)
#self.getScale(l).setScale(scale)
#else:
#self.setLabel(l, unitPrefix='')
#self.getScale(l).setScale(1.0)
self.emit(QtCore.SIGNAL('yRangeChanged'), self, range)
def enableAutoScale(self):
self.ctrl.xAutoRadio.setChecked(True)
self.ctrl.yAutoRadio.setChecked(True)
self.autoBtn.hide()
self.replot()
def updateXScale(self):
"""Set plot to autoscale or not depending on state of radio buttons"""
if self.ctrl.xManualRadio.isChecked():
self.setManualXScale()
else:
self.setAutoXScale()
self.replot()
def updateYScale(self, b=False):
"""Set plot to autoscale or not depending on state of radio buttons"""
if self.ctrl.yManualRadio.isChecked():
self.setManualYScale()
else:
self.setAutoYScale()
self.replot()
def enableManualScale(self, v=[True, True]):
if v[0]:
self.autoScale[0] = False
self.ctrl.xManualRadio.setChecked(True)
#self.setManualXScale()
if v[1]:
self.autoScale[1] = False
self.ctrl.yManualRadio.setChecked(True)
#self.setManualYScale()
self.autoBtn.show()
#self.replot()
def setManualXScale(self):
self.autoScale[0] = False
x1 = float(self.ctrl.xMinText.text())
x2 = float(self.ctrl.xMaxText.text())
self.ctrl.xManualRadio.setChecked(True)
self.setXRange(x1, x2, padding=0)
self.autoBtn.show()
#self.replot()
def setManualYScale(self):
self.autoScale[1] = False
y1 = float(self.ctrl.yMinText.text())
y2 = float(self.ctrl.yMaxText.text())
self.ctrl.yManualRadio.setChecked(True)
self.setYRange(y1, y2, padding=0)
self.autoBtn.show()
#self.replot()
def setAutoXScale(self):
self.autoScale[0] = True
self.ctrl.xAutoRadio.setChecked(True)
#self.replot()
def setAutoYScale(self):
self.autoScale[1] = True
self.ctrl.yAutoRadio.setChecked(True)
#self.replot()
def addItem(self, item, *args):
self.items.append(item)
self.vb.addItem(item, *args)
def removeItem(self, item):
if not item in self.items:
return
self.items.remove(item)
self.vb.removeItem(item)
if item in self.curves:
self.curves.remove(item)
self.updateDecimation()
self.updateParamList()
QtCore.QObject.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
def clear(self):
for i in self.items[:]:
self.removeItem(i)
self.avgCurves = {}
def plot(self, data=None, x=None, clear=False, params=None, pen=None):
if clear:
self.clear()
if params is None:
params = {}
if HAVE_METAARRAY and isinstance(data, MetaArray):
curve = self._plotMetaArray(data, x=x)
elif isinstance(data, ndarray):
curve = self._plotArray(data, x=x)
elif isinstance(data, list):
if x is not None:
x = array(x)
curve = self._plotArray(array(data), x=x)
elif data is None:
curve = PlotCurveItem()
else:
raise Exception('Not sure how to plot object of type %s' % type(data))
#print data, curve
self.addCurve(curve, params)
if pen is not None:
curve.setPen(pen)
return curve
def addCurve(self, c, params=None):
c.setMeta(params)
self.curves.append(c)
#Qwt.QwtPlotCurve.attach(c, self)
self.addItem(c)
## configure curve for this plot
(alpha, auto) = self.alphaState()
c.setAlpha(alpha, auto)
c.setSpectrumMode(self.ctrl.powerSpectrumGroup.isChecked())
c.setPointMode(self.pointMode())
## Hide older plots if needed
self.updateDecimation()
## Add to average if needed
self.updateParamList()
if self.ctrl.averageGroup.isChecked():
self.addAvgCurve(c)
QtCore.QObject.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
self.plotChanged()
def plotChanged(self, curve=None):
## Recompute auto range if needed
for ax in [0, 1]:
if self.autoScale[ax]:
percentScale = [self.ctrl.xAutoPercentSpin.value(), self.ctrl.yAutoPercentSpin.value()][ax] * 0.01
mn = None
mx = None
for c in self.curves + [c[1] for c in self.avgCurves.values()]:
if not c.isVisible():
continue
cmn, cmx = c.getRange(ax, percentScale)
if mn is None or cmn < mn:
mn = cmn
if mx is None or cmx > mx:
mx = cmx
if mn is None or mx is None:
continue
if mn == mx:
mn -= 1
mx += 1
self.setRange(ax, mn, mx)
def replot(self):
self.plotChanged()
self.update()
def updateParamList(self):
self.ctrl.avgParamList.clear()
## Check to see that each parameter for each curve is present in the list
#print "\nUpdate param list", self
#print "paramList:", self.paramList
for c in self.curves:
#print " curve:", c
for p in c.meta().keys():
#print " param:", p
if type(p) is tuple:
p = '.'.join(p)
## If the parameter is not in the list, add it.
matches = self.ctrl.avgParamList.findItems(p, QtCore.Qt.MatchExactly)
#print " matches:", matches
if len(matches) == 0:
i = QtGui.QListWidgetItem(p)
if p in self.paramList and self.paramList[p] is True:
#print " set checked"
i.setCheckState(QtCore.Qt.Checked)
else:
#print " set unchecked"
i.setCheckState(QtCore.Qt.Unchecked)
self.ctrl.avgParamList.addItem(i)
else:
i = matches[0]
self.paramList[p] = (i.checkState() == QtCore.Qt.Checked)
#print "paramList:", self.paramList
def writeSvg(self, fileName=None):
print "writeSvg"
if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName)
self.svg = QtSvg.QSvgGenerator()
self.svg.setFileName(fileName)
self.svg.setSize(QtCore.QSize(self.size().width(), self.size().height()))
self.svg.setResolution(600)
painter = QtGui.QPainter(self.svg)
self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect()))
def writeImage(self, fileName=None):
if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName)
self.png = QtGui.QImage(int(self.size().width()), int(self.size().height()), QtGui.QImage.Format_ARGB32)
painter = QtGui.QPainter(self.png)
painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing)
self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect()))
painter.end()
self.png.save(fileName)
def saveState(self):
if not HAVE_WIDGETGROUP:
raise Exception("State save/restore requires WidgetGroup class.")
state = self.stateGroup.state()
state['paramList'] = self.paramList.copy()
#print "\nSAVE %s:\n" % str(self.name), state
#print "Saving state. averageGroup.isChecked(): %s state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
return state
def restoreState(self, state):
if not HAVE_WIDGETGROUP:
raise Exception("State save/restore requires WidgetGroup class.")
if 'paramList' in state:
self.paramList = state['paramList'].copy()
self.stateGroup.setState(state)
self.updateParamList()
#print "\nRESTORE %s:\n" % str(self.name), state
#print "Restoring state. averageGroup.isChecked(): %s state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
#avg = self.ctrl.averageGroup.isChecked()
#if avg != state['averageGroup']:
#print " WARNING: avgGroup is %s, should be %s" % (str(avg), str(state['averageGroup']))
def widgetGroupInterface(self):
return (None, PlotItem.saveState, PlotItem.restoreState)
def updateSpectrumMode(self, b=None):
if b is None:
b = self.ctrl.powerSpectrumGroup.isChecked()
for c in self.curves:
c.setSpectrumMode(b)
self.enableAutoScale()
self.recomputeAverages()
def updateDecimation(self):
if self.ctrl.maxTracesCheck.isChecked():
numCurves = self.ctrl.maxTracesSpin.value()
else:
numCurves = -1
curves = self.curves[:]
split = len(curves) - numCurves
for i in range(len(curves)):
if numCurves == -1 or i >= split:
curves[i].show()
else:
if self.ctrl.forgetTracesCheck.isChecked():
curves[i].free()
self.removeItem(curves[i])
else:
curves[i].hide()
def updateAlpha(self, *args):
(alpha, auto) = self.alphaState()
for c in self.curves:
c.setAlpha(alpha**2, auto)
#self.replot(autoRange=False)
def alphaState(self):
enabled = self.ctrl.alphaGroup.isChecked()
auto = self.ctrl.autoAlphaCheck.isChecked()
alpha = float(self.ctrl.alphaSlider.value()) / self.ctrl.alphaSlider.maximum()
if auto:
alpha = 1.0 ## should be 1/number of overlapping plots
if not enabled:
auto = False
alpha = 1.0
return (alpha, auto)
def pointMode(self):
if self.ctrl.pointsGroup.isChecked():
if self.ctrl.autoPointsCheck.isChecked():
mode = None
else:
mode = True
else:
mode = False
return mode
#def mousePressEvent(self, ev):
#self.mousePos = array([ev.pos().x(), ev.pos().y()])
#self.pressPos = self.mousePos.copy()
#QtGui.QGraphicsWidget.mousePressEvent(self, ev)
## NOTE: we will only receive move/release events if we run ev.accept()
#print 'press'
#def mouseReleaseEvent(self, ev):
#pos = array([ev.pos().x(), ev.pos().y()])
#print 'release'
#if sum(abs(self.pressPos - pos)) < 3: ## Detect click
#if ev.button() == QtCore.Qt.RightButton:
#print 'popup'
#self.ctrlMenu.popup(self.mapToGlobal(ev.pos()))
#self.mousePos = pos
#QtGui.QGraphicsWidget.mouseReleaseEvent(self, ev)
def resizeEvent(self, ev):
self.ctrlBtn.move(0, self.size().height() - self.ctrlBtn.size().height())
self.autoBtn.move(self.ctrlBtn.width(), self.size().height() - self.autoBtn.size().height())
def hoverMoveEvent(self, ev):
self.mousePos = ev.pos()
self.mouseScreenPos = ev.screenPos()
def ctrlBtnClicked(self):
#print self.mousePos
self.ctrlMenu.popup(self.mouseScreenPos)
#def _checkLabelKey(self, key):
#if key not in self.labels:
#raise Exception("Label '%s' not found. Labels are: %s" % (key, str(self.labels.keys())))
def getLabel(self, key):
pass
#self._checkLabelKey(key)
#return self.labels[key]['item']
def _checkScaleKey(self, key):
if key not in self.scales:
raise Exception("Scale '%s' not found. Scales are: %s" % (key, str(self.scales.keys())))
def getScale(self, key):
self._checkScaleKey(key)
return self.scales[key]['item']
def setLabel(self, key, text=None, units=None, unitPrefix=None, **args):
self.getScale(key).setLabel(text=text, units=units, unitPrefix=unitPrefix, **args)
#if text is not None:
#self.labels[key]['text'] = text
#if units != None:
#self.labels[key]['units'] = units
#if unitPrefix != None:
#self.labels[key]['unitPrefix'] = unitPrefix
#text = self.labels[key]['text']
#units = self.labels[key]['units']
#unitPrefix = self.labels[key]['unitPrefix']
#if text is not '' or units is not '':
#l = self.getLabel(key)
#l.setText("%s (%s%s)" % (text, unitPrefix, units), **args)
#self.showLabel(key)
def showLabel(self, key, show=True):
self.getScale(key).showLabel(show)
#l = self.getLabel(key)
#p = self.labels[key]['pos']
#if show:
#l.show()
#if key in ['left', 'right']:
#self.layout.setColumnFixedWidth(p[1], l.size().width())
#l.setMaximumWidth(20)
#else:
#self.layout.setRowFixedHeight(p[0], l.size().height())
#l.setMaximumHeight(20)
#else:
#l.hide()
#if key in ['left', 'right']:
#self.layout.setColumnFixedWidth(p[1], 0)
#l.setMaximumWidth(0)
#else:
#self.layout.setRowFixedHeight(p[0], 0)
#l.setMaximumHeight(0)
def setTitle(self, title=None, **args):
if title is None:
self.titleLabel.setVisible(False)
self.layout.setRowFixedHeight(0, 0)
self.titleLabel.setMaximumHeight(0)
else:
self.titleLabel.setMaximumHeight(30)
self.layout.setRowFixedHeight(0, 30)
self.titleLabel.setVisible(True)
self.titleLabel.setText(title, **args)
def showScale(self, key, show=True):
s = self.getScale(key)
p = self.scales[key]['pos']
if show:
s.show()
#if key in ['left', 'right']:
#self.layout.setColumnFixedWidth(p[1], s.maximumWidth())
##s.setMaximumWidth(40)
#else:
#self.layout.setRowFixedHeight(p[0], s.maximumHeight())
#s.setMaximumHeight(20)
else:
s.hide()
#if key in ['left', 'right']:
#self.layout.setColumnFixedWidth(p[1], 0)
##s.setMaximumWidth(0)
#else:
#self.layout.setRowFixedHeight(p[0], 0)
#s.setMaximumHeight(0)
def _plotArray(self, arr, x=None):
if arr.ndim != 1:
raise Exception("Array must be 1D to plot (shape is %s)" % arr.shape)
if x is None:
x = arange(arr.shape[0])
if x.ndim != 1:
raise Exception("X array must be 1D to plot (shape is %s)" % x.shape)
c = PlotCurveItem(arr, x=x)
return c
def _plotMetaArray(self, arr, x=None, autoLabel=True):
inf = arr.infoCopy()
if arr.ndim != 1:
raise Exception('can only automatically plot 1 dimensional arrays.')
## create curve
try:
xv = arr.xvals(0)
#print 'xvals:', xv
except:
if x is None:
xv = arange(arr.shape[0])
else:
xv = x
c = PlotCurveItem()
c.setData(x=xv, y=arr.view(ndarray))
if autoLabel:
name = arr._info[0].get('name', None)
units = arr._info[0].get('units', None)
self.setLabel('bottom', text=name, units=units)
name = arr._info[1].get('name', None)
units = arr._info[1].get('units', None)
self.setLabel('left', text=name, units=units)
return c
def saveSvgClicked(self):
self.fileDialog = QtGui.QFileDialog()
#if PlotItem.lastFileDir is not None:
#self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
self.fileDialog.show()
QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
#def svgFileSelected(self, fileName):
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
#self.writeSvg(str(fileName))
def saveImgClicked(self):
self.fileDialog = QtGui.QFileDialog()
#if PlotItem.lastFileDir is not None:
#self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
self.fileDialog.show()
QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage)
#def imgFileSelected(self, fileName):
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
#self.writeImage(str(fileName))
class PlotWidgetManager(QtCore.QObject):
"""Used for managing communication between PlotWidgets"""
def __init__(self):
QtCore.QObject.__init__(self)
self.widgets = weakref.WeakValueDictionary() # Don't keep PlotWidgets around just because they are listed here
def addWidget(self, w, name):
self.widgets[name] = w
self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
def removeWidget(self, name):
if name in self.widgets:
del self.widgets[name]
self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
def listWidgets(self):
return self.widgets.keys()
def getWidget(self, name):
if name not in self.widgets:
return None
else:
return self.widgets[name]
def linkX(self, p1, p2):
QtCore.QObject.connect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
QtCore.QObject.connect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
p1.linkXChanged(p2)
#p2.setManualXScale()
def unlinkX(self, p1, p2):
QtCore.QObject.disconnect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
QtCore.QObject.disconnect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
def linkY(self, p1, p2):
QtCore.QObject.connect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
QtCore.QObject.connect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)
p1.linkYChanged(p2)
#p2.setManualYScale()
def unlinkY(self, p1, p2):
QtCore.QObject.disconnect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
QtCore.QObject.disconnect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)

43
PlotWidget.py Normal file
View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
"""
PlotWidget.py - Convenience class--GraphicsView widget displaying a single PlotItem
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from GraphicsView import *
from PlotItem import *
import exceptions
class PlotWidget(GraphicsView):
"""Widget implementing a graphicsView with a single PlotItem inside."""
def __init__(self, parent=None):
GraphicsView.__init__(self, parent)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.enableMouse(False)
self.plotItem = PlotItem()
self.setCentralItem(self.plotItem)
## Explicitly wrap methods from plotItem
for m in ['addItem', 'autoRange', 'clear']:
setattr(self, m, getattr(self.plotItem, m))
QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged)
def __getattr__(self, attr): ## implicitly wrap methods from plotItem
if hasattr(self.plotItem, attr):
m = getattr(self.plotItem, attr)
if hasattr(m, '__call__'):
return m
raise exceptions.NameError(attr)
def viewChanged(self, *args):
self.emit(QtCore.SIGNAL('viewChanged'), *args)
def widgetGroupInterface(self):
return (None, PlotWidget.saveState, PlotWidget.restoreState)
def saveState(self):
return self.plotItem.saveState()
def restoreState(self, state):
return self.plotItem.restoreState(state)

118
Point.py Normal file
View File

@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
"""
Point.py - Extension of QPointF which adds a few missing methods.
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from PyQt4 import QtCore
from math import acos
def clip(x, mn, mx):
if x > mx:
return mx
if x < mn:
return mn
return x
class Point(QtCore.QPointF):
"""Extension of QPointF which adds a few missing methods."""
def __init__(self, *args):
if len(args) == 1:
if hasattr(args[0], '__getitem__'):
QtCore.QPointF.__init__(self, float(args[0][0]), float(args[0][1]))
return
elif type(args[0]) in [float, int]:
QtCore.QPointF.__init__(self, float(args[0]), float(args[0]))
return
elif len(args) == 2:
QtCore.QPointF.__init__(self, args[0], args[1])
return
QtCore.QPointF.__init__(self, *args)
def __getitem__(self, i):
if i == 0:
return self.x()
elif i == 1:
return self.y()
else:
raise IndexError("Point has no index %d" % i)
def __setitem__(self, i, x):
if i == 0:
return self.setX(x)
elif i == 1:
return self.setY(x)
else:
raise IndexError("Point has no index %d" % i)
def __radd__(self, a):
return self._math_('__radd__', a)
def __add__(self, a):
return self._math_('__add__', a)
def __rsub__(self, a):
return self._math_('__rsub__', a)
def __sub__(self, a):
return self._math_('__sub__', a)
def __rmul__(self, a):
return self._math_('__rmul__', a)
def __mul__(self, a):
return self._math_('__mul__', a)
def __rdiv__(self, a):
return self._math_('__rdiv__', a)
def __div__(self, a):
return self._math_('__div__', a)
def __rpow__(self, a):
return self._math_('__rpow__', a)
def __pow__(self, a):
return self._math_('__pow__', a)
def _math_(self, op, x):
#print "point math:", op
try:
return Point(getattr(QtCore.QPointF, op)(self, x))
except:
x = Point(x)
return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1]))
def length(self):
return (self[0]**2 + self[1]**2) ** 0.5
def angle(self, a):
n1 = self.length()
n2 = a.length()
if n1 == 0. or n2 == 0.:
return None
ang = acos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0))
c = self.cross(a)
if c > 0:
ang *= -1.
return ang
def dot(self, a):
a = Point(a)
return self[0]*a[0] + self[1]*a[1]
def cross(self, a):
a = Point(a)
return self[0]*a[1] - self[1]*a[0]
def __repr__(self):
return "Point(%f, %f)" % (self[0], self[1])
def min(self):
return min(self[0], self[1])
def max(self):
return max(self[0], self[1])

0
__init__.py Normal file
View File

91
functions.py Normal file
View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
"""
functions.py - Miscellaneous functions with no other home
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from PyQt4 import QtGui
from numpy import clip, floor, log
## Copied from acq4/lib/util/functions
SI_PREFIXES = u'yzafpnµm kMGTPEZY'
def siScale(x, minVal=1e-25):
"""Return the recommended scale factor and SI prefix string for x."""
if abs(x) < minVal:
m = 0
x = 0
else:
m = int(clip(floor(log(abs(x))/log(1000)), -9.0, 9.0))
if m == 0:
pref = ''
elif m < -8 or m > 8:
pref = 'e%d' % (m*3)
else:
pref = SI_PREFIXES[m+8]
p = .001**m
return (p, pref)
def mkPen(color=None, hsv=None, width=1, style=None, cosmetic=True):
if color is None:
color = [255, 255, 255]
if hsv is not None:
color = hsvColor(*hsv)
else:
color = mkColor(color)
pen = QtGui.QPen(QtGui.QBrush(color), width)
pen.setCosmetic(cosmetic)
if style is not None:
pen.setStyle(style)
return pen
def hsvColor(h, s=1.0, v=1.0, a=1.0):
c = QtGui.QColor()
c.setHsvF(h, s, v, a)
return c
def mkColor(*args):
"""make a QColor from a variety of argument types"""
err = 'Not sure how to make a color from "%s"' % str(args)
if len(args) == 1:
if isinstance(args[0], QtGui.QColor):
return QtGui.QColor(args[0])
elif hasattr(args[0], '__len__'):
if len(args[0]) == 3:
(r, g, b) = args[0]
a = 255
elif len(args[0]) == 4:
(r, g, b, a) = args[0]
else:
raise Exception(err)
else:
raise Exception(err)
elif len(args) == 3:
(r, g, b) = args
a = 255
elif len(args) == 4:
(r, g, b, a) = args
else:
raise Exception(err)
return QtGui.QColor(r, g, b, a)
def colorStr(c):
"""Generate a hex string code from a QColor"""
return ('%02x'*4) % (c.red(), c.blue(), c.green(), c.alpha())
def intColor(ind, colors=9, values=3, maxValue=255, minValue=150, sat=255):
"""Creates a QColor from a single index. Useful for stepping through a predefined list of colors."""
colors = int(colors)
values = int(values)
ind = int(ind) % (colors * values)
indh = ind % colors
indv = ind / colors
v = minValue + indv * ((maxValue-minValue) / (values-1))
h = (indh * 360) / colors
c = QtGui.QColor()
c.setHsv(h, sat, v)
return c

1481
graphicsItems.py Normal file

File diff suppressed because it is too large Load Diff

32
graphicsWindows.py Normal file
View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
"""
graphicsWindows.py - Convenience classes which create a new window with PlotWidget or ImageView.
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from PyQt4 import QtCore, QtGui
from PlotWidget import *
from ImageView import *
class PlotWindow(QtGui.QMainWindow):
def __init__(self, title=None):
QtGui.QMainWindow.__init__(self)
self.cw = PlotWidget()
self.setCentralWidget(self.cw)
for m in ['plot', 'autoRange', 'addItem', 'setLabel', 'clear']:
setattr(self, m, getattr(self.cw, m))
if title is not None:
self.setWindowTitle(title)
self.show()
class ImageWindow(QtGui.QMainWindow):
def __init__(self, title=None):
QtGui.QMainWindow.__init__(self)
self.cw = ImageView()
self.setCentralWidget(self.cw)
for m in ['setImage', 'autoRange', 'addItem']:
setattr(self, m, getattr(self.cw, m))
if title is not None:
self.setWindowTitle(title)
self.show()

7
license.txt Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2010 Luke Campagnola ('luke.campagnola@%s.com' % 'gmail')
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

277
plotConfigTemplate.py Normal file
View File

@ -0,0 +1,277 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'plotConfigTemplate.ui'
#
# Created: Tue Jan 12 14:23:16 2010
# by: PyQt4 UI code generator 4.5.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(210, 320)
Form.setMaximumSize(QtCore.QSize(250, 350))
self.gridLayout_3 = QtGui.QGridLayout(Form)
self.gridLayout_3.setMargin(0)
self.gridLayout_3.setSpacing(0)
self.gridLayout_3.setObjectName("gridLayout_3")
self.tabWidget = QtGui.QTabWidget(Form)
self.tabWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.tabWidget.setObjectName("tabWidget")
self.tab = QtGui.QWidget()
self.tab.setObjectName("tab")
self.verticalLayout = QtGui.QVBoxLayout(self.tab)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setMargin(0)
self.verticalLayout.setObjectName("verticalLayout")
self.groupBox = QtGui.QGroupBox(self.tab)
self.groupBox.setObjectName("groupBox")
self.gridLayout = QtGui.QGridLayout(self.groupBox)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.gridLayout.setObjectName("gridLayout")
self.xManualRadio = QtGui.QRadioButton(self.groupBox)
self.xManualRadio.setObjectName("xManualRadio")
self.gridLayout.addWidget(self.xManualRadio, 0, 0, 1, 1)
self.xMinText = QtGui.QLineEdit(self.groupBox)
self.xMinText.setObjectName("xMinText")
self.gridLayout.addWidget(self.xMinText, 0, 1, 1, 1)
self.xMaxText = QtGui.QLineEdit(self.groupBox)
self.xMaxText.setObjectName("xMaxText")
self.gridLayout.addWidget(self.xMaxText, 0, 2, 1, 1)
self.xAutoRadio = QtGui.QRadioButton(self.groupBox)
self.xAutoRadio.setChecked(True)
self.xAutoRadio.setObjectName("xAutoRadio")
self.gridLayout.addWidget(self.xAutoRadio, 1, 0, 1, 1)
self.xAutoPercentSpin = QtGui.QSpinBox(self.groupBox)
self.xAutoPercentSpin.setEnabled(True)
self.xAutoPercentSpin.setMinimum(1)
self.xAutoPercentSpin.setMaximum(100)
self.xAutoPercentSpin.setSingleStep(1)
self.xAutoPercentSpin.setProperty("value", QtCore.QVariant(100))
self.xAutoPercentSpin.setObjectName("xAutoPercentSpin")
self.gridLayout.addWidget(self.xAutoPercentSpin, 1, 1, 1, 2)
self.xLinkCombo = QtGui.QComboBox(self.groupBox)
self.xLinkCombo.setObjectName("xLinkCombo")
self.gridLayout.addWidget(self.xLinkCombo, 2, 1, 1, 2)
self.xMouseCheck = QtGui.QCheckBox(self.groupBox)
self.xMouseCheck.setChecked(True)
self.xMouseCheck.setObjectName("xMouseCheck")
self.gridLayout.addWidget(self.xMouseCheck, 3, 1, 1, 1)
self.xLogCheck = QtGui.QCheckBox(self.groupBox)
self.xLogCheck.setObjectName("xLogCheck")
self.gridLayout.addWidget(self.xLogCheck, 3, 0, 1, 1)
self.label = QtGui.QLabel(self.groupBox)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
self.verticalLayout.addWidget(self.groupBox)
self.groupBox_2 = QtGui.QGroupBox(self.tab)
self.groupBox_2.setObjectName("groupBox_2")
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
self.gridLayout_2.setMargin(0)
self.gridLayout_2.setSpacing(0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.yManualRadio = QtGui.QRadioButton(self.groupBox_2)
self.yManualRadio.setObjectName("yManualRadio")
self.gridLayout_2.addWidget(self.yManualRadio, 0, 0, 1, 1)
self.yMinText = QtGui.QLineEdit(self.groupBox_2)
self.yMinText.setObjectName("yMinText")
self.gridLayout_2.addWidget(self.yMinText, 0, 1, 1, 1)
self.yMaxText = QtGui.QLineEdit(self.groupBox_2)
self.yMaxText.setObjectName("yMaxText")
self.gridLayout_2.addWidget(self.yMaxText, 0, 2, 1, 1)
self.yAutoRadio = QtGui.QRadioButton(self.groupBox_2)
self.yAutoRadio.setChecked(True)
self.yAutoRadio.setObjectName("yAutoRadio")
self.gridLayout_2.addWidget(self.yAutoRadio, 1, 0, 1, 1)
self.yAutoPercentSpin = QtGui.QSpinBox(self.groupBox_2)
self.yAutoPercentSpin.setEnabled(True)
self.yAutoPercentSpin.setMinimum(1)
self.yAutoPercentSpin.setMaximum(100)
self.yAutoPercentSpin.setSingleStep(1)
self.yAutoPercentSpin.setProperty("value", QtCore.QVariant(100))
self.yAutoPercentSpin.setObjectName("yAutoPercentSpin")
self.gridLayout_2.addWidget(self.yAutoPercentSpin, 1, 1, 1, 2)
self.yLinkCombo = QtGui.QComboBox(self.groupBox_2)
self.yLinkCombo.setObjectName("yLinkCombo")
self.gridLayout_2.addWidget(self.yLinkCombo, 2, 1, 1, 2)
self.yMouseCheck = QtGui.QCheckBox(self.groupBox_2)
self.yMouseCheck.setChecked(True)
self.yMouseCheck.setObjectName("yMouseCheck")
self.gridLayout_2.addWidget(self.yMouseCheck, 3, 1, 1, 1)
self.yLogCheck = QtGui.QCheckBox(self.groupBox_2)
self.yLogCheck.setObjectName("yLogCheck")
self.gridLayout_2.addWidget(self.yLogCheck, 3, 0, 1, 1)
self.label_2 = QtGui.QLabel(self.groupBox_2)
self.label_2.setObjectName("label_2")
self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1)
self.verticalLayout.addWidget(self.groupBox_2)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtGui.QWidget()
self.tab_2.setObjectName("tab_2")
self.verticalLayout_2 = QtGui.QVBoxLayout(self.tab_2)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setMargin(0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.powerSpectrumGroup = QtGui.QGroupBox(self.tab_2)
self.powerSpectrumGroup.setCheckable(True)
self.powerSpectrumGroup.setChecked(False)
self.powerSpectrumGroup.setObjectName("powerSpectrumGroup")
self.verticalLayout_2.addWidget(self.powerSpectrumGroup)
self.decimateGroup = QtGui.QGroupBox(self.tab_2)
self.decimateGroup.setCheckable(True)
self.decimateGroup.setObjectName("decimateGroup")
self.gridLayout_4 = QtGui.QGridLayout(self.decimateGroup)
self.gridLayout_4.setMargin(0)
self.gridLayout_4.setSpacing(0)
self.gridLayout_4.setObjectName("gridLayout_4")
self.manualDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
self.manualDecimateRadio.setObjectName("manualDecimateRadio")
self.gridLayout_4.addWidget(self.manualDecimateRadio, 0, 0, 1, 1)
self.decimateSpin = QtGui.QSpinBox(self.decimateGroup)
self.decimateSpin.setObjectName("decimateSpin")
self.gridLayout_4.addWidget(self.decimateSpin, 0, 1, 1, 1)
self.autoDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
self.autoDecimateRadio.setChecked(True)
self.autoDecimateRadio.setObjectName("autoDecimateRadio")
self.gridLayout_4.addWidget(self.autoDecimateRadio, 1, 0, 1, 1)
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
self.maxTracesCheck.setObjectName("maxTracesCheck")
self.gridLayout_4.addWidget(self.maxTracesCheck, 2, 0, 1, 1)
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
self.maxTracesSpin.setObjectName("maxTracesSpin")
self.gridLayout_4.addWidget(self.maxTracesSpin, 2, 1, 1, 1)
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
self.forgetTracesCheck.setObjectName("forgetTracesCheck")
self.gridLayout_4.addWidget(self.forgetTracesCheck, 3, 0, 1, 2)
self.verticalLayout_2.addWidget(self.decimateGroup)
self.averageGroup = QtGui.QGroupBox(self.tab_2)
self.averageGroup.setCheckable(True)
self.averageGroup.setChecked(False)
self.averageGroup.setObjectName("averageGroup")
self.gridLayout_5 = QtGui.QGridLayout(self.averageGroup)
self.gridLayout_5.setMargin(0)
self.gridLayout_5.setSpacing(0)
self.gridLayout_5.setObjectName("gridLayout_5")
self.avgParamList = QtGui.QListWidget(self.averageGroup)
self.avgParamList.setObjectName("avgParamList")
self.gridLayout_5.addWidget(self.avgParamList, 0, 0, 1, 1)
self.verticalLayout_2.addWidget(self.averageGroup)
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = QtGui.QWidget()
self.tab_3.setObjectName("tab_3")
self.verticalLayout_3 = QtGui.QVBoxLayout(self.tab_3)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.alphaGroup = QtGui.QGroupBox(self.tab_3)
self.alphaGroup.setCheckable(True)
self.alphaGroup.setObjectName("alphaGroup")
self.horizontalLayout = QtGui.QHBoxLayout(self.alphaGroup)
self.horizontalLayout.setObjectName("horizontalLayout")
self.autoAlphaCheck = QtGui.QCheckBox(self.alphaGroup)
self.autoAlphaCheck.setChecked(False)
self.autoAlphaCheck.setObjectName("autoAlphaCheck")
self.horizontalLayout.addWidget(self.autoAlphaCheck)
self.alphaSlider = QtGui.QSlider(self.alphaGroup)
self.alphaSlider.setMaximum(1000)
self.alphaSlider.setProperty("value", QtCore.QVariant(1000))
self.alphaSlider.setOrientation(QtCore.Qt.Horizontal)
self.alphaSlider.setObjectName("alphaSlider")
self.horizontalLayout.addWidget(self.alphaSlider)
self.verticalLayout_3.addWidget(self.alphaGroup)
self.gridGroup = QtGui.QGroupBox(self.tab_3)
self.gridGroup.setCheckable(True)
self.gridGroup.setObjectName("gridGroup")
self.verticalLayout_4 = QtGui.QVBoxLayout(self.gridGroup)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.gridAlphaSlider = QtGui.QSlider(self.gridGroup)
self.gridAlphaSlider.setMaximum(255)
self.gridAlphaSlider.setProperty("value", QtCore.QVariant(70))
self.gridAlphaSlider.setOrientation(QtCore.Qt.Horizontal)
self.gridAlphaSlider.setObjectName("gridAlphaSlider")
self.verticalLayout_4.addWidget(self.gridAlphaSlider)
self.verticalLayout_3.addWidget(self.gridGroup)
self.pointsGroup = QtGui.QGroupBox(self.tab_3)
self.pointsGroup.setCheckable(True)
self.pointsGroup.setObjectName("pointsGroup")
self.verticalLayout_5 = QtGui.QVBoxLayout(self.pointsGroup)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.autoPointsCheck = QtGui.QCheckBox(self.pointsGroup)
self.autoPointsCheck.setChecked(True)
self.autoPointsCheck.setObjectName("autoPointsCheck")
self.verticalLayout_5.addWidget(self.autoPointsCheck)
self.verticalLayout_3.addWidget(self.pointsGroup)
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem)
self.tabWidget.addTab(self.tab_3, "")
self.tab_4 = QtGui.QWidget()
self.tab_4.setObjectName("tab_4")
self.gridLayout_7 = QtGui.QGridLayout(self.tab_4)
self.gridLayout_7.setObjectName("gridLayout_7")
spacerItem1 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem1, 0, 0, 1, 1)
self.gridLayout_6 = QtGui.QGridLayout()
self.gridLayout_6.setObjectName("gridLayout_6")
self.saveSvgBtn = QtGui.QPushButton(self.tab_4)
self.saveSvgBtn.setObjectName("saveSvgBtn")
self.gridLayout_6.addWidget(self.saveSvgBtn, 0, 0, 1, 1)
self.saveImgBtn = QtGui.QPushButton(self.tab_4)
self.saveImgBtn.setObjectName("saveImgBtn")
self.gridLayout_6.addWidget(self.saveImgBtn, 1, 0, 1, 1)
self.saveMaBtn = QtGui.QPushButton(self.tab_4)
self.saveMaBtn.setObjectName("saveMaBtn")
self.gridLayout_6.addWidget(self.saveMaBtn, 2, 0, 1, 1)
self.gridLayout_7.addLayout(self.gridLayout_6, 0, 1, 1, 1)
spacerItem2 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem2, 0, 2, 1, 1)
spacerItem3 = QtGui.QSpacerItem(20, 211, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem3, 1, 1, 1, 1)
self.tabWidget.addTab(self.tab_4, "")
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
self.retranslateUi(Form)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.groupBox.setTitle(QtGui.QApplication.translate("Form", "X Axis", None, QtGui.QApplication.UnicodeUTF8))
self.xManualRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8))
self.xMinText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8))
self.xMaxText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8))
self.xAutoRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
self.xAutoPercentSpin.setSuffix(QtGui.QApplication.translate("Form", "%", None, QtGui.QApplication.UnicodeUTF8))
self.xMouseCheck.setText(QtGui.QApplication.translate("Form", "Mouse", None, QtGui.QApplication.UnicodeUTF8))
self.xLogCheck.setText(QtGui.QApplication.translate("Form", "Log", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Form", "Link with:", None, QtGui.QApplication.UnicodeUTF8))
self.groupBox_2.setTitle(QtGui.QApplication.translate("Form", "Y Axis", None, QtGui.QApplication.UnicodeUTF8))
self.yManualRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8))
self.yMinText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8))
self.yMaxText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8))
self.yAutoRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
self.yAutoPercentSpin.setSuffix(QtGui.QApplication.translate("Form", "%", None, QtGui.QApplication.UnicodeUTF8))
self.yMouseCheck.setText(QtGui.QApplication.translate("Form", "Mouse", None, QtGui.QApplication.UnicodeUTF8))
self.yLogCheck.setText(QtGui.QApplication.translate("Form", "Log", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("Form", "Link with:", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QtGui.QApplication.translate("Form", "Scale", None, QtGui.QApplication.UnicodeUTF8))
self.powerSpectrumGroup.setTitle(QtGui.QApplication.translate("Form", "Power Spectrum", None, QtGui.QApplication.UnicodeUTF8))
self.decimateGroup.setTitle(QtGui.QApplication.translate("Form", "Decimate", None, QtGui.QApplication.UnicodeUTF8))
self.manualDecimateRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8))
self.autoDecimateRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
self.maxTracesCheck.setText(QtGui.QApplication.translate("Form", "Max Traces:", None, QtGui.QApplication.UnicodeUTF8))
self.forgetTracesCheck.setText(QtGui.QApplication.translate("Form", "Forget hidden traces", None, QtGui.QApplication.UnicodeUTF8))
self.averageGroup.setTitle(QtGui.QApplication.translate("Form", "Average", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QtGui.QApplication.translate("Form", "Data", None, QtGui.QApplication.UnicodeUTF8))
self.alphaGroup.setTitle(QtGui.QApplication.translate("Form", "Alpha", None, QtGui.QApplication.UnicodeUTF8))
self.autoAlphaCheck.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
self.gridGroup.setTitle(QtGui.QApplication.translate("Form", "Grid", None, QtGui.QApplication.UnicodeUTF8))
self.pointsGroup.setTitle(QtGui.QApplication.translate("Form", "Points", None, QtGui.QApplication.UnicodeUTF8))
self.autoPointsCheck.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QtGui.QApplication.translate("Form", "Display", None, QtGui.QApplication.UnicodeUTF8))
self.saveSvgBtn.setText(QtGui.QApplication.translate("Form", "SVG", None, QtGui.QApplication.UnicodeUTF8))
self.saveImgBtn.setText(QtGui.QApplication.translate("Form", "Image", None, QtGui.QApplication.UnicodeUTF8))
self.saveMaBtn.setText(QtGui.QApplication.translate("Form", "MetaArray", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), QtGui.QApplication.translate("Form", "Save", None, QtGui.QApplication.UnicodeUTF8))

527
plotConfigTemplate.ui Normal file
View File

@ -0,0 +1,527 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>210</width>
<height>320</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Scale</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>X Axis</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="xManualRadio">
<property name="text">
<string>Manual</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="xMinText">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="xMaxText">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="xAutoRadio">
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QSpinBox" name="xAutoPercentSpin">
<property name="enabled">
<bool>true</bool>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="xLinkCombo"/>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="xMouseCheck">
<property name="text">
<string>Mouse</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="xLogCheck">
<property name="text">
<string>Log</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Link with:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Y Axis</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="yManualRadio">
<property name="text">
<string>Manual</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="yMinText">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="yMaxText">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="yAutoRadio">
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QSpinBox" name="yAutoPercentSpin">
<property name="enabled">
<bool>true</bool>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="yLinkCombo"/>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="yMouseCheck">
<property name="text">
<string>Mouse</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="yLogCheck">
<property name="text">
<string>Log</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Link with:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="powerSpectrumGroup">
<property name="title">
<string>Power Spectrum</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="decimateGroup">
<property name="title">
<string>Decimate</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="manualDecimateRadio">
<property name="text">
<string>Manual</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="decimateSpin"/>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="autoDecimateRadio">
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="maxTracesCheck">
<property name="text">
<string>Max Traces:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="maxTracesSpin"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="forgetTracesCheck">
<property name="text">
<string>Forget hidden traces</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="averageGroup">
<property name="title">
<string>Average</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QListWidget" name="avgParamList"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Display</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="alphaGroup">
<property name="title">
<string>Alpha</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="autoAlphaCheck">
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="alphaSlider">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gridGroup">
<property name="title">
<string>Grid</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QSlider" name="gridAlphaSlider">
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>70</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="pointsGroup">
<property name="title">
<string>Points</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="autoPointsCheck">
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Save</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>59</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QPushButton" name="saveSvgBtn">
<property name="text">
<string>SVG</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="saveImgBtn">
<property name="text">
<string>Image</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="saveMaBtn">
<property name="text">
<string>MetaArray</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>59</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>211</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

21
test_ImageView.py Normal file
View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from ImageView import *
from numpy import random
from PyQt4 import QtCore, QtGui
from scipy.ndimage import *
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
imv = ImageView()
win.setCentralWidget(imv)
win.show()
img = gaussian_filter(random.random((200, 200)), (5, 5)) * 5
data = random.random((100, 200, 200))
data += img
for i in range(data.shape[0]):
data[i] += exp(-(2.*i)/data.shape[0])
data += 10
imv.setImage(data)

17
test_MultiPlotWidget.py Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from scipy import random
from numpy import linspace
from PyQt4 import QtGui, QtCore
from MultiPlotWidget import *
from metaarray import *
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
pw = MultiPlotWidget()
mw.setCentralWidget(pw)
mw.show()
ma = MetaArray(random.random((3, 1000)), info=[{'name': 'Signal', 'cols': [{'name': 'Col1'}, {'name': 'Col2'}, {'name': 'Col3'}]}, {'name': 'Time', 'vals': linspace(0., 1., 1000)}])
pw.plot(ma)

80
test_PlotWidget.py Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from scipy import random
from PyQt4 import QtGui, QtCore
from PlotWidget import *
from graphicsItems import *
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
cw = QtGui.QWidget()
mw.setCentralWidget(cw)
l = QtGui.QVBoxLayout()
cw.setLayout(l)
pw = PlotWidget()
l.addWidget(pw)
pw2 = PlotWidget()
l.addWidget(pw2)
pw3 = PlotWidget()
l.addWidget(pw3)
pw.registerPlot('Plot1')
pw2.registerPlot('Plot2')
#p1 = PlotCurveItem()
#pw.addItem(p1)
p1 = pw.plot()
rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 1))
rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100)))
pw.addItem(rect)
pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(255, 255, 255, 10)), 5)
pen.setCosmetic(True)
#pen.setJoinStyle(QtCore.Qt.MiterJoin)
p1.setShadowPen(pen)
p1.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255, 50)))
#l1 = QtGui.QGraphicsLineItem(0, 2, 2, 3)
#l1.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
l2 = InfiniteLine(pw, 1.5, 0)
#l3 = InfiniteLine(pw, [1.5, 1.5], 45)
#pw.addItem(l1)
pw.addItem(l2)
#pw.addItem(l3)
pw3.plot(array([100000]*100))
mw.show()
def rand(n):
data = random.random(n)
data[int(n*0.1):int(n*0.13)] += .5
data[int(n*0.18)] += 2
data[int(n*0.1):int(n*0.13)] *= 5
data[int(n*0.18)] *= 20
return data, arange(n, n+len(data)) / float(n)
def updateData():
yd, xd = rand(10000)
p1.updateData(yd, x=xd)
yd, xd = rand(10000)
updateData()
pw.autoRange()
t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
t.start(50)
for i in range(0, 5):
for j in range(0, 3):
yd, xd = rand(10000)
pw2.plot(yd*(j+1), xd, params={'iter': i, 'val': j})
#app.exec_()

88
test_ROItypes.py Executable file
View File

@ -0,0 +1,88 @@
#!/usr/bin/python -i
# -*- coding: utf-8 -*-
from scipy import zeros
from graphicsWindows import *
from graphicsItems import *
from widgets import *
from PlotWidget import *
from PyQt4 import QtCore, QtGui
qapp = QtGui.QApplication([])
#i = PlotWindow(array([0,1,2,1,2]), parent=None, title='')
class Win(QtGui.QMainWindow):
pass
w = Win()
v = GraphicsView()
v.invertY(True)
v.setAspectLocked(True)
v.enableMouse(True)
v.autoPixelScale = False
w.setCentralWidget(v)
#s = QtGui.QGraphicsScene()
#v.setScene(s)
s = v.scene()
#p = Plot(array([0,2,1,3,4]), copy=False)
#s.addItem(p)
arr = ones((100, 100), dtype=float)
arr[45:55, 45:55] = 0
arr[25, :] = 5
arr[:, 25] = 5
arr[75, :] = 5
arr[:, 75] = 5
arr[50, :] = 10
arr[:, 50] = 10
im1 = ImageItem(arr)
im2 = ImageItem(arr)
s.addItem(im1)
s.addItem(im2)
im2.moveBy(110, 20)
im3 = ImageItem()
s.addItem(im3)
im3.moveBy(0, 130)
im3.setZValue(10)
im4 = ImageItem()
s.addItem(im4)
im4.moveBy(110, 130)
im4.setZValue(10)
#g = Grid(view=v, bounds=QtCore.QRectF(0.1, 0.1, 0.8, 0.8))
#g = Grid(view=v)
#s.addItem(g)
#wid = RectROI([0, 0], [2, 2], maxBounds=QtCore.QRectF(-1, -1, 5, 5))
roi = TestROI([0, 0], [20, 20], maxBounds=QtCore.QRectF(-10, -10, 230, 140))
s.addItem(roi)
roi2 = LineROI([0, 0], [20, 20], width=5)
s.addItem(roi2)
mlroi = MultiLineROI([[0, 50], [50, 60], [60, 30]], width=5)
s.addItem(mlroi)
elroi = EllipseROI([110, 10], [30, 20])
s.addItem(elroi)
croi = CircleROI([110, 50], [20, 20])
s.addItem(croi)
def updateImg(roi):
global im1, im2, im3, im4, arr
arr1 = roi.getArrayRegion(arr, img=im1)
im3.updateImage(arr1, autoRange=True)
arr2 = roi.getArrayRegion(arr, img=im2)
im4.updateImage(arr2, autoRange=True)
roi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi))
roi2.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi2))
croi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(croi))
elroi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(elroi))
mlroi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(mlroi))
v.setRange(QtCore.QRect(-2, -2, 220, 220))
w.show()

100
test_viewBox.py Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from scipy import random
from PyQt4 import QtGui, QtCore
from GraphicsView import *
from graphicsItems import *
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
cw = QtGui.QWidget()
vl = QtGui.QVBoxLayout()
cw.setLayout(vl)
mw.setCentralWidget(cw)
mw.show()
mw.resize(800, 600)
gv = GraphicsView(cw)
gv.enableMouse(False)
#w1 = QtGui.QGraphicsWidget()
l = QtGui.QGraphicsGridLayout()
l.setHorizontalSpacing(0)
l.setVerticalSpacing(0)
vb = ViewBox()
p1 = PlotCurveItem()
#gv.scene().addItem(vb)
vb.addItem(p1)
vl.addWidget(gv)
rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 1))
rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100)))
vb.addItem(rect)
l.addItem(vb, 0, 2)
gv.centralWidget.setLayout(l)
xScale = ScaleItem(orientation='bottom', linkView=vb)
l.addItem(xScale, 1, 2)
yScale = ScaleItem(orientation='left', linkView=vb)
l.addItem(yScale, 0, 1)
xLabel = LabelItem(u"<span style='color: #ff0000; font-weight: bold'>X</span> <i>Axis</i> <span style='font-size: 6pt'>(μV)</span>", html=True, color='CCC')
l.setRowFixedHeight(2, 20)
l.setRowFixedHeight(1, 40)
l.addItem(xLabel, 2, 2)
yLabel = LabelItem("Y Axis", color=QtGui.QColor(200, 200, 200))
yLabel.setAngle(90)
l.setColumnFixedWidth(0, 20)
l.setColumnFixedWidth(1, 60)
l.addItem(yLabel, 0, 0)
#grid = GridItem(gv)
#vb.addItem(grid)
#gv.scene().addItem(w1)
#w1.setGeometry(0, 0, 1, 1)
#c1 = Qwt.QwtPlotCurve()
#c1.setData(range(len(data)), data)
#c1.attach(p1)
#c2 = PlotCurve()
#c2.setData([1,2,3,4,5,6,7,8], [1,2,10,4,3,2,4,1])
#c2.attach(p2)
def rand(n):
data = random.random(n)
data[int(n*0.1):int(n*0.13)] += .5
data[int(n*0.18)] += 2
data[int(n*0.1):int(n*0.13)] *= 5
data[int(n*0.18)] *= 20
#c1.setData(range(len(data)), data)
return data, arange(n, n+len(data)) / float(n)
def updateData():
yd, xd = rand(10000)
p1.updateData(yd, x=xd)
#vb.setRange(p1.boundingRect())
#p1.plot(yd, x=xd, clear=True)
yd, xd = rand(10000)
#p2.plot(yd * 1000, x=xd)
#for i in [1,2]:
#for j in range(3):
#yd, xd = rand(1000)
#p3.plot(yd * 100000 * i, x=xd, params={'repetitions': j, 'scale': i})
updateData()
vb.autoRange()
t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
t.start(50)

837
widgets.py Normal file
View File

@ -0,0 +1,837 @@
# -*- coding: utf-8 -*-
"""
widgets.py - Interactive graphics items for GraphicsView (ROI widgets)
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
Implements a series of graphics items which display movable/scalable/rotatable shapes
for use as region-of-interest markers. ROI class automatically handles extraction
of array data from ImageItems.
"""
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor
from numpy.linalg import norm
import scipy.ndimage as ndimage
from Point import *
from math import cos, sin
def rectStr(r):
return "[%f, %f] + [%f, %f]" % (r.x(), r.y(), r.width(), r.height())
## Multiple inheritance not allowed in PyQt. Retarded workaround:
class QObjectWorkaround:
def __init__(self):
self._qObj_ = QtCore.QObject()
def __getattr__(self, attr):
if attr == '_qObj_':
raise Exception("QObjectWorkaround not initialized!")
return getattr(self._qObj_, attr)
def connect(self, *args):
return QtCore.QObject.connect(self._qObj_, *args)
class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
def __init__(self, pos, size=Point(1, 1), angle=0.0, invertible=False, maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None):
QObjectWorkaround.__init__(self)
QtGui.QGraphicsItem.__init__(self, parent)
pos = Point(pos)
size = Point(size)
self.aspectLocked = False
self.translatable = True
self.pen = QtGui.QPen(QtGui.QColor(255, 255, 255))
self.handlePen = QtGui.QPen(QtGui.QColor(150, 255, 255))
self.handles = []
self.state = {'pos': pos, 'size': size, 'angle': angle}
self.lastState = None
self.setPos(pos)
self.rotate(-angle)
self.setZValue(10)
self.handleSize = 4
self.invertible = invertible
self.maxBounds = maxBounds
self.snapSize = snapSize
self.translateSnap = translateSnap
self.rotateSnap = rotateSnap
self.scaleSnap = scaleSnap
self.setFlag(self.ItemIsSelectable, True)
def setZValue(self, z):
QtGui.QGraphicsItem.setZValue(self, z)
for h in self.handles:
h['item'].setZValue(z+1)
def sceneBounds(self):
return self.sceneTransform().mapRect(self.boundingRect())
def parentBounds(self):
return self.mapToParent(self.boundingRect()).boundingRect()
def setPen(self, pen):
self.pen = pen
self.update()
def setPos(self, pos, update=True):
pos = Point(pos)
self.state['pos'] = pos
QtGui.QGraphicsItem.setPos(self, pos)
if update:
self.handleChange()
def setSize(self, size, update=True):
size = Point(size)
self.prepareGeometryChange()
self.state['size'] = size
if update:
self.updateHandles()
self.handleChange()
def addTranslateHandle(self, pos, axes=None, item=None):
pos = Point(pos)
return self.addHandle({'type': 't', 'pos': pos, 'item': item})
def addScaleHandle(self, pos, center, axes=None, item=None):
pos = Point(pos)
center = Point(center)
info = {'type': 's', 'center': center, 'pos': pos, 'item': item}
if pos.x() == center.x():
info['xoff'] = True
if pos.y() == center.y():
info['yoff'] = True
return self.addHandle(info)
def addRotateHandle(self, pos, center, item=None):
pos = Point(pos)
center = Point(center)
return self.addHandle({'type': 'r', 'center': center, 'pos': pos, 'item': item})
def addScaleRotateHandle(self, pos, center, item=None):
pos = Point(pos)
center = Point(center)
if pos[0] != center[0] and pos[1] != center[1]:
raise Exception("Scale/rotate handles must have either the same x or y coordinate as their center point.")
return self.addHandle({'type': 'sr', 'center': center, 'pos': pos, 'item': item})
def addHandle(self, info):
if not info.has_key('item') or info['item'] is None:
#print "BEFORE ADD CHILD:", self.childItems()
h = Handle(self.handleSize, typ=info['type'], pen=self.handlePen, parent=self)
#print "AFTER ADD CHILD:", self.childItems()
h.setPos(info['pos'] * self.state['size'])
info['item'] = h
else:
h = info['item']
iid = len(self.handles)
h.connectROI(self, iid)
#h.mouseMoveEvent = lambda ev: self.pointMoveEvent(iid, ev)
#h.mousePressEvent = lambda ev: self.pointPressEvent(iid, ev)
#h.mouseReleaseEvent = lambda ev: self.pointReleaseEvent(iid, ev)
self.handles.append(info)
h.setZValue(self.zValue()+1)
#if self.isSelected():
#h.show()
#else:
#h.hide()
return h
def mapSceneToParent(self, pt):
return self.mapToParent(self.mapFromScene(pt))
def setSelected(self, s):
QtGui.QGraphicsItem.setSelected(self, s)
#print "select", self, s
if s:
for h in self.handles:
h['item'].show()
else:
for h in self.handles:
h['item'].hide()
def mousePressEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton:
self.setSelected(True)
if self.translatable:
self.cursorOffset = self.scenePos() - ev.scenePos()
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
ev.accept()
else:
ev.ignore()
def mouseMoveEvent(self, ev):
#print "mouse move", ev.pos()
if self.translatable:
snap = None
if self.translateSnap or (ev.modifiers() & QtCore.Qt.ControlModifier):
snap = Point(self.snapSize, self.snapSize)
newPos = ev.scenePos() + self.cursorOffset
newPos = self.mapSceneToParent(newPos)
self.translate(newPos - self.pos(), snap=snap)
def mouseReleaseEvent(self, ev):
if self.translatable:
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
def pointPressEvent(self, pt, ev):
#print "press"
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
#self.pressPos = self.mapFromScene(ev.scenePos())
#self.pressHandlePos = self.handles[pt]['item'].pos()
def pointReleaseEvent(self, pt, ev):
#print "release"
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
def stateCopy(self):
sc = {}
sc['pos'] = Point(self.state['pos'])
sc['size'] = Point(self.state['size'])
sc['angle'] = self.state['angle']
return sc
def updateHandles(self):
#print "update", self.handles
for h in self.handles:
#print " try", h
if h['item'] in self.childItems():
p = h['pos']
h['item'].setPos(h['pos'] * self.state['size'])
#else:
#print " Not child!", self.childItems()
def checkPointMove(self, pt, pos, modifiers):
return True
def pointMoveEvent(self, pt, ev):
self.movePoint(pt, ev.scenePos(), ev.modifiers())
def movePoint(self, pt, pos, modifiers=QtCore.Qt.KeyboardModifier()):
#print "movePoint", pos
newState = self.stateCopy()
h = self.handles[pt]
#p0 = self.mapToScene(h['item'].pos())
p0 = self.mapToScene(h['pos'] * self.state['size'])
p1 = Point(pos)
p0 = self.mapSceneToParent(p0)
p1 = self.mapSceneToParent(p1)
if h.has_key('center'):
c = h['center']
cs = c * self.state['size']
#lpOrig = h['pos'] -
#lp0 = self.mapFromScene(p0) - cs
#lp1 = self.mapFromScene(p1) - cs
lp0 = self.mapFromParent(p0) - cs
lp1 = self.mapFromParent(p1) - cs
if h['type'] == 't':
#p0 = Point(self.mapToScene(h['item'].pos()))
#p1 = Point(pos + self.mapToScene(self.pressHandlePos) - self.mapToScene(self.pressPos))
snap = None
if self.translateSnap or (modifiers & QtCore.Qt.ControlModifier):
snap = Point(self.snapSize, self.snapSize)
self.translate(p1-p0, snap=snap, update=False)
elif h['type'] == 's':
#c = h['center']
#cs = c * self.state['size']
#p1 = (self.mapFromScene(ev.scenePos()) + self.pressHandlePos - self.pressPos) - cs
if h['center'][0] == h['pos'][0]:
lp1[0] = 0
if h['center'][1] == h['pos'][1]:
lp1[1] = 0
if self.scaleSnap or (modifiers & QtCore.Qt.ControlModifier):
lp1[0] = round(lp1[0] / self.snapSize) * self.snapSize
lp1[1] = round(lp1[1] / self.snapSize) * self.snapSize
hs = h['pos'] - c
if hs[0] == 0:
hs[0] = 1
if hs[1] == 0:
hs[1] = 1
newSize = lp1 / hs
if newSize[0] == 0:
newSize[0] = newState['size'][0]
if newSize[1] == 0:
newSize[1] = newState['size'][1]
if not self.invertible:
if newSize[0] < 0:
newSize[0] = newState['size'][0]
if newSize[1] < 0:
newSize[1] = newState['size'][1]
if self.aspectLocked:
newSize[0] = newSize[1]
s0 = c * self.state['size']
s1 = c * newSize
cc = self.mapToParent(s0 - s1) - self.mapToParent(Point(0, 0))
newState['size'] = newSize
newState['pos'] = newState['pos'] + cc
if self.maxBounds is not None:
r = self.stateRect(newState)
if not self.maxBounds.contains(r):
return
self.setPos(newState['pos'], update=False)
self.prepareGeometryChange()
self.state = newState
self.updateHandles()
elif h['type'] == 'r':
#newState = self.stateCopy()
#c = h['center']
#cs = c * self.state['size']
#p0 = Point(h['item'].pos()) - cs
#p1 = (self.mapFromScene(ev.scenePos()) + self.pressHandlePos - self.pressPos) - cs
if lp1.length() == 0 or lp0.length() == 0:
return
ang = newState['angle'] + lp0.angle(lp1)
if ang is None:
return
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
ang = round(ang / (pi/12.)) * (pi/12.)
tr = QtGui.QTransform()
tr.rotate(-ang * 180. / pi)
cc = self.mapToParent(cs) - (tr.map(cs) + self.state['pos'])
newState['angle'] = ang
newState['pos'] = newState['pos'] + cc
if self.maxBounds is not None:
r = self.stateRect(newState)
if not self.maxBounds.contains(r):
return
self.setTransform(tr)
self.setPos(newState['pos'], update=False)
self.state = newState
elif h['type'] == 'sr':
#newState = self.stateCopy()
if h['center'][0] == h['pos'][0]:
scaleAxis = 1
else:
scaleAxis = 0
#c = h['center']
#cs = c * self.state['size']
#p0 = Point(h['item'].pos()) - cs
#p1 = (self.mapFromScene(ev.scenePos()) + self.pressHandlePos - self.pressPos) - cs
if lp1.length() == 0 or lp0.length() == 0:
return
ang = newState['angle'] + lp0.angle(lp1)
if ang is None:
return
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
ang = round(ang / (pi/12.)) * (pi/12.)
hs = abs(h['pos'][scaleAxis] - c[scaleAxis])
newState['size'][scaleAxis] = lp1.length() / hs
if self.scaleSnap or (modifiers & QtCore.Qt.ControlModifier):
newState['size'][scaleAxis] = round(newState['size'][scaleAxis] / self.snapSize) * self.snapSize
if newState['size'][scaleAxis] == 0:
newState['size'][scaleAxis] = 1
c1 = c * newState['size']
tr = QtGui.QTransform()
tr.rotate(-ang * 180. / pi)
cc = self.mapToParent(cs) - (tr.map(c1) + self.state['pos'])
newState['angle'] = ang
newState['pos'] = newState['pos'] + cc
if self.maxBounds is not None:
r = self.stateRect(newState)
if not self.maxBounds.contains(r):
return
self.setTransform(tr)
self.setPos(newState['pos'], update=False)
self.prepareGeometryChange()
self.state = newState
self.updateHandles()
self.handleChange()
def handleChange(self):
changed = False
if self.lastState is None:
changed = True
else:
for k in self.state.keys():
if self.state[k] != self.lastState[k]:
#print "state %s has changed; emit signal" % k
changed = True
self.lastState = self.stateCopy()
if changed:
self.update()
self.emit(QtCore.SIGNAL('regionChanged'), self)
def scale(self, s, center=[0,0]):
c = self.mapToScene(Point(center) * self.state['size'])
self.prepareGeometryChange()
self.state['size'] = self.state['size'] * s
c1 = self.mapToScene(Point(center) * self.state['size'])
self.state['pos'] = self.state['pos'] + c - c1
self.setPos(self.state['pos'])
self.updateHandles()
def translate(self, *args, **kargs):
"""accepts either (x, y, snap) or ([x,y], snap) as arguments"""
if 'snap' not in kargs:
snap = None
else:
snap = kargs['snap']
if len(args) == 1:
pt = args[0]
else:
pt = args
newState = self.stateCopy()
newState['pos'] = newState['pos'] + pt
if snap != None:
newState['pos'][0] = round(newState['pos'][0] / snap[0]) * snap[0]
newState['pos'][1] = round(newState['pos'][1] / snap[1]) * snap[1]
#d = ev.scenePos() - self.mapToScene(self.pressPos)
if self.maxBounds is not None:
r = self.stateRect(newState)
#r0 = self.sceneTransform().mapRect(self.boundingRect())
d = Point(0,0)
if self.maxBounds.left() > r.left():
d[0] = self.maxBounds.left() - r.left()
elif self.maxBounds.right() < r.right():
d[0] = self.maxBounds.right() - r.right()
if self.maxBounds.top() > r.top():
d[1] = self.maxBounds.top() - r.top()
elif self.maxBounds.bottom() < r.bottom():
d[1] = self.maxBounds.bottom() - r.bottom()
newState['pos'] += d
self.state['pos'] = newState['pos']
self.setPos(self.state['pos'])
#if 'update' not in kargs or kargs['update'] is True:
self.handleChange()
def stateRect(self, state):
r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1])
tr = QtGui.QTransform()
tr.rotate(-state['angle'] * 180 / pi)
r = tr.mapRect(r)
return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1])
def boundingRect(self):
return QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1])
def paint(self, p, opt, widget):
r = self.boundingRect()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen)
p.drawRect(r)
def getArraySlice(self, data, img, axes=(0,1), returnSlice=True):
"""Return a tuple of slice objects that can be used to slice the region from data covered by this ROI.
Also returns the transform which maps the ROI into data coordinates.
If returnSlice is set to False, the function returns a pair of tuples with the values that would have
been used to generate the slice objects. ((ax0Start, ax0Stop), (ax1Start, ax1Stop))"""
#print "getArraySlice"
## Determine shape of array along ROI axes
dShape = (data.shape[axes[0]], data.shape[axes[1]])
#print " dshape", dShape
## Determine transform that maps ROI bounding box to image coordinates
tr = self.sceneTransform() * img.sceneTransform().inverted()[0]
## Modify transform to scale from image coords to data coords
#m = QtGui.QTransform()
tr.scale(float(dShape[0]) / img.width(), float(dShape[1]) / img.height())
#tr = tr * m
## Transform ROI bounds into data bounds
dataBounds = tr.mapRect(self.boundingRect())
#print " boundingRect:", self.boundingRect()
#print " dataBounds:", dataBounds
## Intersect transformed ROI bounds with data bounds
intBounds = dataBounds.intersect(QtCore.QRectF(0, 0, dShape[0], dShape[1]))
#print " intBounds:", intBounds
## Determine index values to use when referencing the array.
bounds = (
(int(min(intBounds.left(), intBounds.right())), int(1+max(intBounds.left(), intBounds.right()))),
(int(min(intBounds.bottom(), intBounds.top())), int(1+max(intBounds.bottom(), intBounds.top())))
)
#print " bounds:", bounds
if returnSlice:
## Create slice objects
sl = [slice(None)] * data.ndim
sl[axes[0]] = slice(*bounds[0])
sl[axes[1]] = slice(*bounds[1])
return tuple(sl), tr
else:
return bounds, tr
def getArrayRegion(self, data, img, axes=(0,1)):
## transpose data so x and y are the first 2 axes
trAx = range(0, data.ndim)
trAx.remove(axes[0])
trAx.remove(axes[1])
tr1 = tuple(axes) + tuple(trAx)
arr = data.transpose(tr1)
## Determine the minimal area of the data we will need
(dataBounds, roiDataTransform) = self.getArraySlice(data, img, returnSlice=False, axes=axes)
## Pad data boundaries by 1px if possible
dataBounds = (
(max(dataBounds[0][0]-1, 0), min(dataBounds[0][1]+1, arr.shape[0])),
(max(dataBounds[1][0]-1, 0), min(dataBounds[1][1]+1, arr.shape[1]))
)
## Extract minimal data from array
arr1 = arr[dataBounds[0][0]:dataBounds[0][1], dataBounds[1][0]:dataBounds[1][1]]
## Update roiDataTransform to reflect this extraction
roiDataTransform *= QtGui.QTransform().translate(-dataBounds[0][0], -dataBounds[1][0])
### (roiDataTransform now maps from ROI coords to extracted data coords)
## Rotate array
if abs(self.state['angle']) > 1e-5:
arr2 = ndimage.rotate(arr1, self.state['angle'] * 180 / pi, order=1)
## update data transforms to reflect this rotation
rot = QtGui.QTransform().rotate(self.state['angle'] * 180 / pi)
roiDataTransform *= rot
## The rotation also causes a shift which must be accounted for:
dataBound = QtCore.QRectF(0, 0, arr1.shape[0], arr1.shape[1])
rotBound = rot.mapRect(dataBound)
roiDataTransform *= QtGui.QTransform().translate(-rotBound.left(), -rotBound.top())
else:
arr2 = arr1
### Shift off partial pixels
# 1. map ROI into current data space
roiBounds = roiDataTransform.mapRect(self.boundingRect())
# 2. Determine amount to shift data
shift = (int(roiBounds.left()) - roiBounds.left(), int(roiBounds.bottom()) - roiBounds.bottom())
if abs(shift[0]) > 1e-6 or abs(shift[1]) > 1e-6:
# 3. pad array with 0s before shifting
arr2a = zeros((arr2.shape[0]+2, arr2.shape[1]+2) + arr2.shape[2:], dtype=arr2.dtype)
arr2a[1:-1, 1:-1] = arr2
# 4. shift array and udpate transforms
arr3 = ndimage.shift(arr2a, shift + (0,)*(arr2.ndim-2), order=1)
roiDataTransform *= QtGui.QTransform().translate(1+shift[0], 1+shift[1])
else:
arr3 = arr2
### Extract needed region from rotated/shifted array
# 1. map ROI into current data space (round these values off--they should be exact integer values at this point)
roiBounds = roiDataTransform.mapRect(self.boundingRect())
#print self, roiBounds.height()
#import traceback
#traceback.print_stack()
roiBounds = QtCore.QRect(round(roiBounds.left()), round(roiBounds.top()), round(roiBounds.width()), round(roiBounds.height()))
#2. intersect ROI with data bounds
dataBounds = roiBounds.intersect(QtCore.QRect(0, 0, arr3.shape[0], arr3.shape[1]))
#3. Extract data from array
db = dataBounds
bounds = (
(db.left(), db.right()+1),
(db.top(), db.bottom()+1)
)
arr4 = arr3[bounds[0][0]:bounds[0][1], bounds[1][0]:bounds[1][1]]
### Create zero array in size of ROI
arr5 = zeros((roiBounds.width(), roiBounds.height()) + arr4.shape[2:], dtype=arr4.dtype)
## Fill array with ROI data
orig = Point(dataBounds.topLeft() - roiBounds.topLeft())
subArr = arr5[orig[0]:orig[0]+arr4.shape[0], orig[1]:orig[1]+arr4.shape[1]]
subArr[:] = arr4[:subArr.shape[0], :subArr.shape[1]]
## figure out the reverse transpose order
tr2 = array(tr1)
for i in range(0, len(tr2)):
tr2[tr1[i]] = i
tr2 = tuple(tr2)
## Untranspose array before returning
return arr5.transpose(tr2)
class Handle(QtGui.QGraphicsItem):
def __init__(self, radius, typ=None, pen=QtGui.QPen(QtGui.QColor(200, 200, 220)), parent=None):
#print " create item with parent", parent
self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
QtGui.QGraphicsItem.__init__(self, parent)
self.setZValue(11)
self.roi = []
self.radius = radius
self.typ = typ
self.prepareGeometryChange()
self.pen = pen
if typ == 't':
self.sides = 4
self.startAng = pi/4
elif typ == 's':
self.sides = 4
self.startAng = 0
elif typ == 'r':
self.sides = 12
self.startAng = 0
elif typ == 'sr':
self.sides = 12
self.startAng = 0
else:
self.sides = 4
self.startAng = pi/4
def connectROI(self, roi, i):
self.roi.append((roi, i))
def boundingRect(self):
return self.bounds
def mousePressEvent(self, ev):
print "handle press"
if ev.button() != QtCore.Qt.LeftButton:
ev.ignore()
return
self.cursorOffset = self.scenePos() - ev.scenePos()
for r in self.roi:
r[0].pointPressEvent(r[1], ev)
print " accepted."
ev.accept()
def mouseReleaseEvent(self, ev):
#print "release"
for r in self.roi:
r[0].pointReleaseEvent(r[1], ev)
def mouseMoveEvent(self, ev):
#print "handle mouseMove", ev.pos()
pos = ev.scenePos() + self.cursorOffset
self.movePoint(pos, ev.modifiers())
def movePoint(self, pos, modifiers=QtCore.Qt.KeyboardModifier()):
for r in self.roi:
if not r[0].checkPointMove(r[1], pos, modifiers):
return
#print "point moved; inform %d ROIs" % len(self.roi)
for r in self.roi:
r[0].movePoint(r[1], pos, modifiers)
def paint(self, p, opt, widget):
m = p.transform()
mi = m.inverted()[0]
## Determine length of unit vector in painter's coords
size = mi.map(Point(self.radius, self.radius)) - mi.map(Point(0, 0))
size = (size.x()*size.x() + size.y() * size.y()) ** 0.5
bounds = QtCore.QRectF(-size, -size, size*2, size*2)
if bounds != self.bounds:
self.bounds = bounds
self.prepareGeometryChange()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen)
ang = self.startAng
dt = 2*pi / self.sides
for i in range(0, self.sides):
x1 = size * cos(ang)
y1 = size * sin(ang)
x2 = size * cos(ang+dt)
y2 = size * sin(ang+dt)
ang += dt
p.drawLine(Point(x1, y1), Point(x2, y2))
class TestROI(ROI):
def __init__(self, pos, size, **args):
#QtGui.QGraphicsRectItem.__init__(self, pos[0], pos[1], size[0], size[1])
ROI.__init__(self, pos, size, **args)
#self.addTranslateHandle([0, 0])
self.addTranslateHandle([0.5, 0.5])
self.addScaleHandle([1, 1], [0, 0])
self.addScaleHandle([0, 0], [1, 1])
self.addScaleRotateHandle([1, 0.5], [0.5, 0.5])
self.addScaleHandle([0.5, 1], [0.5, 0.5])
self.addRotateHandle([1, 0], [0, 0])
self.addRotateHandle([0, 1], [1, 1])
class RectROI(ROI):
def __init__(self, pos, size, centered=False, sideScalers=False, **args):
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
ROI.__init__(self, pos, size, **args)
if centered:
center = [0.5, 0.5]
else:
center = [0, 0]
#self.addTranslateHandle(center)
self.addScaleHandle([1, 1], center)
if sideScalers:
self.addScaleHandle([1, 0.5], [center[0], 0.5])
self.addScaleHandle([0.5, 1], [0.5, center[1]])
class LineROI(ROI):
def __init__(self, pos1, pos2, width, **args):
pos1 = Point(pos1)
pos2 = Point(pos2)
d = pos2-pos1
l = d.length()
ang = Point(1, 0).angle(d)
c = Point(-width/2. * sin(ang), -width/2. * cos(ang))
pos1 = pos1 + c
ROI.__init__(self, pos1, size=Point(l, width), angle=ang*180/pi, **args)
self.addScaleRotateHandle([0, 0.5], [1, 0.5])
self.addScaleRotateHandle([1, 0.5], [0, 0.5])
self.addScaleHandle([0.5, 1], [0.5, 0.5])
class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
def __init__(self, points, width, **args):
QObjectWorkaround.__init__(self)
QtGui.QGraphicsItem.__init__(self)
self.roiArgs = args
if len(points) < 2:
raise Exception("Must start with at least 2 points")
self.lines = []
self.lines.append(ROI([0, 0], [1, 5], parent=self))
self.lines[-1].addScaleHandle([0.5, 1], [0.5, 0.5])
h = self.lines[-1].addScaleRotateHandle([0, 0.5], [1, 0.5])
h.movePoint(points[0])
h.movePoint(points[0])
for i in range(1, len(points)):
h = self.lines[-1].addScaleRotateHandle([1, 0.5], [0, 0.5])
if i < len(points)-1:
self.lines.append(ROI([0, 0], [1, 5], parent=self))
self.lines[-1].addScaleRotateHandle([0, 0.5], [1, 0.5], item=h)
h.movePoint(points[i])
h.movePoint(points[i])
for l in self.lines:
l.translatable = False
#self.addToGroup(l)
l.connect(QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
l.connect(QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
l.connect(QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
def paint(self, *args):
pass
def boundingRect(self):
return QtCore.QRectF()
def roiChangedEvent(self):
w = self.lines[0].state['size'][1]
for l in self.lines[1:]:
w0 = l.state['size'][1]
l.scale([1.0, w/w0], center=[0.5,0.5])
self.emit(QtCore.SIGNAL('regionChanged'), self)
def roiChangeStartedEvent(self):
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
def roiChangeFinishedEvent(self):
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
def getArrayRegion(self, arr, img=None):
rgns = []
for l in self.lines:
rgn = l.getArrayRegion(arr, img)
if rgn is None:
continue
#return None
rgns.append(rgn)
#print l.state['size']
#print [(r.shape) for r in rgns]
return vstack(rgns)
class EllipseROI(ROI):
def __init__(self, pos, size, **args):
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
ROI.__init__(self, pos, size, **args)
self.addRotateHandle([1.0, 0.5], [0.5, 0.5])
self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5])
def paint(self, p, opt, widget):
r = self.boundingRect()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen)
p.scale(r.width(), r.height())## workaround for GL bug
r = QtCore.QRectF(r.x()/r.width(), r.y()/r.height(), 1,1)
p.drawEllipse(r)
def getArrayRegion(self, arr, img=None):
arr = ROI.getArrayRegion(self, arr, img)
if arr is None or arr.shape[0] == 0 or arr.shape[1] == 0:
return None
w = arr.shape[0]
h = arr.shape[1]
## generate an ellipsoidal mask
mask = fromfunction(lambda x,y: (((x+0.5)/(w/2.)-1)**2+ ((y+0.5)/(h/2.)-1)**2)**0.5 < 1, (w, h))
return arr * mask
def shape(self):
self.path = QtGui.QPainterPath()
self.path.addEllipse(self.boundingRect())
return self.path
class CircleROI(EllipseROI):
def __init__(self, pos, size, **args):
ROI.__init__(self, pos, size, **args)
self.aspectLocked = True
#self.addTranslateHandle([0.5, 0.5])
self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5])