Initial commit (again)
This commit is contained in:
commit
3d84aefbc4
413
GraphicsView.py
Normal file
413
GraphicsView.py
Normal 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
275
ImageView.py
Normal 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
188
ImageViewTemplate.py
Normal 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
341
ImageViewTemplate.ui
Normal 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
66
MultiPlotItem.py
Normal 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
39
MultiPlotWidget.py
Normal 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
2099
PIL_Fix/Image.py
Executable file
File diff suppressed because it is too large
Load Diff
11
PIL_Fix/README
Normal file
11
PIL_Fix/README
Normal 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
957
PlotItem.py
Normal 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
43
PlotWidget.py
Normal 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
118
Point.py
Normal 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
0
__init__.py
Normal file
91
functions.py
Normal file
91
functions.py
Normal 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
1481
graphicsItems.py
Normal file
File diff suppressed because it is too large
Load Diff
32
graphicsWindows.py
Normal file
32
graphicsWindows.py
Normal 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
7
license.txt
Normal 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
277
plotConfigTemplate.py
Normal 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
527
plotConfigTemplate.ui
Normal 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
21
test_ImageView.py
Normal 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
17
test_MultiPlotWidget.py
Executable 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
80
test_PlotWidget.py
Executable 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
88
test_ROItypes.py
Executable 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
100
test_viewBox.py
Executable 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
837
widgets.py
Normal 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])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user