merged many changes in from acq4:

- added vertical lines / regions for plots
 - added gradient editor widget
 - many bugfixes
 - cleaned up imageview a bit
This commit is contained in:
Luke Campagnola 2010-07-24 14:29:09 -04:00
parent 7efc975400
commit 2ca08c69ce
18 changed files with 2811 additions and 1384 deletions

364
GradientWidget.py Normal file
View File

@ -0,0 +1,364 @@
# -*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
class TickSlider(QtGui.QGraphicsView):
def __init__(self, parent=None, orientation='bottom', allowAdd=True, **kargs):
QtGui.QGraphicsView.__init__(self, parent)
#self.orientation = orientation
self.allowAdd = allowAdd
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing)
self.length = 100
self.tickSize = 15
self.orientations = {
'left': (270, 1, -1),
'right': (270, 1, 1),
'top': (0, 1, -1),
'bottom': (0, 1, 1)
}
self.scene = QtGui.QGraphicsScene()
self.setScene(self.scene)
self.ticks = {}
self.maxDim = 20
self.setOrientation(orientation)
self.setFrameStyle(QtGui.QFrame.NoFrame | QtGui.QFrame.Plain)
self.setBackgroundRole(QtGui.QPalette.NoRole)
self.setMouseTracking(True)
def keyPressEvent(self, ev):
ev.ignore()
def setMaxDim(self, mx=None):
if mx is None:
mx = self.maxDim
else:
self.maxDim = mx
if self.orientation in ['bottom', 'top']:
self.setFixedHeight(mx)
self.setMaximumWidth(16777215)
else:
self.setFixedWidth(mx)
self.setMaximumHeight(16777215)
def setOrientation(self, ort):
self.orientation = ort
self.resetTransform()
self.rotate(self.orientations[ort][0])
self.scale(*self.orientations[ort][1:])
self.setMaxDim()
def addTick(self, x, color=None, movable=True):
if color is None:
color = QtGui.QColor(255,255,255)
tick = Tick(self, [x*self.length, 0], color, movable, self.tickSize)
self.ticks[tick] = x
self.scene.addItem(tick)
return tick
def removeTick(self, tick):
del self.ticks[tick]
self.scene.removeItem(tick)
def tickMoved(self, tick, pos):
#print "tick changed"
## Correct position of tick if it has left bounds.
newX = min(max(0, pos.x()), self.length)
pos.setX(newX)
tick.setPos(pos)
self.ticks[tick] = float(newX) / self.length
def tickClicked(self, tick, ev):
if ev.button() == QtCore.Qt.RightButton:
self.removeTick(tick)
def widgetLength(self):
if self.orientation in ['bottom', 'top']:
return self.width()
else:
return self.height()
def resizeEvent(self, ev):
wlen = max(40, self.widgetLength())
self.setLength(wlen-self.tickSize)
bounds = self.scene.itemsBoundingRect()
bounds.setLeft(min(-self.tickSize*0.5, bounds.left()))
bounds.setRight(max(self.length + self.tickSize, bounds.right()))
#bounds.setTop(min(bounds.top(), self.tickSize))
#bounds.setBottom(max(0, bounds.bottom()))
self.setSceneRect(bounds)
self.fitInView(bounds, QtCore.Qt.KeepAspectRatio)
def setLength(self, newLen):
for t, x in self.ticks.items():
t.setPos(x * newLen, t.pos().y())
self.length = float(newLen)
def mousePressEvent(self, ev):
QtGui.QGraphicsView.mousePressEvent(self, ev)
self.ignoreRelease = False
if len(self.items(ev.pos())) > 0: ## Let items handle their own clicks
self.ignoreRelease = True
def mouseReleaseEvent(self, ev):
QtGui.QGraphicsView.mouseReleaseEvent(self, ev)
if self.ignoreRelease:
return
pos = self.mapToScene(ev.pos())
if pos.x() < 0 or pos.x() > self.length:
return
if pos.y() < 0 or pos.y() > self.tickSize:
return
if ev.button() == QtCore.Qt.LeftButton and self.allowAdd:
pos.setX(min(max(pos.x(), 0), self.length))
self.addTick(pos.x()/self.length)
elif ev.button() == QtCore.Qt.RightButton:
self.showMenu(ev)
def showMenu(self, ev):
pass
def setTickColor(self, tick, color):
tick = self.getTick(tick)
tick.color = color
tick.setBrush(QtGui.QBrush(QtGui.QColor(tick.color)))
def setTickValue(self, tick, val):
tick = self.getTick(tick)
val = min(max(0.0, val), 1.0)
x = val * self.length
pos = tick.pos()
pos.setX(x)
tick.setPos(pos)
self.ticks[tick] = val
def tickValue(self, tick):
tick = self.getTick(tick)
return self.ticks[tick]
def getTick(self, tick):
if type(tick) is int:
tick = self.listTicks()[tick][0]
return tick
def mouseMoveEvent(self, ev):
QtGui.QGraphicsView.mouseMoveEvent(self, ev)
#print ev.pos(), ev.buttons()
def listTicks(self):
ticks = self.ticks.items()
ticks.sort(lambda a,b: cmp(a[1], b[1]))
return ticks
class GradientWidget(TickSlider):
def __init__(self, *args, **kargs):
TickSlider.__init__(self, *args, **kargs)
self.rectSize = 15
self.gradRect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, -self.rectSize, 100, self.rectSize))
self.colorMode = 'rgb'
#self.gradient = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(100,0))
self.scene.addItem(self.gradRect)
self.addTick(0, QtGui.QColor(0,0,0), True)
self.addTick(1, QtGui.QColor(255,0,0), True)
self.setMaxDim(self.rectSize + self.tickSize)
self.updateGradient()
#self.btn = QtGui.QPushButton('RGB')
#self.btnProxy = self.scene.addWidget(self.btn)
#self.btnProxy.setFlag(self.btnProxy.ItemIgnoresTransformations)
#self.btnProxy.scale(0.7, 0.7)
#self.btnProxy.translate(-self.btnProxy.sceneBoundingRect().width()+self.tickSize/2., 0)
#if self.orientation == 'bottom':
#self.btnProxy.translate(0, -self.rectSize)
def setColorMode(self, cm):
if cm not in ['rgb', 'hsv']:
raise Exception("Unknown color mode %s" % str(cm))
self.colorMode = cm
self.updateGradient()
def updateGradient(self):
self.gradient = self.getGradient()
self.gradRect.setBrush(QtGui.QBrush(self.gradient))
self.emit(QtCore.SIGNAL('gradientChanged'), self)
def setLength(self, newLen):
TickSlider.setLength(self, newLen)
self.gradRect.setRect(0, -self.rectSize, newLen, self.rectSize)
self.updateGradient()
def tickClicked(self, tick, ev):
if ev.button() == QtCore.Qt.LeftButton:
if not tick.colorChangeAllowed:
return
color = QtGui.QColorDialog.getColor(tick.color, None, "Select Color", QtGui.QColorDialog.ShowAlphaChannel)
if color.isValid():
self.setTickColor(tick, color)
self.updateGradient()
elif ev.button() == QtCore.Qt.RightButton:
if not tick.removeAllowed:
return
if len(self.ticks) > 2:
self.removeTick(tick)
self.updateGradient()
def tickMoved(self, tick, pos):
TickSlider.tickMoved(self, tick, pos)
self.updateGradient()
def getGradient(self):
g = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(self.length,0))
if self.colorMode == 'rgb':
ticks = self.listTicks()
g.setStops([(x, QtGui.QColor(t.color)) for t,x in ticks])
elif self.colorMode == 'hsv': ## HSV mode is approximated for display by interpolating 10 points between each stop
ticks = self.listTicks()
stops = []
stops.append((ticks[0][1], ticks[0][0].color))
for i in range(1,len(ticks)):
x1 = ticks[i-1][1]
x2 = ticks[i][1]
dx = (x2-x1) / 10.
for j in range(1,10):
x = x1 + dx*j
stops.append((x, self.getColor(x)))
stops.append((x2, self.getColor(x2)))
g.setStops(stops)
return g
def getColor(self, x):
ticks = self.listTicks()
if x <= ticks[0][1]:
return QtGui.QColor(ticks[0][0].color) # always copy colors before handing them out
if x >= ticks[-1][1]:
return QtGui.QColor(ticks[-1][0].color)
x2 = ticks[0][1]
for i in range(1,len(ticks)):
x1 = x2
x2 = ticks[i][1]
if x1 <= x and x2 >= x:
break
dx = (x2-x1)
if dx == 0:
f = 0.
else:
f = (x-x1) / dx
c1 = ticks[i-1][0].color
c2 = ticks[i][0].color
if self.colorMode == 'rgb':
r = c1.red() * (1.-f) + c2.red() * f
g = c1.green() * (1.-f) + c2.green() * f
b = c1.blue() * (1.-f) + c2.blue() * f
return QtGui.QColor(r, g, b)
elif self.colorMode == 'hsv':
h1,s1,v1,_ = c1.getHsv()
h2,s2,v2,_ = c2.getHsv()
h = h1 * (1.-f) + h2 * f
s = s1 * (1.-f) + s2 * f
v = v1 * (1.-f) + v2 * f
c = QtGui.QColor()
c.setHsv(h,s,v)
return c
def mouseReleaseEvent(self, ev):
TickSlider.mouseReleaseEvent(self, ev)
self.updateGradient()
def addTick(self, x, color=None, movable=True):
if color is None:
color = self.getColor(x)
t = TickSlider.addTick(self, x, color=color, movable=movable)
t.colorChangeAllowed = True
t.removeAllowed = True
return t
class GammaWidget(TickSlider):
pass
class Tick(QtGui.QGraphicsPolygonItem):
def __init__(self, view, pos, color, movable=True, scale=10):
#QObjectWorkaround.__init__(self)
self.movable = movable
self.view = view
self.scale = scale
self.color = color
#self.endTick = endTick
self.pg = QtGui.QPolygonF([QtCore.QPointF(0,0), QtCore.QPointF(-scale/3**0.5,scale), QtCore.QPointF(scale/3**0.5,scale)])
QtGui.QGraphicsPolygonItem.__init__(self, self.pg)
self.setPos(pos[0], pos[1])
self.setFlags(QtGui.QGraphicsItem.ItemIsMovable | QtGui.QGraphicsItem.ItemIsSelectable)
self.setBrush(QtGui.QBrush(QtGui.QColor(self.color)))
if self.movable:
self.setZValue(1)
else:
self.setZValue(0)
#def x(self):
#return self.pos().x()/100.
def mouseMoveEvent(self, ev):
#print self, "move", ev.scenePos()
if not self.movable:
return
if not ev.buttons() & QtCore.Qt.LeftButton:
return
newPos = ev.scenePos() + self.mouseOffset
newPos.setY(self.pos().y())
#newPos.setX(min(max(newPos.x(), 0), 100))
self.setPos(newPos)
self.view.tickMoved(self, newPos)
self.movedSincePress = True
#self.emit(QtCore.SIGNAL('tickChanged'), self)
ev.accept()
def mousePressEvent(self, ev):
self.movedSincePress = False
if ev.button() == QtCore.Qt.LeftButton:
ev.accept()
self.mouseOffset = self.pos() - ev.scenePos()
self.pressPos = ev.scenePos()
elif ev.button() == QtCore.Qt.RightButton:
ev.accept()
#if self.endTick:
#return
#self.view.tickChanged(self, delete=True)
def mouseReleaseEvent(self, ev):
#print self, "release", ev.scenePos()
if not self.movedSincePress:
self.view.tickClicked(self, ev)
#if ev.button() == QtCore.Qt.LeftButton and ev.scenePos() == self.pressPos:
#color = QtGui.QColorDialog.getColor(self.color, None, "Select Color", QtGui.QColorDialog.ShowAlphaChannel)
#if color.isValid():
#self.color = color
#self.setBrush(QtGui.QBrush(QtGui.QColor(self.color)))
##self.emit(QtCore.SIGNAL('tickChanged'), self)
#self.view.tickChanged(self)

26
GradientWidgetTest.py Normal file
View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from GradientWidget import *
from PyQt4 import QtGui
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
w.show()
w.resize(400,400)
cw = QtGui.QWidget()
w.setCentralWidget(cw)
l = QtGui.QGridLayout()
l.setSpacing(0)
cw.setLayout(l)
w1 = GradientWidget(orientation='top')
w2 = GradientWidget(orientation='right', allowAdd=False)
w2.setTickColor(1, QtGui.QColor(255,255,255))
w3 = GradientWidget(orientation='bottom')
w4 = TickSlider(orientation='left')
l.addWidget(w1, 0, 1)
l.addWidget(w2, 1, 2)
l.addWidget(w3, 2, 1)
l.addWidget(w4, 1, 0)

View File

@ -10,11 +10,11 @@ from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
#import time #import time
from Point import * from Point import *
#from vector import * #from vector import *
import sys
class GraphicsView(QtGui.QGraphicsView): class GraphicsView(QtGui.QGraphicsView):
def __init__(self, *args): def __init__(self, parent=None, useOpenGL=True):
"""Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the """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 viewed coordinate range. Also automatically creates a QGraphicsScene and a central QGraphicsWidget
that is automatically scaled to the full view geometry. that is automatically scaled to the full view geometry.
@ -26,8 +26,9 @@ class GraphicsView(QtGui.QGraphicsView):
The view can be panned using the middle mouse button and scaled using the right mouse button if The view can be panned using the middle mouse button and scaled using the right mouse button if
enabled via enableMouse().""" enabled via enableMouse()."""
QtGui.QGraphicsView.__init__(self, *args) QtGui.QGraphicsView.__init__(self, parent)
self.setViewport(QtOpenGL.QGLWidget()) self.useOpenGL(useOpenGL)
palette = QtGui.QPalette() palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(0,0,0)) brush = QtGui.QBrush(QtGui.QColor(0,0,0))
brush.setStyle(QtCore.Qt.SolidPattern) brush.setStyle(QtCore.Qt.SolidPattern)
@ -49,6 +50,7 @@ class GraphicsView(QtGui.QGraphicsView):
#self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor) #self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate) self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate)
self.setSceneRect(QtCore.QRectF(-1e10, -1e10, 2e10, 2e10)) self.setSceneRect(QtCore.QRectF(-1e10, -1e10, 2e10, 2e10))
#self.setSceneRect(1, 1, 0, 0) ## Set an empty (but non-zero) scene rect so that the view doesn't try to automatically update for us.
#self.setInteractive(False) #self.setInteractive(False)
self.lockedViewports = [] self.lockedViewports = []
self.lastMousePos = None self.lastMousePos = None
@ -68,6 +70,18 @@ class GraphicsView(QtGui.QGraphicsView):
self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False) self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False)
self.clickAccepted = False self.clickAccepted = False
def useOpenGL(self, b=True):
if b:
v = QtOpenGL.QGLWidget()
else:
v = QtGui.QWidget()
#v.setStyleSheet("background-color: #000000;")
self.setViewport(v)
def keyPressEvent(self, ev):
ev.ignore()
def setCentralItem(self, item): def setCentralItem(self, item):
if self.centralWidget is not None: if self.centralWidget is not None:
self.scene().removeItem(self.centralWidget) self.scene().removeItem(self.centralWidget)
@ -77,6 +91,9 @@ class GraphicsView(QtGui.QGraphicsView):
def addItem(self, *args): def addItem(self, *args):
return self.scene().addItem(*args) return self.scene().addItem(*args)
def removeItem(self, *args):
return self.scene().removeItem(*args)
def enableMouse(self, b=True): def enableMouse(self, b=True):
self.mouseEnabled = b self.mouseEnabled = b
self.autoPixelRange = (not b) self.autoPixelRange = (not b)
@ -128,6 +145,8 @@ class GraphicsView(QtGui.QGraphicsView):
v.setXRange(self.range, padding=0) v.setXRange(self.range, padding=0)
def visibleRange(self): def visibleRange(self):
"""Return the boundaries of the view in scene coordinates"""
## easier to just return self.range ?
r = QtCore.QRectF(self.rect()) r = QtCore.QRectF(self.rect())
return self.viewportTransform().inverted()[0].mapRect(r) return self.viewportTransform().inverted()[0].mapRect(r)
@ -347,6 +366,16 @@ class GraphicsView(QtGui.QGraphicsView):
self.setRenderHints(rh) self.setRenderHints(rh)
self.png.save(fileName) self.png.save(fileName)
def writePs(self, fileName=None):
if fileName is None:
fileName = str(QtGui.QFileDialog.getSaveFileName())
printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
printer.setOutputFileName(fileName)
painter = QtGui.QPainter(printer)
self.render(painter)
painter.end()
#def getFreehandLine(self): #def getFreehandLine(self):
## Wait for click ## Wait for click

View File

@ -17,6 +17,11 @@ from ImageViewTemplate import *
from graphicsItems import * from graphicsItems import *
from widgets import ROI from widgets import ROI
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
import sys
from numpy import ndarray
import ptime
from SignalProxy import proxyConnect
class PlotROI(ROI): class PlotROI(ROI):
def __init__(self, size): def __init__(self, size):
@ -35,12 +40,25 @@ class ImageView(QtGui.QWidget):
self.ui = Ui_Form() self.ui = Ui_Form()
self.ui.setupUi(self) self.ui.setupUi(self)
self.scene = self.ui.graphicsView.sceneObj self.scene = self.ui.graphicsView.sceneObj
self.ignoreTimeLine = False
if 'linux' in sys.platform.lower(): ## Stupid GL bug in linux.
self.ui.graphicsView.setViewport(QtGui.QWidget())
self.ui.graphicsView.enableMouse(True) self.ui.graphicsView.enableMouse(True)
self.ui.graphicsView.autoPixelRange = False self.ui.graphicsView.autoPixelRange = False
self.ui.graphicsView.setAspectLocked(True) self.ui.graphicsView.setAspectLocked(True)
self.ui.graphicsView.invertY() self.ui.graphicsView.invertY()
self.ui.graphicsView.enableMouse() self.ui.graphicsView.enableMouse()
self. ticks = [t[0] for t in self.ui.gradientWidget.listTicks()]
self.ticks[0].colorChangeAllowed = False
self.ticks[1].colorChangeAllowed = False
self.ui.gradientWidget.allowAdd = False
self.ui.gradientWidget.setTickColor(self.ticks[1], QtGui.QColor(255,255,255))
self.ui.gradientWidget.setOrientation('right')
self.imageItem = ImageItem() self.imageItem = ImageItem()
self.scene.addItem(self.imageItem) self.scene.addItem(self.imageItem)
self.currentIndex = 0 self.currentIndex = 0
@ -51,26 +69,46 @@ class ImageView(QtGui.QWidget):
self.roi.setZValue(20) self.roi.setZValue(20)
self.scene.addItem(self.roi) self.scene.addItem(self.roi)
self.roi.hide() self.roi.hide()
self.ui.roiPlot.hide() self.normRoi = PlotROI(10)
self.normRoi.setPen(QtGui.QPen(QtGui.QColor(255,255,0)))
self.normRoi.setZValue(20)
self.scene.addItem(self.normRoi)
self.normRoi.hide()
#self.ui.roiPlot.hide()
self.roiCurve = self.ui.roiPlot.plot() self.roiCurve = self.ui.roiPlot.plot()
self.roiTimeLine = InfiniteLine(self.ui.roiPlot, 0) self.timeLine = InfiniteLine(self.ui.roiPlot, 0, movable=True)
self.roiTimeLine.setPen(QtGui.QPen(QtGui.QColor(255, 255, 0, 200))) self.timeLine.setPen(QtGui.QPen(QtGui.QColor(255, 255, 0, 200)))
self.ui.roiPlot.addItem(self.roiTimeLine) self.timeLine.setZValue(1)
self.ui.roiPlot.addItem(self.timeLine)
self.ui.splitter.setSizes([self.height()-35, 35])
self.ui.roiPlot.showScale('left', False)
self.normLines = [] self.keysPressed = {}
for i in [0,1]: self.playTimer = QtCore.QTimer()
l = InfiniteLine(self.ui.roiPlot, 0) self.playRate = 0
l.setPen(QtGui.QPen(QtGui.QColor(0, 100, 200, 200))) self.lastPlayTime = 0
self.ui.roiPlot.addItem(l)
self.normLines.append(l)
l.hide()
for fn in ['addItem']: #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()
self.normRgn = LinearRegionItem(self.ui.roiPlot, 'vertical')
self.normRgn.setZValue(0)
self.ui.roiPlot.addItem(self.normRgn)
self.normRgn.hide()
## wrap functions from graphics view
for fn in ['addItem', 'removeItem']:
setattr(self, fn, getattr(self.ui.graphicsView, fn)) 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.timeSlider, QtCore.SIGNAL('valueChanged(int)'), self.timeChanged)
QtCore.QObject.connect(self.ui.whiteSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage) self.timeLine.connect(QtCore.SIGNAL('positionChanged'), self.timeLineChanged)
QtCore.QObject.connect(self.ui.blackSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage) #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.gradientWidget, QtCore.SIGNAL('gradientChanged'), self.updateImage)
QtCore.QObject.connect(self.ui.roiBtn, QtCore.SIGNAL('clicked()'), self.roiClicked) QtCore.QObject.connect(self.ui.roiBtn, QtCore.SIGNAL('clicked()'), self.roiClicked)
self.roi.connect(QtCore.SIGNAL('regionChanged'), self.roiChanged) 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.normBtn, QtCore.SIGNAL('toggled(bool)'), self.normToggled)
@ -80,37 +118,177 @@ class ImageView(QtGui.QWidget):
QtCore.QObject.connect(self.ui.normROICheck, 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.normFrameCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normTimeRangeCheck, 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.playTimer, QtCore.SIGNAL('timeout()'), self.timeout)
QtCore.QObject.connect(self.ui.normStopSlider, QtCore.SIGNAL('valueChanged(int)'), 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.normProxy = proxyConnect(self.normRgn, QtCore.SIGNAL('regionChanged'), self.updateNorm)
self.normRoi.connect(QtCore.SIGNAL('regionChangeFinished'), self.updateNorm)
self.ui.roiPlot.registerPlot(self.name + '_ROI') self.ui.roiPlot.registerPlot(self.name + '_ROI')
def updateNorm(self): self.noRepeatKeys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]
for l, sl in zip(self.normLines, [self.ui.normStartSlider, self.ui.normStopSlider]):
if self.ui.normTimeRangeCheck.isChecked(): #def __dtor__(self):
l.show() ##print "Called ImageView sip destructor"
#self.quit()
#QtGui.QWidget.__dtor__(self)
def quit(self):
self.scene.clear()
del self.image
del self.imageDisp
def keyPressEvent(self, ev):
if ev.key() == QtCore.Qt.Key_Space:
if self.playRate == 0:
fps = (self.getProcessedImage().shape[0]-1) / (self.tVals[-1] - self.tVals[0])
self.play(fps)
#print fps
else: else:
l.hide() self.play(0)
ev.accept()
elif ev.key() == QtCore.Qt.Key_Home:
self.setCurrentIndex(0)
self.play(0)
ev.accept()
elif ev.key() == QtCore.Qt.Key_End:
self.setCurrentIndex(self.getProcessedImage().shape[0]-1)
self.play(0)
ev.accept()
elif ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
self.keysPressed[ev.key()] = 1
self.evalKeyState()
else:
QtGui.QWidget.keyPressEvent(self, ev)
i, t = self.timeIndex(sl) def keyReleaseEvent(self, ev):
l.setPos(t) if ev.key() in [QtCore.Qt.Key_Space, QtCore.Qt.Key_Home, QtCore.Qt.Key_End]:
ev.accept()
elif ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
try:
del self.keysPressed[ev.key()]
except:
self.keysPressed = {}
self.evalKeyState()
else:
QtGui.QWidget.keyReleaseEvent(self, ev)
def evalKeyState(self):
if len(self.keysPressed) == 1:
key = self.keysPressed.keys()[0]
if key == QtCore.Qt.Key_Right:
self.play(20)
self.lastPlayTime = ptime.time() + 0.2 ## 2ms wait before start
self.jumpFrames(1)
elif key == QtCore.Qt.Key_Left:
self.play(-20)
self.lastPlayTime = ptime.time() + 0.2
self.jumpFrames(-1)
elif key == QtCore.Qt.Key_Up:
self.play(-100)
elif key == QtCore.Qt.Key_Down:
self.play(100)
elif key == QtCore.Qt.Key_PageUp:
self.play(-1000)
elif key == QtCore.Qt.Key_PageDown:
self.play(1000)
else:
self.play(0)
def play(self, rate):
#print "play:", rate
self.playRate = rate
if rate == 0:
self.playTimer.stop()
return
self.lastPlayTime = ptime.time()
if not self.playTimer.isActive():
self.playTimer.start(16)
def timeout(self):
now = ptime.time()
dt = now - self.lastPlayTime
if dt < 0:
return
n = int(self.playRate * dt)
#print n, dt
if n != 0:
#print n, dt, self.lastPlayTime
self.lastPlayTime += (float(n)/self.playRate)
if self.currentIndex+n > self.image.shape[0]:
self.play(0)
self.jumpFrames(n)
def setCurrentIndex(self, ind):
self.currentIndex = clip(ind, 0, self.getProcessedImage().shape[0]-1)
self.updateImage()
self.ignoreTimeLine = True
self.timeLine.setValue(self.tVals[self.currentIndex])
self.ignoreTimeLine = False
def jumpFrames(self, n):
"""If this is a video, move ahead n frames"""
if self.axes['t'] is not None:
self.setCurrentIndex(self.currentIndex + n)
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)
if self.ui.normTimeRangeCheck.isChecked():
#print "show!"
self.normRgn.show()
else:
self.normRgn.hide()
if self.ui.normROICheck.isChecked():
#print "show!"
self.normRoi.show()
else:
self.normRoi.hide()
self.imageDisp = None self.imageDisp = None
self.updateImage() self.updateImage()
self.roiChanged() self.roiChanged()
def normToggled(self, b): def normToggled(self, b):
self.ui.normGroup.setVisible(b) self.ui.normGroup.setVisible(b)
self.normRoi.setVisible(b and self.ui.normROICheck.isChecked())
self.normRgn.setVisible(b and self.ui.normTimeRangeCheck.isChecked())
def roiClicked(self): def roiClicked(self):
if self.ui.roiBtn.isChecked(): if self.ui.roiBtn.isChecked():
self.roi.show() self.roi.show()
self.ui.roiPlot.show() #self.ui.roiPlot.show()
self.ui.roiPlot.setMouseEnabled(True, True)
self.ui.splitter.setSizes([self.height()*0.6, self.height()*0.4])
self.roiCurve.show()
self.roiChanged() self.roiChanged()
self.ui.roiPlot.showScale('left', True)
else: else:
self.roi.hide() self.roi.hide()
self.ui.roiPlot.hide() self.ui.roiPlot.setMouseEnabled(False, False)
self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
self.ui.splitter.setSizes([self.height()-35, 35])
self.roiCurve.hide()
self.ui.roiPlot.showScale('left', False)
def roiChanged(self): def roiChanged(self):
if self.image is None: if self.image is None:
@ -130,20 +308,36 @@ class ImageView(QtGui.QWidget):
self.roiCurve.setData(y=data, x=self.tVals) self.roiCurve.setData(y=data, x=self.tVals)
#self.ui.roiPlot.replot() #self.ui.roiPlot.replot()
def setImage(self, img, autoRange=True, autoLevels=True, levels=None): def setImage(self, img, autoRange=True, autoLevels=True, levels=None, axes=None, xvals=None):
"""Set the image to be displayed in the widget.
Options are:
img: ndarray; the image to be displayed.
autoRange: bool; whether to scale/pan the view to fit the image.
autoLevels: bool; whether to update the white/black levels to fit the image.
levels: (min, max); the white and black level values to use.
axes: {'t':0, 'x':1, 'y':2, 'c':3}; Dictionary indicating the interpretation for each axis.
This is only needed to override the default guess.
"""
if not isinstance(img, ndarray):
raise Exception("Image must be specified as ndarray.")
self.image = img self.image = img
if hasattr(img, 'xvals'):
if xvals is not None:
self.tVals = xvals
elif hasattr(img, 'xvals'):
self.tVals = img.xvals(0) self.tVals = img.xvals(0)
else: else:
self.tVals = arange(img.shape[0]) self.tVals = arange(img.shape[0])
self.ui.timeSlider.setValue(0) #self.ui.timeSlider.setValue(0)
#self.ui.normStartSlider.setValue(0) #self.ui.normStartSlider.setValue(0)
#self.ui.timeSlider.setMaximum(img.shape[0]-1) #self.ui.timeSlider.setMaximum(img.shape[0]-1)
if axes is None:
if img.ndim == 2: if img.ndim == 2:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None} self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
elif img.ndim == 3: elif img.ndim == 3:
if img.shape[2] <= 3: if img.shape[2] <= 4:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2} self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
else: else:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None} self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
@ -163,11 +357,36 @@ class ImageView(QtGui.QWidget):
if self.ui.roiBtn.isChecked(): if self.ui.roiBtn.isChecked():
self.roiChanged() self.roiChanged()
if self.axes['t'] is not None:
#self.ui.roiPlot.show()
self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
#self.ui.roiPlot.setMouseEnabled(False, False)
if len(self.tVals) > 1:
start = self.tVals.min()
stop = self.tVals.max() + abs(self.tVals[-1] - self.tVals[0]) * 0.02
elif len(self.tVals) == 1:
start = self.tVals[0] - 0.5
stop = self.tVals[0] + 0.5
else:
start = 0
stop = 1
for s in [self.timeLine, self.normRgn]:
s.setBounds([start, stop])
#else:
#self.ui.roiPlot.hide()
self.roiClicked()
def autoLevels(self): def autoLevels(self):
image = self.getProcessedImage() image = self.getProcessedImage()
self.ui.whiteSlider.setValue(self.ui.whiteSlider.maximum()) #self.ui.whiteSlider.setValue(self.ui.whiteSlider.maximum())
self.ui.blackSlider.setValue(0) #self.ui.blackSlider.setValue(0)
self.ui.gradientWidget.setTickValue(self.ticks[0], 0.0)
self.ui.gradientWidget.setTickValue(self.ticks[1], 1.0)
self.imageItem.setLevels(white=self.whiteLevel(), black=self.blackLevel()) self.imageItem.setLevels(white=self.whiteLevel(), black=self.blackLevel())
def autoRange(self): def autoRange(self):
@ -189,7 +408,7 @@ class ImageView(QtGui.QWidget):
return image return image
div = self.ui.normDivideRadio.isChecked() div = self.ui.normDivideRadio.isChecked()
norm = image.copy() norm = image.view(ndarray).copy()
#if div: #if div:
#norm = ones(image.shape) #norm = ones(image.shape)
#else: #else:
@ -198,8 +417,8 @@ class ImageView(QtGui.QWidget):
norm = norm.astype(float32) norm = norm.astype(float32)
if self.ui.normTimeRangeCheck.isChecked() and image.ndim == 3: if self.ui.normTimeRangeCheck.isChecked() and image.ndim == 3:
(sind, start) = self.timeIndex(self.ui.normStartSlider) (sind, start) = self.timeIndex(self.normRgn.lines[0])
(eind, end) = self.timeIndex(self.ui.normStopSlider) (eind, end) = self.timeIndex(self.normRgn.lines[1])
#print start, end, sind, eind #print start, end, sind, eind
n = image[sind:eind+1].mean(axis=0) n = image[sind:eind+1].mean(axis=0)
n.shape = (1,) + n.shape n.shape = (1,) + n.shape
@ -216,17 +435,27 @@ class ImageView(QtGui.QWidget):
else: else:
norm -= n norm -= n
if self.ui.normROICheck.isChecked() and image.ndim == 3:
n = self.normRoi.getArrayRegion(norm, self.imageItem, (1, 2)).mean(axis=1).mean(axis=1)
n = n[:,newaxis,newaxis]
#print start, end, sind, eind
if div:
norm /= n
else:
norm -= n
return norm return norm
def timeLineChanged(self):
#(ind, time) = self.timeIndex(self.ui.timeSlider)
def timeChanged(self): if self.ignoreTimeLine:
(ind, time) = self.timeIndex(self.ui.timeSlider) return
self.play(0)
(ind, time) = self.timeIndex(self.timeLine)
if ind != self.currentIndex: if ind != self.currentIndex:
self.currentIndex = ind self.currentIndex = ind
self.updateImage() self.updateImage()
self.roiTimeLine.setPos(time) #self.timeLine.setPos(time)
#self.ui.roiPlot.replot()
self.emit(QtCore.SIGNAL('timeChanged'), ind, time) self.emit(QtCore.SIGNAL('timeChanged'), ind, time)
def updateImage(self): def updateImage(self):
@ -237,29 +466,38 @@ class ImageView(QtGui.QWidget):
image = self.getProcessedImage() image = self.getProcessedImage()
#print "update:", image.ndim, image.max(), image.min(), self.blackLevel(), self.whiteLevel() #print "update:", image.ndim, image.max(), image.min(), self.blackLevel(), self.whiteLevel()
if self.axes['t'] is None: if self.axes['t'] is None:
self.ui.timeSlider.hide() #self.ui.timeSlider.hide()
self.imageItem.updateImage(image, white=self.whiteLevel(), black=self.blackLevel()) self.imageItem.updateImage(image, white=self.whiteLevel(), black=self.blackLevel())
self.ui.roiPlot.hide()
self.ui.roiBtn.hide()
else: else:
self.ui.timeSlider.show() self.ui.roiBtn.show()
self.ui.roiPlot.show()
#self.ui.timeSlider.show()
self.imageItem.updateImage(image[self.currentIndex], white=self.whiteLevel(), black=self.blackLevel()) self.imageItem.updateImage(image[self.currentIndex], white=self.whiteLevel(), black=self.blackLevel())
def timeIndex(self, slider): def timeIndex(self, slider):
"""Return the time and frame index indicated by a slider""" """Return the time and frame index indicated by a slider"""
if self.image is None: if self.image is None:
return (0,0) return (0,0)
v = slider.value() #v = slider.value()
vmax = slider.maximum() #vmax = slider.maximum()
f = float(v) / vmax #f = float(v) / vmax
t = 0.0
t = slider.value()
#t = 0.0
#xv = self.image.xvals('Time') #xv = self.image.xvals('Time')
xv = self.tVals xv = self.tVals
if xv is None: if xv is None:
ind = int(f * self.image.shape[0]) ind = int(t)
#ind = int(f * self.image.shape[0])
else: else:
if len(xv) < 2: if len(xv) < 2:
return (0,0) return (0,0)
totTime = xv[-1] + (xv[-1]-xv[-2]) totTime = xv[-1] + (xv[-1]-xv[-2])
t = f * totTime #t = f * totTime
inds = argwhere(xv < t) inds = argwhere(xv < t)
if len(inds) < 1: if len(inds) < 1:
return (0,t) return (0,t)
@ -268,8 +506,10 @@ class ImageView(QtGui.QWidget):
return ind, t return ind, t
def whiteLevel(self): def whiteLevel(self):
return self.levelMin + (self.levelMax-self.levelMin) * self.ui.whiteSlider.value() / self.ui.whiteSlider.maximum() return self.levelMin + (self.levelMax-self.levelMin) * self.ui.gradientWidget.tickValue(self.ticks[1])
#return self.levelMin + (self.levelMax-self.levelMin) * self.ui.whiteSlider.value() / self.ui.whiteSlider.maximum()
def blackLevel(self): def blackLevel(self):
return self.levelMin + ((self.levelMax-self.levelMin) / self.ui.blackSlider.maximum()) * self.ui.blackSlider.value() return self.levelMin + (self.levelMax-self.levelMin) * self.ui.gradientWidget.tickValue(self.ticks[0])
#return self.levelMin + ((self.levelMax-self.levelMin) / self.ui.blackSlider.maximum()) * self.ui.blackSlider.value()

View File

@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'ImageViewTemplate.ui' # Form implementation generated from reading ui file 'ImageViewTemplate.ui'
# #
# Created: Mon Mar 29 22:40:48 2010 # Created: Sat Jul 17 13:05:44 2010
# by: PyQt4 UI code generator 4.6 # by: PyQt4 UI code generator 4.5.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -12,7 +12,7 @@ from PyQt4 import QtCore, QtGui
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
Form.resize(757, 495) Form.resize(726, 588)
self.verticalLayout = QtGui.QVBoxLayout(Form) self.verticalLayout = QtGui.QVBoxLayout(Form)
self.verticalLayout.setSpacing(0) self.verticalLayout.setSpacing(0)
self.verticalLayout.setMargin(0) self.verticalLayout.setMargin(0)
@ -23,52 +23,45 @@ class Ui_Form(object):
self.layoutWidget = QtGui.QWidget(self.splitter) self.layoutWidget = QtGui.QWidget(self.splitter)
self.layoutWidget.setObjectName("layoutWidget") self.layoutWidget.setObjectName("layoutWidget")
self.gridLayout = QtGui.QGridLayout(self.layoutWidget) self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0) self.gridLayout.setSpacing(0)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.graphicsView = GraphicsView(self.layoutWidget) self.graphicsView = GraphicsView(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(10)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
self.graphicsView.setSizePolicy(sizePolicy) self.graphicsView.setSizePolicy(sizePolicy)
self.graphicsView.setObjectName("graphicsView") self.graphicsView.setObjectName("graphicsView")
self.gridLayout.addWidget(self.graphicsView, 0, 0, 3, 1) self.gridLayout.addWidget(self.graphicsView, 1, 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 = QtGui.QPushButton(self.layoutWidget)
self.roiBtn.setMaximumSize(QtCore.QSize(40, 16777215)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.roiBtn.sizePolicy().hasHeightForWidth())
self.roiBtn.setSizePolicy(sizePolicy)
self.roiBtn.setMaximumSize(QtCore.QSize(30, 16777215))
self.roiBtn.setCheckable(True) self.roiBtn.setCheckable(True)
self.roiBtn.setObjectName("roiBtn") self.roiBtn.setObjectName("roiBtn")
self.gridLayout.addWidget(self.roiBtn, 2, 1, 1, 3) self.gridLayout.addWidget(self.roiBtn, 3, 3, 1, 1)
self.timeSlider = QtGui.QSlider(self.layoutWidget) self.gradientWidget = GradientWidget(self.layoutWidget)
self.timeSlider.setMaximum(65535) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
self.timeSlider.setOrientation(QtCore.Qt.Horizontal) sizePolicy.setHorizontalStretch(0)
self.timeSlider.setObjectName("timeSlider") sizePolicy.setVerticalStretch(100)
self.gridLayout.addWidget(self.timeSlider, 4, 0, 1, 1) sizePolicy.setHeightForWidth(self.gradientWidget.sizePolicy().hasHeightForWidth())
self.gradientWidget.setSizePolicy(sizePolicy)
self.gradientWidget.setObjectName("gradientWidget")
self.gridLayout.addWidget(self.gradientWidget, 1, 3, 1, 1)
self.normBtn = QtGui.QPushButton(self.layoutWidget) self.normBtn = QtGui.QPushButton(self.layoutWidget)
self.normBtn.setMaximumSize(QtCore.QSize(50, 16777215)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.normBtn.sizePolicy().hasHeightForWidth())
self.normBtn.setSizePolicy(sizePolicy)
self.normBtn.setMaximumSize(QtCore.QSize(30, 16777215))
self.normBtn.setCheckable(True) self.normBtn.setCheckable(True)
self.normBtn.setObjectName("normBtn") self.normBtn.setObjectName("normBtn")
self.gridLayout.addWidget(self.normBtn, 4, 1, 1, 2) self.gridLayout.addWidget(self.normBtn, 2, 3, 1, 1)
self.normGroup = QtGui.QGroupBox(self.layoutWidget) self.normGroup = QtGui.QGroupBox(self.layoutWidget)
self.normGroup.setObjectName("normGroup") self.normGroup.setObjectName("normGroup")
self.gridLayout_2 = QtGui.QGridLayout(self.normGroup) self.gridLayout_2 = QtGui.QGridLayout(self.normGroup)
@ -102,44 +95,28 @@ class Ui_Form(object):
font.setBold(True) font.setBold(True)
self.label_4.setFont(font) self.label_4.setFont(font)
self.label_4.setObjectName("label_4") self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 4, 0, 1, 1) self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
self.normROICheck = QtGui.QCheckBox(self.normGroup) self.normROICheck = QtGui.QCheckBox(self.normGroup)
self.normROICheck.setObjectName("normROICheck") self.normROICheck.setObjectName("normROICheck")
self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1) 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 = QtGui.QDoubleSpinBox(self.normGroup)
self.normXBlurSpin.setObjectName("normXBlurSpin") self.normXBlurSpin.setObjectName("normXBlurSpin")
self.gridLayout_2.addWidget(self.normXBlurSpin, 4, 2, 1, 1) self.gridLayout_2.addWidget(self.normXBlurSpin, 2, 2, 1, 1)
self.label_8 = QtGui.QLabel(self.normGroup) self.label_8 = QtGui.QLabel(self.normGroup)
self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_8.setObjectName("label_8") self.label_8.setObjectName("label_8")
self.gridLayout_2.addWidget(self.label_8, 4, 1, 1, 1) self.gridLayout_2.addWidget(self.label_8, 2, 1, 1, 1)
self.label_9 = QtGui.QLabel(self.normGroup) self.label_9 = QtGui.QLabel(self.normGroup)
self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_9.setObjectName("label_9") self.label_9.setObjectName("label_9")
self.gridLayout_2.addWidget(self.label_9, 4, 3, 1, 1) self.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1)
self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup) self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normYBlurSpin.setObjectName("normYBlurSpin") self.normYBlurSpin.setObjectName("normYBlurSpin")
self.gridLayout_2.addWidget(self.normYBlurSpin, 4, 4, 1, 1) self.gridLayout_2.addWidget(self.normYBlurSpin, 2, 4, 1, 1)
self.label_10 = QtGui.QLabel(self.normGroup) self.label_10 = QtGui.QLabel(self.normGroup)
self.label_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_10.setObjectName("label_10") self.label_10.setObjectName("label_10")
self.gridLayout_2.addWidget(self.label_10, 4, 5, 1, 1) self.gridLayout_2.addWidget(self.label_10, 2, 5, 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 = QtGui.QRadioButton(self.normGroup)
self.normOffRadio.setChecked(True) self.normOffRadio.setChecked(True)
self.normOffRadio.setObjectName("normOffRadio") self.normOffRadio.setObjectName("normOffRadio")
@ -152,9 +129,14 @@ class Ui_Form(object):
self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1) self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup) self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normTBlurSpin.setObjectName("normTBlurSpin") self.normTBlurSpin.setObjectName("normTBlurSpin")
self.gridLayout_2.addWidget(self.normTBlurSpin, 4, 6, 1, 1) self.gridLayout_2.addWidget(self.normTBlurSpin, 2, 6, 1, 1)
self.gridLayout.addWidget(self.normGroup, 5, 0, 1, 4) self.gridLayout.addWidget(self.normGroup, 0, 0, 1, 4)
self.roiPlot = PlotWidget(self.splitter) self.roiPlot = PlotWidget(self.splitter)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.roiPlot.sizePolicy().hasHeightForWidth())
self.roiPlot.setSizePolicy(sizePolicy)
self.roiPlot.setMinimumSize(QtCore.QSize(0, 40)) self.roiPlot.setMinimumSize(QtCore.QSize(0, 40))
self.roiPlot.setObjectName("roiPlot") self.roiPlot.setObjectName("roiPlot")
self.verticalLayout.addWidget(self.splitter) self.verticalLayout.addWidget(self.splitter)
@ -164,10 +146,8 @@ class Ui_Form(object):
def retranslateUi(self, Form): def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8)) Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Form", "B", None, QtGui.QApplication.UnicodeUTF8)) self.roiBtn.setText(QtGui.QApplication.translate("Form", "R", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("Form", "W", None, QtGui.QApplication.UnicodeUTF8)) self.normBtn.setText(QtGui.QApplication.translate("Form", "N", 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.normGroup.setTitle(QtGui.QApplication.translate("Form", "Normalization", None, QtGui.QApplication.UnicodeUTF8))
self.normSubtractRadio.setText(QtGui.QApplication.translate("Form", "Subtract", 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.normDivideRadio.setText(QtGui.QApplication.translate("Form", "Divide", None, QtGui.QApplication.UnicodeUTF8))
@ -178,11 +158,10 @@ class Ui_Form(object):
self.label_8.setText(QtGui.QApplication.translate("Form", "X", 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_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.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.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.normTimeRangeCheck.setText(QtGui.QApplication.translate("Form", "Time range", None, QtGui.QApplication.UnicodeUTF8))
self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8)) self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8))
from GraphicsView import GraphicsView from GraphicsView import GraphicsView
from pyqtgraph.GradientWidget import GradientWidget
from PlotWidget import PlotWidget from PlotWidget import PlotWidget

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>757</width> <width>726</width>
<height>495</height> <height>588</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -27,113 +27,77 @@
</property> </property>
<widget class="QWidget" name="layoutWidget"> <widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0" rowspan="3"> <item row="1" column="0" rowspan="3">
<widget class="GraphicsView" name="graphicsView" native="true"> <widget class="GraphicsView" name="graphicsView" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>10</horstretch>
<verstretch>0</verstretch> <verstretch>10</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<zorder>normGroup</zorder>
<zorder>normGroup</zorder>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="3" column="3">
<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"> <widget class="QPushButton" name="roiBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>40</width> <width>30</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>ROI</string> <string>R</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="1" column="3">
<widget class="QSlider" name="timeSlider"> <widget class="GradientWidget" name="gradientWidget" native="true">
<property name="maximum"> <property name="sizePolicy">
<number>65535</number> <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
</property> <horstretch>0</horstretch>
<property name="orientation"> <verstretch>100</verstretch>
<enum>Qt::Horizontal</enum> </sizepolicy>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1" colspan="2"> <item row="2" column="3">
<widget class="QPushButton" name="normBtn"> <widget class="QPushButton" name="normBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>50</width> <width>30</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>Norm</string> <string>N</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="4"> <item row="0" column="0" colspan="4">
<widget class="QGroupBox" name="normGroup"> <widget class="QGroupBox" name="normGroup">
<property name="title"> <property name="title">
<string>Normalization</string> <string>Normalization</string>
@ -188,7 +152,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="font"> <property name="font">
<font> <font>
@ -208,30 +172,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="6"> <item row="2" column="2">
<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"/> <widget class="QDoubleSpinBox" name="normXBlurSpin"/>
</item> </item>
<item row="4" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>X</string> <string>X</string>
@ -241,7 +185,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="3"> <item row="2" column="3">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
<string>Y</string> <string>Y</string>
@ -251,10 +195,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="4"> <item row="2" column="4">
<widget class="QDoubleSpinBox" name="normYBlurSpin"/> <widget class="QDoubleSpinBox" name="normYBlurSpin"/>
</item> </item>
<item row="4" column="5"> <item row="2" column="5">
<widget class="QLabel" name="label_10"> <widget class="QLabel" name="label_10">
<property name="text"> <property name="text">
<string>T</string> <string>T</string>
@ -264,20 +208,6 @@
</property> </property>
</widget> </widget>
</item> </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"> <item row="0" column="3">
<widget class="QRadioButton" name="normOffRadio"> <widget class="QRadioButton" name="normOffRadio">
<property name="text"> <property name="text">
@ -302,7 +232,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="6"> <item row="2" column="6">
<widget class="QDoubleSpinBox" name="normTBlurSpin"/> <widget class="QDoubleSpinBox" name="normTBlurSpin"/>
</item> </item>
</layout> </layout>
@ -311,6 +241,12 @@
</layout> </layout>
</widget> </widget>
<widget class="PlotWidget" name="roiPlot" native="true"> <widget class="PlotWidget" name="roiPlot" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>0</width>
@ -323,6 +259,12 @@
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>GradientWidget</class>
<extends>QWidget</extends>
<header>pyqtgraph.GradientWidget</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>GraphicsView</class> <class>GraphicsView</class>
<extends>QWidget</extends> <extends>QWidget</extends>

22
ObjectWorkaround.py Normal file
View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
class QObjectWorkaround:
def __init__(self):
self._qObj_ = QtCore.QObject()
def connect(self, *args):
if args[0] is self:
return QtCore.QObject.connect(self._qObj_, *args[1:])
else:
return QtCore.QObject.connect(self._qObj_, *args)
def disconnect(self, *args):
return QtCore.QObject.disconnect(self._qObj_, *args)
def emit(self, *args):
return QtCore.QObject.emit(self._qObj_, *args)
def blockSignals(self, b):
return self._qObj_.blockSignals(b)
class QGraphicsObject(QtGui.QGraphicsItem, QObjectWorkaround):
def __init__(self, *args):
QtGui.QGraphicsItem.__init__(self, *args)
QObjectWorkaround.__init__(self)

View File

@ -20,6 +20,8 @@ This class is very heavily featured:
from graphicsItems import * from graphicsItems import *
from plotConfigTemplate import * from plotConfigTemplate import *
from PyQt4 import QtGui, QtCore, QtSvg from PyQt4 import QtGui, QtCore, QtSvg
#from ObjectWorkaround import *
#tryWorkaround(QtCore, QtGui)
import weakref import weakref
try: try:
@ -42,7 +44,7 @@ class PlotItem(QtGui.QGraphicsWidget):
lastFileDir = None lastFileDir = None
managers = {} managers = {}
def __init__(self, parent=None): def __init__(self, parent=None, name=None):
QtGui.QGraphicsWidget.__init__(self, parent) QtGui.QGraphicsWidget.__init__(self, parent)
## Set up control buttons ## Set up control buttons
@ -56,6 +58,7 @@ class PlotItem(QtGui.QGraphicsWidget):
for b in [self.ctrlBtn, self.autoBtn]: for b in [self.ctrlBtn, self.autoBtn]:
proxy = QtGui.QGraphicsProxyWidget(self) proxy = QtGui.QGraphicsProxyWidget(self)
proxy.setWidget(b) proxy.setWidget(b)
proxy.setAcceptHoverEvents(False)
b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt") b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked) QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale) QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
@ -124,7 +127,7 @@ class PlotItem(QtGui.QGraphicsWidget):
## Wrap a few methods from viewBox ## Wrap a few methods from viewBox
for m in ['setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect']: for m in ['setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect', 'setMouseEnabled']:
setattr(self, m, getattr(self.vb, m)) setattr(self, m, getattr(self.vb, m))
self.items = [] self.items = []
@ -178,9 +181,13 @@ class PlotItem(QtGui.QGraphicsWidget):
QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), 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.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
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(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode) 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.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked) QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
#QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid) #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(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
@ -216,6 +223,10 @@ class PlotItem(QtGui.QGraphicsWidget):
self.showScale('left', True) self.showScale('left', True)
self.showScale('bottom', True) self.showScale('bottom', True)
if name is not None:
self.registerPlot(name)
def __del__(self): def __del__(self):
if self.manager is not None: if self.manager is not None:
self.manager.removeWidget(self.name) self.manager.removeWidget(self.name)
@ -251,6 +262,13 @@ class PlotItem(QtGui.QGraphicsWidget):
print " error during update. Referrers are:", refs print " error during update. Referrers are:", refs
raise raise
def updateGrid(self, *args):
g = self.ctrl.gridGroup.isChecked()
if g:
g = self.ctrl.gridAlphaSlider.value()
for k in self.scales:
self.scales[k]['item'].setGrid(g)
def viewGeometry(self): def viewGeometry(self):
"""return the screen geometry of the viewbox""" """return the screen geometry of the viewbox"""
v = self.scene().views()[0] v = self.scene().views()[0]
@ -261,6 +279,8 @@ class PlotItem(QtGui.QGraphicsWidget):
return wr return wr
def viewChanged(self, *args): def viewChanged(self, *args):
self.emit(QtCore.SIGNAL('viewChanged'), *args) self.emit(QtCore.SIGNAL('viewChanged'), *args)
@ -273,23 +293,31 @@ class PlotItem(QtGui.QGraphicsWidget):
def yLinkComboChanged(self): def yLinkComboChanged(self):
self.setYLink(str(self.ctrl.yLinkCombo.currentText())) self.setYLink(str(self.ctrl.yLinkCombo.currentText()))
def setXLink(self, plotName=None): def setXLink(self, plot=None):
"""Link this plot's X axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
if isinstance(plot, basestring):
if self.manager is None: if self.manager is None:
return return
if self.xLinkPlot is not None: if self.xLinkPlot is not None:
self.manager.unlinkX(self, self.xLinkPlot) self.manager.unlinkX(self, self.xLinkPlot)
plot = self.manager.getWidget(plotName) plot = self.manager.getWidget(plot)
if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
plot = plot.getPlotItem()
self.xLinkPlot = plot self.xLinkPlot = plot
if plot is not None: if plot is not None:
self.setManualXScale() self.setManualXScale()
self.manager.linkX(self, plot) self.manager.linkX(self, plot)
def setYLink(self, plotName=None): def setYLink(self, plot=None):
"""Link this plot's Y axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
if isinstance(plot, basestring):
if self.manager is None: if self.manager is None:
return return
if self.yLinkPlot is not None: if self.yLinkPlot is not None:
self.manager.unlinkY(self, self.yLinkPlot) self.manager.unlinkY(self, self.yLinkPlot)
plot = self.manager.getWidget(plotName) plot = self.manager.getWidget(plot)
if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
plot = plot.getPlotItem()
self.yLinkPlot = plot self.yLinkPlot = plot
if plot is not None: if plot is not None:
self.setManualYScale() self.setManualYScale()
@ -339,6 +367,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.recomputeAverages() self.recomputeAverages()
def recomputeAverages(self): def recomputeAverages(self):
if not self.ctrl.averageGroup.isChecked():
return
for k in self.avgCurves: for k in self.avgCurves:
self.removeItem(self.avgCurves[k][1]) self.removeItem(self.avgCurves[k][1])
#Qwt.QwtPlotCurve.detach(self.avgCurves[k][1]) #Qwt.QwtPlotCurve.detach(self.avgCurves[k][1])
@ -406,6 +436,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.vb.setMouseEnabled(*state) self.vb.setMouseEnabled(*state)
def xRangeChanged(self, _, range): def xRangeChanged(self, _, range):
if any(isnan(range)) or any(isinf(range)):
raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
self.ctrl.xMinText.setText('%0.5g' % range[0]) self.ctrl.xMinText.setText('%0.5g' % range[0])
self.ctrl.xMaxText.setText('%0.5g' % range[1]) self.ctrl.xMaxText.setText('%0.5g' % range[1])
@ -423,6 +455,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.emit(QtCore.SIGNAL('xRangeChanged'), self, range) self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
def yRangeChanged(self, _, range): def yRangeChanged(self, _, range):
if any(isnan(range)) or any(isinf(range)):
raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
self.ctrl.yMinText.setText('%0.5g' % range[0]) self.ctrl.yMinText.setText('%0.5g' % range[0])
self.ctrl.yMaxText.setText('%0.5g' % range[1]) self.ctrl.yMaxText.setText('%0.5g' % range[1])
@ -511,18 +545,25 @@ class PlotItem(QtGui.QGraphicsWidget):
if not item in self.items: if not item in self.items:
return return
self.items.remove(item) self.items.remove(item)
if item.scene() is not None:
self.vb.removeItem(item) self.vb.removeItem(item)
if item in self.curves: if item in self.curves:
self.curves.remove(item) self.curves.remove(item)
self.updateDecimation() self.updateDecimation()
self.updateParamList() self.updateParamList()
QtCore.QObject.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged) item.connect(QtCore.SIGNAL('plotChanged'), self.plotChanged)
def clear(self): def clear(self):
for i in self.items[:]: for i in self.items[:]:
self.removeItem(i) self.removeItem(i)
self.avgCurves = {} self.avgCurves = {}
def clearPlots(self):
for i in self.curves[:]:
self.removeItem(i)
self.avgCurves = {}
def plot(self, data=None, x=None, clear=False, params=None, pen=None): def plot(self, data=None, x=None, clear=False, params=None, pen=None):
if clear: if clear:
self.clear() self.clear()
@ -549,6 +590,8 @@ class PlotItem(QtGui.QGraphicsWidget):
return curve return curve
def addCurve(self, c, params=None): def addCurve(self, c, params=None):
if params is None:
params = {}
c.setMeta(params) c.setMeta(params)
self.curves.append(c) self.curves.append(c)
#Qwt.QwtPlotCurve.attach(c, self) #Qwt.QwtPlotCurve.attach(c, self)
@ -569,7 +612,7 @@ class PlotItem(QtGui.QGraphicsWidget):
if self.ctrl.averageGroup.isChecked(): if self.ctrl.averageGroup.isChecked():
self.addAvgCurve(c) self.addAvgCurve(c)
QtCore.QObject.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged) c.connect(QtCore.SIGNAL('plotChanged'), self.plotChanged)
self.plotChanged() self.plotChanged()
def plotChanged(self, curve=None): def plotChanged(self, curve=None):
@ -587,7 +630,7 @@ class PlotItem(QtGui.QGraphicsWidget):
mn = cmn mn = cmn
if mx is None or cmx > mx: if mx is None or cmx > mx:
mx = cmx mx = cmx
if mn is None or mx is None: if mn is None or mx is None or any(isnan([mn, mx])) or any(isinf([mn, mx])):
continue continue
if mn == mx: if mn == mx:
mn -= 1 mn -= 1
@ -632,24 +675,58 @@ class PlotItem(QtGui.QGraphicsWidget):
if fileName is None: if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName() fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName) fileName = str(fileName)
PlotItem.lastFileDir = os.path.dirname(fileName)
self.svg = QtSvg.QSvgGenerator() self.svg = QtSvg.QSvgGenerator()
self.svg.setFileName(fileName) self.svg.setFileName(fileName)
res = 120. res = 120.
self.svg.setResolution(res) #bounds = self.mapRectToScene(self.boundingRect())
self.svg.setSize(QtCore.QSize(self.size().width(), self.size().height())) view = self.scene().views()[0]
painter = QtGui.QPainter(self.svg) bounds = view.viewport().rect()
#self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect())) bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())
items = self.scene().items()
self.scene().views()[0].drawItems(painter, len(items), items)
self.svg.setResolution(res)
#self.svg.setSize(QtCore.QSize(self.size().width(), self.size().height()))
self.svg.setViewBox(bounds)
self.svg.setSize(QtCore.QSize(bounds.width(), bounds.height()))
painter = QtGui.QPainter(self.svg)
#self.scene().render(painter, QtCore.QRectF(), view.mapToScene(bounds).boundingRect())
#items = self.scene().items()
#self.scene().views()[0].drawItems(painter, len(items), items)
#print view, type(view)
view.render(painter, bounds)
painter.end()
## Workaround to set pen widths correctly
import re
data = open(fileName).readlines()
for i in range(len(data)):
line = data[i]
m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
if m is not None:
#print "Matched group:", line
g = m.groups()
matrix = map(float, g[2].split(','))
#print "matrix:", matrix
scale = max(abs(matrix[0]), abs(matrix[3]))
if scale == 0 or scale == 1.0:
continue
data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
#print "old line:", line
#print "new line:", data[i]
open(fileName, 'w').write(''.join(data))
def writeImage(self, fileName=None): def writeImage(self, fileName=None):
if fileName is None: if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName() fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName) fileName = str(fileName)
PlotItem.lastFileDir = os.path.dirname(fileName)
self.png = QtGui.QImage(int(self.size().width()), int(self.size().height()), QtGui.QImage.Format_ARGB32) self.png = QtGui.QImage(int(self.size().width()), int(self.size().height()), QtGui.QImage.Format_ARGB32)
painter = QtGui.QPainter(self.png) painter = QtGui.QPainter(self.png)
painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing) painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing)
@ -657,6 +734,29 @@ class PlotItem(QtGui.QGraphicsWidget):
painter.end() painter.end()
self.png.save(fileName) self.png.save(fileName)
def writeCsv(self, fileName=None):
if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName)
PlotItem.lastFileDir = os.path.dirname(fileName)
fd = open(fileName, 'w')
data = [c.getData() for c in self.curves]
i = 0
while True:
done = True
for d in data:
if i < len(d[0]):
fd.write('%g,%g,'%(d[0][i], d[1][i]))
done = False
else:
fd.write(' , ,')
fd.write('\n')
if done:
break
i += 1
fd.close()
def saveState(self): def saveState(self):
if not HAVE_WIDGETGROUP: if not HAVE_WIDGETGROUP:
@ -672,8 +772,18 @@ class PlotItem(QtGui.QGraphicsWidget):
raise Exception("State save/restore requires WidgetGroup class.") raise Exception("State save/restore requires WidgetGroup class.")
if 'paramList' in state: if 'paramList' in state:
self.paramList = state['paramList'].copy() self.paramList = state['paramList'].copy()
self.stateGroup.setState(state) self.stateGroup.setState(state)
self.updateSpectrumMode()
self.updateDownsampling()
self.updateAlpha()
self.updateDecimation()
self.stateGroup.setState(state)
self.updateXScale()
self.updateYScale()
self.updateParamList() self.updateParamList()
#print "\nRESTORE %s:\n" % str(self.name), state #print "\nRESTORE %s:\n" % str(self.name), state
#print "Restoring state. averageGroup.isChecked(): %s state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup'])) #print "Restoring state. averageGroup.isChecked(): %s state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
#avg = self.ctrl.averageGroup.isChecked() #avg = self.ctrl.averageGroup.isChecked()
@ -787,7 +897,6 @@ class PlotItem(QtGui.QGraphicsWidget):
self.mouseScreenPos = ev.screenPos() self.mouseScreenPos = ev.screenPos()
def ctrlBtnClicked(self): def ctrlBtnClicked(self):
#print self.mousePos
self.ctrlMenu.popup(self.mouseScreenPos) self.ctrlMenu.popup(self.mouseScreenPos)
#def _checkLabelKey(self, key): #def _checkLabelKey(self, key):
@ -923,6 +1032,8 @@ class PlotItem(QtGui.QGraphicsWidget):
#self.fileDialog.setDirectory(PlotItem.lastFileDir) #self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile) self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
if PlotItem.lastFileDir is not None:
self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.show() self.fileDialog.show()
QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg) QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
@ -934,11 +1045,23 @@ class PlotItem(QtGui.QGraphicsWidget):
self.fileDialog = QtGui.QFileDialog() self.fileDialog = QtGui.QFileDialog()
#if PlotItem.lastFileDir is not None: #if PlotItem.lastFileDir is not None:
#self.fileDialog.setDirectory(PlotItem.lastFileDir) #self.fileDialog.setDirectory(PlotItem.lastFileDir)
if PlotItem.lastFileDir is not None:
self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile) self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
self.fileDialog.show() self.fileDialog.show()
QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage) QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage)
def saveCsvClicked(self):
self.fileDialog = QtGui.QFileDialog()
#if PlotItem.lastFileDir is not None:
#self.fileDialog.setDirectory(PlotItem.lastFileDir)
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.writeCsv)
#def imgFileSelected(self, fileName): #def imgFileSelected(self, fileName):
##PlotWidget.lastFileDir = os.path.split(fileName)[0] ##PlotWidget.lastFileDir = os.path.split(fileName)[0]
#self.writeImage(str(fileName)) #self.writeImage(str(fileName))

View File

@ -11,17 +11,26 @@ import exceptions
class PlotWidget(GraphicsView): class PlotWidget(GraphicsView):
"""Widget implementing a graphicsView with a single PlotItem inside.""" """Widget implementing a graphicsView with a single PlotItem inside."""
def __init__(self, parent=None): def __init__(self, parent=None, **kargs):
GraphicsView.__init__(self, parent) GraphicsView.__init__(self, parent)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.enableMouse(False) self.enableMouse(False)
self.plotItem = PlotItem() self.plotItem = PlotItem(**kargs)
self.setCentralItem(self.plotItem) self.setCentralItem(self.plotItem)
## Explicitly wrap methods from plotItem ## Explicitly wrap methods from plotItem
for m in ['addItem', 'autoRange', 'clear']: for m in ['addItem', 'removeItem', 'autoRange', 'clear', 'setXRange', 'setYRange']:
setattr(self, m, getattr(self.plotItem, m)) setattr(self, m, getattr(self.plotItem, m))
QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged) QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged)
#def __dtor__(self):
##print "Called plotWidget sip destructor"
#self.quit()
def quit(self):
self.plotItem.clear()
self.scene().clear()
def __getattr__(self, attr): ## implicitly wrap methods from plotItem def __getattr__(self, attr): ## implicitly wrap methods from plotItem
if hasattr(self.plotItem, attr): if hasattr(self.plotItem, attr):
m = getattr(self.plotItem, attr) m = getattr(self.plotItem, attr)
@ -41,3 +50,5 @@ class PlotWidget(GraphicsView):
def restoreState(self, state): def restoreState(self, state):
return self.plotItem.restoreState(state) return self.plotItem.restoreState(state)
def getPlotItem(self):
return self.plotItem

View File

@ -4,7 +4,7 @@ import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from pyqtgraph.ImageView import * from pyqtgraph.ImageView import *
from numpy import random from numpy import random, linspace
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from scipy.ndimage import * from scipy.ndimage import *
@ -17,14 +17,27 @@ win.setCentralWidget(imv)
win.show() win.show()
## Create random 3D data set ## Create random 3D data set
img = gaussian_filter(random.random((200, 200)), (5, 5)) * 5 img = gaussian_filter(random.normal(size=(200, 200)), (5, 5)) * 20 + 100
data = random.random((100, 200, 200)) img = img[newaxis,:,:]
data += img decay = exp(-linspace(0,0.3,100))[:,newaxis,newaxis]
for i in range(data.shape[0]): data = random.normal(size=(100, 200, 200))
data[i] += exp(-(2.*i)/data.shape[0]) data += img * decay
data += 10
#for i in range(data.shape[0]):
#data[i] += 10*exp(-(2.*i)/data.shape[0])
data += 2
## Add time-varying signal
sig = zeros(data.shape[0])
sig[30:] += exp(-linspace(1,10, 70))
sig[40:] += exp(-linspace(1,10, 60))
sig[70:] += exp(-linspace(1,10, 30))
sig = sig[:,newaxis,newaxis] * 3
data[:,50:60,50:60] += sig
## Display the data ## Display the data
imv.setImage(data) imv.setImage(data, xvals=linspace(1., 3., data.shape[0]))
app.exec_() app.exec_()

View File

@ -34,18 +34,26 @@ rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 1))
rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100))) rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100)))
pw.addItem(rect) pw.addItem(rect)
pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(255, 255, 255, 10)), 5) #pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(255, 255, 255, 50)), 5)
pen.setCosmetic(True) #pen.setCosmetic(True)
#pen.setJoinStyle(QtCore.Qt.MiterJoin) #pen.setJoinStyle(QtCore.Qt.MiterJoin)
p1.setShadowPen(pen) #p1.setShadowPen(pen)
p1.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255, 50))) p1.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255)))
#l1 = QtGui.QGraphicsLineItem(0, 2, 2, 3) #l1 = QtGui.QGraphicsLineItem(0, 2, 2, 3)
#l1.setPen(QtGui.QPen(QtGui.QColor(255,0,0))) #l1.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
l2 = InfiniteLine(pw, 1.5, 0)
#l2 = InfiniteLine(pw2, 1.5, 90, movable=True)
#
#lr1 = LinearRegionItem(pw2, 'vertical', [1.1, 1.3])
#pw2.addItem(lr1)
#lr2 = LinearRegionItem(pw2, 'horizontal', [50, 100])
#pw2.addItem(lr2)
#l3 = InfiniteLine(pw, [1.5, 1.5], 45) #l3 = InfiniteLine(pw, [1.5, 1.5], 45)
#pw.addItem(l1) #pw.addItem(l1)
pw.addItem(l2) #pw2.addItem(l2)
#pw.addItem(l3) #pw.addItem(l3)
pw3.plot(array([100000]*100)) pw3.plot(array([100000]*100))
@ -72,12 +80,14 @@ updateData()
pw.autoRange() pw.autoRange()
t = QtCore.QTimer() t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData) QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
t.start(50) t.start(50)
for i in range(0, 5): for i in range(0, 5):
for j in range(0, 3): for j in range(0, 3):
yd, xd = rand(10000) yd, xd = rand(10000)
pw2.plot(yd*(j+1), xd, params={'iter': i, 'val': j}) pw2.plot(yd*(j+1), xd, params={'iter': i, 'val': j})
app.exec_() #app.exec_()

View File

@ -19,7 +19,7 @@ class Win(QtGui.QMainWindow):
pass pass
w = Win() w = Win()
v = GraphicsView() v = GraphicsView(useOpenGL=False)
v.invertY(True) v.invertY(True)
v.setAspectLocked(True) v.setAspectLocked(True)
v.enableMouse(True) v.enableMouse(True)
@ -72,6 +72,9 @@ elroi = EllipseROI([110, 10], [30, 20])
s.addItem(elroi) s.addItem(elroi)
croi = CircleROI([110, 50], [20, 20]) croi = CircleROI([110, 50], [20, 20])
s.addItem(croi) s.addItem(croi)
troi = PolygonROI([[0,0], [1,0], [0,1]])
s.addItem(troi)
def updateImg(roi): def updateImg(roi):
global im1, im2, im3, im4, arr global im1, im2, im3, im4, arr
@ -80,11 +83,11 @@ def updateImg(roi):
arr2 = roi.getArrayRegion(arr, img=im2) arr2 = roi.getArrayRegion(arr, img=im2)
im4.updateImage(arr2, autoRange=True) im4.updateImage(arr2, autoRange=True)
roi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi)) roi.connect(roi, QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi))
roi2.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi2)) roi2.connect(roi2, QtCore.SIGNAL('regionChanged'), lambda: updateImg(roi2))
croi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(croi)) croi.connect(croi, QtCore.SIGNAL('regionChanged'), lambda: updateImg(croi))
elroi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(elroi)) elroi.connect(elroi, QtCore.SIGNAL('regionChanged'), lambda: updateImg(elroi))
mlroi.connect(QtCore.SIGNAL('regionChanged'), lambda: updateImg(mlroi)) mlroi.connect(mlroi, QtCore.SIGNAL('regionChanged'), lambda: updateImg(mlroi))
v.setRange(QtCore.QRect(-2, -2, 220, 220)) v.setRange(QtCore.QRect(-2, -2, 220, 220))

View File

@ -30,7 +30,30 @@ vb = ViewBox()
p1 = PlotCurveItem() p1 = PlotCurveItem()
vb.addItem(p1) vb.addItem(p1)
vl.addWidget(gv) vl.addWidget(gv)
rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 1))
class movableRect(QtGui.QGraphicsRectItem):
def __init__(self, *args):
QtGui.QGraphicsRectItem.__init__(self, *args)
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, ev):
self.savedPen = self.pen()
self.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255)))
ev.ignore()
def hoverLeaveEvent(self, ev):
self.setPen(self.savedPen)
ev.ignore()
def mousePressEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton:
ev.accept()
self.pressDelta = self.mapToParent(ev.pos()) - self.pos()
else:
ev.ignore()
def mouseMoveEvent(self, ev):
self.setPos(self.mapToParent(ev.pos()) - self.pressDelta)
#rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 1))
rect = movableRect(QtCore.QRectF(0, 0, 1, 1))
rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100))) rect.setPen(QtGui.QPen(QtGui.QColor(100, 200, 100)))
vb.addItem(rect) vb.addItem(rect)
@ -67,4 +90,4 @@ t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData) QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
t.start(50) t.start(50)
app.exec_() #app.exec_()

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,16 @@ Distributed under MIT/X11 license. See license.txt for more infomation.
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from PlotWidget import * from PlotWidget import *
from ImageView import * from ImageView import *
QAPP = None
class PlotWindow(QtGui.QMainWindow): class PlotWindow(QtGui.QMainWindow):
def __init__(self, title=None): def __init__(self, title=None):
if QtGui.QApplication.instance() is None:
global QAPP
QAPP = QtGui.QApplication([])
QtGui.QMainWindow.__init__(self) QtGui.QMainWindow.__init__(self)
self.cw = PlotWidget() self.cw = PlotWidget()
self.setCentralWidget(self.cw) self.setCentralWidget(self.cw)
for m in ['plot', 'autoRange', 'addItem', 'setLabel', 'clear']: for m in ['plot', 'autoRange', 'addItem', 'removeItem', 'setLabel', 'clear']:
setattr(self, m, getattr(self.cw, m)) setattr(self, m, getattr(self.cw, m))
if title is not None: if title is not None:
self.setWindowTitle(title) self.setWindowTitle(title)
@ -22,10 +25,13 @@ class PlotWindow(QtGui.QMainWindow):
class ImageWindow(QtGui.QMainWindow): class ImageWindow(QtGui.QMainWindow):
def __init__(self, title=None): def __init__(self, title=None):
if QtGui.QApplication.instance() is None:
global QAPP
QAPP = QtGui.QApplication([])
QtGui.QMainWindow.__init__(self) QtGui.QMainWindow.__init__(self)
self.cw = ImageView() self.cw = ImageView()
self.setCentralWidget(self.cw) self.setCentralWidget(self.cw)
for m in ['setImage', 'autoRange', 'addItem']: for m in ['setImage', 'autoRange', 'addItem', 'removeItem', 'blackLevel', 'whiteLevel', 'imageItem']:
setattr(self, m, getattr(self.cw, m)) setattr(self, m, getattr(self.cw, m))
if title is not None: if title is not None:
self.setWindowTitle(title) self.setWindowTitle(title)

View File

@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'plotConfigTemplate.ui' # Form implementation generated from reading ui file 'plotConfigTemplate.ui'
# #
# Created: Mon Mar 29 22:40:47 2010 # Created: Sat Jul 17 00:28:43 2010
# by: PyQt4 UI code generator 4.6 # by: PyQt4 UI code generator 4.7.2
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -12,7 +12,7 @@ from PyQt4 import QtCore, QtGui
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
Form.resize(210, 340) Form.resize(250, 340)
Form.setMaximumSize(QtCore.QSize(250, 350)) Form.setMaximumSize(QtCore.QSize(250, 350))
self.gridLayout_3 = QtGui.QGridLayout(Form) self.gridLayout_3 = QtGui.QGridLayout(Form)
self.gridLayout_3.setMargin(0) self.gridLayout_3.setMargin(0)
@ -187,6 +187,7 @@ class Ui_Form(object):
self.verticalLayout_3.addWidget(self.alphaGroup) self.verticalLayout_3.addWidget(self.alphaGroup)
self.gridGroup = QtGui.QGroupBox(self.tab_3) self.gridGroup = QtGui.QGroupBox(self.tab_3)
self.gridGroup.setCheckable(True) self.gridGroup.setCheckable(True)
self.gridGroup.setChecked(False)
self.gridGroup.setObjectName("gridGroup") self.gridGroup.setObjectName("gridGroup")
self.verticalLayout_4 = QtGui.QVBoxLayout(self.gridGroup) self.verticalLayout_4 = QtGui.QVBoxLayout(self.gridGroup)
self.verticalLayout_4.setObjectName("verticalLayout_4") self.verticalLayout_4.setObjectName("verticalLayout_4")
@ -227,6 +228,9 @@ class Ui_Form(object):
self.saveMaBtn = QtGui.QPushButton(self.tab_4) self.saveMaBtn = QtGui.QPushButton(self.tab_4)
self.saveMaBtn.setObjectName("saveMaBtn") self.saveMaBtn.setObjectName("saveMaBtn")
self.gridLayout_6.addWidget(self.saveMaBtn, 2, 0, 1, 1) self.gridLayout_6.addWidget(self.saveMaBtn, 2, 0, 1, 1)
self.saveCsvBtn = QtGui.QPushButton(self.tab_4)
self.saveCsvBtn.setObjectName("saveCsvBtn")
self.gridLayout_6.addWidget(self.saveCsvBtn, 3, 0, 1, 1)
self.gridLayout_7.addLayout(self.gridLayout_6, 0, 1, 1, 1) self.gridLayout_7.addLayout(self.gridLayout_6, 0, 1, 1, 1)
spacerItem2 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) spacerItem2 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem2, 0, 2, 1, 1) self.gridLayout_7.addItem(spacerItem2, 0, 2, 1, 1)
@ -281,5 +285,6 @@ class Ui_Form(object):
self.saveSvgBtn.setText(QtGui.QApplication.translate("Form", "SVG", 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.saveImgBtn.setText(QtGui.QApplication.translate("Form", "Image", None, QtGui.QApplication.UnicodeUTF8))
self.saveMaBtn.setText(QtGui.QApplication.translate("Form", "MetaArray", None, QtGui.QApplication.UnicodeUTF8)) self.saveMaBtn.setText(QtGui.QApplication.translate("Form", "MetaArray", None, QtGui.QApplication.UnicodeUTF8))
self.saveCsvBtn.setText(QtGui.QApplication.translate("Form", "CSV", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), QtGui.QApplication.translate("Form", "Save", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), QtGui.QApplication.translate("Form", "Save", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>210</width> <width>250</width>
<height>340</height> <height>340</height>
</rect> </rect>
</property> </property>
@ -35,7 +35,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
@ -419,6 +419,9 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item> <item>
<widget class="QSlider" name="gridAlphaSlider"> <widget class="QSlider" name="gridAlphaSlider">
@ -514,6 +517,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QPushButton" name="saveCsvBtn">
<property name="text">
<string>CSV</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="0" column="2"> <item row="0" column="2">

View File

@ -10,25 +10,26 @@ of array data from ImageItems.
""" """
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
from numpy.linalg import norm from numpy.linalg import norm
import scipy.ndimage as ndimage import scipy.ndimage as ndimage
from Point import * from Point import *
from math import cos, sin from math import cos, sin
from ObjectWorkaround import *
def rectStr(r): def rectStr(r):
return "[%f, %f] + [%f, %f]" % (r.x(), r.y(), r.width(), r.height()) return "[%f, %f] + [%f, %f]" % (r.x(), r.y(), r.width(), r.height())
## Multiple inheritance not allowed in PyQt. Retarded workaround: # Multiple inheritance not allowed in PyQt. Retarded workaround:
class QObjectWorkaround: #class QObjectWorkaround:
def __init__(self): #def __init__(self):
self._qObj_ = QtCore.QObject() #self._qObj_ = QtCore.QObject()
def __getattr__(self, attr): #def __getattr__(self, attr):
if attr == '_qObj_': #if attr == '_qObj_':
raise Exception("QObjectWorkaround not initialized!") #raise Exception("QObjectWorkaround not initialized!")
return getattr(self._qObj_, attr) #return getattr(self._qObj_, attr)
def connect(self, *args): #def connect(self, *args):
return QtCore.QObject.connect(self._qObj_, *args) #return QtCore.QObject.connect(self._qObj_, *args)
class ROI(QtGui.QGraphicsItem, QObjectWorkaround): class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
@ -46,10 +47,10 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
self.state = {'pos': pos, 'size': size, 'angle': angle} self.state = {'pos': pos, 'size': size, 'angle': angle}
self.lastState = None self.lastState = None
self.setPos(pos) self.setPos(pos)
self.rotate(-angle) self.rotate(-angle * 180. / pi)
self.setZValue(10) self.setZValue(10)
self.handleSize = 4 self.handleSize = 5
self.invertible = invertible self.invertible = invertible
self.maxBounds = maxBounds self.maxBounds = maxBounds
@ -93,6 +94,10 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
pos = Point(pos) pos = Point(pos)
return self.addHandle({'type': 't', 'pos': pos, 'item': item}) return self.addHandle({'type': 't', 'pos': pos, 'item': item})
def addFreeHandle(self, pos, axes=None, item=None):
pos = Point(pos)
return self.addHandle({'type': 'f', 'pos': pos, 'item': item})
def addScaleHandle(self, pos, center, axes=None, item=None): def addScaleHandle(self, pos, center, axes=None, item=None):
pos = Point(pos) pos = Point(pos)
center = Point(center) center = Point(center)
@ -238,6 +243,9 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
snap = Point(self.snapSize, self.snapSize) snap = Point(self.snapSize, self.snapSize)
self.translate(p1-p0, snap=snap, update=False) self.translate(p1-p0, snap=snap, update=False)
elif h['type'] == 'f':
h['item'].setPos(self.mapFromScene(pos))
elif h['type'] == 's': elif h['type'] == 's':
#c = h['center'] #c = h['center']
#cs = c * self.state['size'] #cs = c * self.state['size']
@ -439,11 +447,14 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
return QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]) return QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1])
def paint(self, p, opt, widget): def paint(self, p, opt, widget):
p.save()
r = self.boundingRect() r = self.boundingRect()
p.setRenderHint(QtGui.QPainter.Antialiasing) p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen) p.setPen(self.pen)
p.drawRect(r) p.translate(r.left(), r.top())
p.scale(r.width(), r.height())
p.drawRect(0, 0, 1, 1)
p.restore()
def getArraySlice(self, data, img, axes=(0,1), returnSlice=True): 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. """Return a tuple of slice objects that can be used to slice the region from data covered by this ROI.
@ -602,15 +613,21 @@ class Handle(QtGui.QGraphicsItem):
#print " create item with parent", parent #print " create item with parent", parent
self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10) self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
QtGui.QGraphicsItem.__init__(self, parent) QtGui.QGraphicsItem.__init__(self, parent)
self.setFlag(self.ItemIgnoresTransformations)
self.setZValue(11) self.setZValue(11)
self.roi = [] self.roi = []
self.radius = radius self.radius = radius
self.typ = typ self.typ = typ
self.prepareGeometryChange() self.prepareGeometryChange()
self.pen = pen self.pen = pen
self.pen.setWidth(0)
self.pen.setCosmetic(True)
if typ == 't': if typ == 't':
self.sides = 4 self.sides = 4
self.startAng = pi/4 self.startAng = pi/4
elif typ == 'f':
self.sides = 4
self.startAng = pi/4
elif typ == 's': elif typ == 's':
self.sides = 4 self.sides = 4
self.startAng = 0 self.startAng = 0
@ -660,20 +677,24 @@ class Handle(QtGui.QGraphicsItem):
r[0].movePoint(r[1], pos, modifiers) r[0].movePoint(r[1], pos, modifiers)
def paint(self, p, opt, widget): def paint(self, p, opt, widget):
m = p.transform() ## determine rotation of transform
mi = m.inverted()[0] m = self.sceneTransform()
#mi = m.inverted()[0]
v = m.map(QtCore.QPointF(1, 0)) - m.map(QtCore.QPointF(0, 0))
va = arctan2(v.y(), v.x())
## Determine length of unit vector in painter's coords ## Determine length of unit vector in painter's coords
size = mi.map(Point(self.radius, self.radius)) - mi.map(Point(0, 0)) #size = mi.map(Point(self.radius, self.radius)) - mi.map(Point(0, 0))
size = (size.x()*size.x() + size.y() * size.y()) ** 0.5 #size = (size.x()*size.x() + size.y() * size.y()) ** 0.5
size = self.radius
bounds = QtCore.QRectF(-size, -size, size*2, size*2) bounds = QtCore.QRectF(-size, -size, size*2, size*2)
if bounds != self.bounds: if bounds != self.bounds:
self.bounds = bounds self.bounds = bounds
self.prepareGeometryChange() self.prepareGeometryChange()
p.setRenderHint(QtGui.QPainter.Antialiasing) p.setRenderHints(p.Antialiasing, True)
p.setPen(self.pen) p.setPen(self.pen)
ang = self.startAng ang = self.startAng + va
dt = 2*pi / self.sides dt = 2*pi / self.sides
for i in range(0, self.sides): for i in range(0, self.sides):
x1 = size * cos(ang) x1 = size * cos(ang)
@ -757,9 +778,9 @@ class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
for l in self.lines: for l in self.lines:
l.translatable = False l.translatable = False
#self.addToGroup(l) #self.addToGroup(l)
l.connect(QtCore.SIGNAL('regionChanged'), self.roiChangedEvent) l.connect(l, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
l.connect(QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent) l.connect(l, QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
l.connect(QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent) l.connect(l, QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
def paint(self, *args): def paint(self, *args):
pass pass
@ -835,3 +856,34 @@ class CircleROI(EllipseROI):
#self.addTranslateHandle([0.5, 0.5]) #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]) self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5])
class PolygonROI(ROI):
def __init__(self, positions):
ROI.__init__(self, [0,0], [100,100])
for p in positions:
self.addFreeHandle(p)
def movePoint(self, *args, **kargs):
ROI.movePoint(self, *args, **kargs)
self.prepareGeometryChange()
def paint(self, p, *args):
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen)
for i in range(len(self.handles)):
h1 = self.handles[i]['item'].pos()
h2 = self.handles[i-1]['item'].pos()
p.drawLine(h1, h2)
def boundingRect(self):
r = QtCore.QRectF()
for h in self.handles:
r |= self.mapFromItem(h['item'], h['item'].boundingRect()).boundingRect()
return r
def shape(self):
p = QtGui.QPainterPath()
p.moveTo(self.handles[0]['item'].pos())
for i in range(len(self.handles)):
p.lineTo(self.handles[i]['item'].pos())
return p