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
from Point import *
#from vector import *
import sys
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
viewed coordinate range. Also automatically creates a QGraphicsScene and a central QGraphicsWidget
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
enabled via enableMouse()."""
QtGui.QGraphicsView.__init__(self, *args)
self.setViewport(QtOpenGL.QGLWidget())
QtGui.QGraphicsView.__init__(self, parent)
self.useOpenGL(useOpenGL)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(0,0,0))
brush.setStyle(QtCore.Qt.SolidPattern)
@ -49,6 +50,7 @@ class GraphicsView(QtGui.QGraphicsView):
#self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate)
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.lockedViewports = []
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.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):
if self.centralWidget is not None:
self.scene().removeItem(self.centralWidget)
@ -77,6 +91,9 @@ class GraphicsView(QtGui.QGraphicsView):
def addItem(self, *args):
return self.scene().addItem(*args)
def removeItem(self, *args):
return self.scene().removeItem(*args)
def enableMouse(self, b=True):
self.mouseEnabled = b
self.autoPixelRange = (not b)
@ -128,6 +145,8 @@ class GraphicsView(QtGui.QGraphicsView):
v.setXRange(self.range, padding=0)
def visibleRange(self):
"""Return the boundaries of the view in scene coordinates"""
## easier to just return self.range ?
r = QtCore.QRectF(self.rect())
return self.viewportTransform().inverted()[0].mapRect(r)
@ -347,6 +366,16 @@ class GraphicsView(QtGui.QGraphicsView):
self.setRenderHints(rh)
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):
## Wait for click

View File

@ -17,6 +17,11 @@ from ImageViewTemplate import *
from graphicsItems import *
from widgets import ROI
from PyQt4 import QtCore, QtGui
import sys
from numpy import ndarray
import ptime
from SignalProxy import proxyConnect
class PlotROI(ROI):
def __init__(self, size):
@ -35,12 +40,25 @@ class ImageView(QtGui.QWidget):
self.ui = Ui_Form()
self.ui.setupUi(self)
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.autoPixelRange = False
self.ui.graphicsView.setAspectLocked(True)
self.ui.graphicsView.invertY()
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.scene.addItem(self.imageItem)
self.currentIndex = 0
@ -51,26 +69,46 @@ class ImageView(QtGui.QWidget):
self.roi.setZValue(20)
self.scene.addItem(self.roi)
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.roiTimeLine = InfiniteLine(self.ui.roiPlot, 0)
self.roiTimeLine.setPen(QtGui.QPen(QtGui.QColor(255, 255, 0, 200)))
self.ui.roiPlot.addItem(self.roiTimeLine)
self.timeLine = InfiniteLine(self.ui.roiPlot, 0, movable=True)
self.timeLine.setPen(QtGui.QPen(QtGui.QColor(255, 255, 0, 200)))
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 = []
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.keysPressed = {}
self.playTimer = QtCore.QTimer()
self.playRate = 0
self.lastPlayTime = 0
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))
QtCore.QObject.connect(self.ui.timeSlider, QtCore.SIGNAL('valueChanged(int)'), self.timeChanged)
QtCore.QObject.connect(self.ui.whiteSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage)
QtCore.QObject.connect(self.ui.blackSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateImage)
#QtCore.QObject.connect(self.ui.timeSlider, QtCore.SIGNAL('valueChanged(int)'), self.timeChanged)
self.timeLine.connect(QtCore.SIGNAL('positionChanged'), self.timeLineChanged)
#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)
self.roi.connect(QtCore.SIGNAL('regionChanged'), self.roiChanged)
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.normFrameCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normTimeRangeCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
QtCore.QObject.connect(self.ui.normStartSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateNorm)
QtCore.QObject.connect(self.ui.normStopSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateNorm)
QtCore.QObject.connect(self.playTimer, QtCore.SIGNAL('timeout()'), self.timeout)
##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')
def updateNorm(self):
for l, sl in zip(self.normLines, [self.ui.normStartSlider, self.ui.normStopSlider]):
if self.ui.normTimeRangeCheck.isChecked():
l.show()
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]
#def __dtor__(self):
##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:
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)
l.setPos(t)
def keyReleaseEvent(self, ev):
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.updateImage()
self.roiChanged()
def normToggled(self, 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):
if self.ui.roiBtn.isChecked():
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.ui.roiPlot.showScale('left', True)
else:
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):
if self.image is None:
@ -130,25 +308,41 @@ class ImageView(QtGui.QWidget):
self.roiCurve.setData(y=data, x=self.tVals)
#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
if hasattr(img, 'xvals'):
if xvals is not None:
self.tVals = xvals
elif hasattr(img, 'xvals'):
self.tVals = img.xvals(0)
else:
self.tVals = arange(img.shape[0])
self.ui.timeSlider.setValue(0)
#self.ui.timeSlider.setValue(0)
#self.ui.normStartSlider.setValue(0)
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
if img.ndim == 2:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
elif img.ndim == 3:
if img.shape[2] <= 3:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
else:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
elif img.ndim == 4:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
if axes is None:
if img.ndim == 2:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
elif img.ndim == 3:
if img.shape[2] <= 4:
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
else:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
elif img.ndim == 4:
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
self.imageDisp = None
@ -163,11 +357,36 @@ class ImageView(QtGui.QWidget):
if self.ui.roiBtn.isChecked():
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):
image = self.getProcessedImage()
self.ui.whiteSlider.setValue(self.ui.whiteSlider.maximum())
self.ui.blackSlider.setValue(0)
#self.ui.whiteSlider.setValue(self.ui.whiteSlider.maximum())
#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())
def autoRange(self):
@ -189,7 +408,7 @@ class ImageView(QtGui.QWidget):
return image
div = self.ui.normDivideRadio.isChecked()
norm = image.copy()
norm = image.view(ndarray).copy()
#if div:
#norm = ones(image.shape)
#else:
@ -198,8 +417,8 @@ class ImageView(QtGui.QWidget):
norm = norm.astype(float32)
if self.ui.normTimeRangeCheck.isChecked() and image.ndim == 3:
(sind, start) = self.timeIndex(self.ui.normStartSlider)
(eind, end) = self.timeIndex(self.ui.normStopSlider)
(sind, start) = self.timeIndex(self.normRgn.lines[0])
(eind, end) = self.timeIndex(self.normRgn.lines[1])
#print start, end, sind, eind
n = image[sind:eind+1].mean(axis=0)
n.shape = (1,) + n.shape
@ -216,17 +435,27 @@ class ImageView(QtGui.QWidget):
else:
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
def timeChanged(self):
(ind, time) = self.timeIndex(self.ui.timeSlider)
def timeLineChanged(self):
#(ind, time) = self.timeIndex(self.ui.timeSlider)
if self.ignoreTimeLine:
return
self.play(0)
(ind, time) = self.timeIndex(self.timeLine)
if ind != self.currentIndex:
self.currentIndex = ind
self.updateImage()
self.roiTimeLine.setPos(time)
#self.ui.roiPlot.replot()
#self.timeLine.setPos(time)
self.emit(QtCore.SIGNAL('timeChanged'), ind, time)
def updateImage(self):
@ -237,29 +466,38 @@ class ImageView(QtGui.QWidget):
image = self.getProcessedImage()
#print "update:", image.ndim, image.max(), image.min(), self.blackLevel(), self.whiteLevel()
if self.axes['t'] is None:
self.ui.timeSlider.hide()
#self.ui.timeSlider.hide()
self.imageItem.updateImage(image, white=self.whiteLevel(), black=self.blackLevel())
self.ui.roiPlot.hide()
self.ui.roiBtn.hide()
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())
def timeIndex(self, slider):
"""Return the time and frame index indicated by a slider"""
if self.image is None:
return (0,0)
v = slider.value()
vmax = slider.maximum()
f = float(v) / vmax
t = 0.0
#v = slider.value()
#vmax = slider.maximum()
#f = float(v) / vmax
t = slider.value()
#t = 0.0
#xv = self.image.xvals('Time')
xv = self.tVals
if xv is None:
ind = int(f * self.image.shape[0])
ind = int(t)
#ind = int(f * self.image.shape[0])
else:
if len(xv) < 2:
return (0,0)
totTime = xv[-1] + (xv[-1]-xv[-2])
t = f * totTime
#t = f * totTime
inds = argwhere(xv < t)
if len(inds) < 1:
return (0,t)
@ -268,8 +506,10 @@ class ImageView(QtGui.QWidget):
return ind, t
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):
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'
#
# Created: Mon Mar 29 22:40:48 2010
# by: PyQt4 UI code generator 4.6
# Created: Sat Jul 17 13:05:44 2010
# by: PyQt4 UI code generator 4.5.4
#
# WARNING! All changes made in this file will be lost!
@ -12,7 +12,7 @@ from PyQt4 import QtCore, QtGui
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(757, 495)
Form.resize(726, 588)
self.verticalLayout = QtGui.QVBoxLayout(Form)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setMargin(0)
@ -23,52 +23,45 @@ class Ui_Form(object):
self.layoutWidget = QtGui.QWidget(self.splitter)
self.layoutWidget.setObjectName("layoutWidget")
self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.gridLayout.setObjectName("gridLayout")
self.graphicsView = GraphicsView(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHorizontalStretch(10)
sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
self.graphicsView.setSizePolicy(sizePolicy)
self.graphicsView.setObjectName("graphicsView")
self.gridLayout.addWidget(self.graphicsView, 0, 0, 3, 1)
self.blackSlider = QtGui.QSlider(self.layoutWidget)
self.blackSlider.setMaximum(4096)
self.blackSlider.setOrientation(QtCore.Qt.Vertical)
self.blackSlider.setInvertedAppearance(False)
self.blackSlider.setInvertedControls(False)
self.blackSlider.setTickPosition(QtGui.QSlider.TicksBelow)
self.blackSlider.setTickInterval(410)
self.blackSlider.setObjectName("blackSlider")
self.gridLayout.addWidget(self.blackSlider, 0, 1, 1, 1)
self.whiteSlider = QtGui.QSlider(self.layoutWidget)
self.whiteSlider.setMaximum(4096)
self.whiteSlider.setProperty("value", 4096)
self.whiteSlider.setOrientation(QtCore.Qt.Vertical)
self.whiteSlider.setObjectName("whiteSlider")
self.gridLayout.addWidget(self.whiteSlider, 0, 2, 1, 2)
self.label = QtGui.QLabel(self.layoutWidget)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 1, 1, 1, 1)
self.label_2 = QtGui.QLabel(self.layoutWidget)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 1, 2, 1, 1)
self.gridLayout.addWidget(self.graphicsView, 1, 0, 3, 1)
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.setObjectName("roiBtn")
self.gridLayout.addWidget(self.roiBtn, 2, 1, 1, 3)
self.timeSlider = QtGui.QSlider(self.layoutWidget)
self.timeSlider.setMaximum(65535)
self.timeSlider.setOrientation(QtCore.Qt.Horizontal)
self.timeSlider.setObjectName("timeSlider")
self.gridLayout.addWidget(self.timeSlider, 4, 0, 1, 1)
self.gridLayout.addWidget(self.roiBtn, 3, 3, 1, 1)
self.gradientWidget = GradientWidget(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(100)
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.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.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.setObjectName("normGroup")
self.gridLayout_2 = QtGui.QGridLayout(self.normGroup)
@ -102,44 +95,28 @@ class Ui_Form(object):
font.setBold(True)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 4, 0, 1, 1)
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
self.normROICheck = QtGui.QCheckBox(self.normGroup)
self.normROICheck.setObjectName("normROICheck")
self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1)
self.normStartSlider = QtGui.QSlider(self.normGroup)
self.normStartSlider.setMaximum(65535)
self.normStartSlider.setOrientation(QtCore.Qt.Horizontal)
self.normStartSlider.setObjectName("normStartSlider")
self.gridLayout_2.addWidget(self.normStartSlider, 2, 0, 1, 6)
self.normStopSlider = QtGui.QSlider(self.normGroup)
self.normStopSlider.setMaximum(65535)
self.normStopSlider.setOrientation(QtCore.Qt.Horizontal)
self.normStopSlider.setObjectName("normStopSlider")
self.gridLayout_2.addWidget(self.normStopSlider, 3, 0, 1, 6)
self.normXBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normXBlurSpin.setObjectName("normXBlurSpin")
self.gridLayout_2.addWidget(self.normXBlurSpin, 4, 2, 1, 1)
self.gridLayout_2.addWidget(self.normXBlurSpin, 2, 2, 1, 1)
self.label_8 = QtGui.QLabel(self.normGroup)
self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_8.setObjectName("label_8")
self.gridLayout_2.addWidget(self.label_8, 4, 1, 1, 1)
self.gridLayout_2.addWidget(self.label_8, 2, 1, 1, 1)
self.label_9 = QtGui.QLabel(self.normGroup)
self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_9.setObjectName("label_9")
self.gridLayout_2.addWidget(self.label_9, 4, 3, 1, 1)
self.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1)
self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
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.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_10.setObjectName("label_10")
self.gridLayout_2.addWidget(self.label_10, 4, 5, 1, 1)
self.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.gridLayout_2.addWidget(self.label_10, 2, 5, 1, 1)
self.normOffRadio = QtGui.QRadioButton(self.normGroup)
self.normOffRadio.setChecked(True)
self.normOffRadio.setObjectName("normOffRadio")
@ -152,9 +129,14 @@ class Ui_Form(object):
self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
self.normTBlurSpin.setObjectName("normTBlurSpin")
self.gridLayout_2.addWidget(self.normTBlurSpin, 4, 6, 1, 1)
self.gridLayout.addWidget(self.normGroup, 5, 0, 1, 4)
self.gridLayout_2.addWidget(self.normTBlurSpin, 2, 6, 1, 1)
self.gridLayout.addWidget(self.normGroup, 0, 0, 1, 4)
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.setObjectName("roiPlot")
self.verticalLayout.addWidget(self.splitter)
@ -164,10 +146,8 @@ class Ui_Form(object):
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Form", "B", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("Form", "W", None, QtGui.QApplication.UnicodeUTF8))
self.roiBtn.setText(QtGui.QApplication.translate("Form", "ROI", None, QtGui.QApplication.UnicodeUTF8))
self.normBtn.setText(QtGui.QApplication.translate("Form", "Norm", None, QtGui.QApplication.UnicodeUTF8))
self.roiBtn.setText(QtGui.QApplication.translate("Form", "R", None, QtGui.QApplication.UnicodeUTF8))
self.normBtn.setText(QtGui.QApplication.translate("Form", "N", None, QtGui.QApplication.UnicodeUTF8))
self.normGroup.setTitle(QtGui.QApplication.translate("Form", "Normalization", None, QtGui.QApplication.UnicodeUTF8))
self.normSubtractRadio.setText(QtGui.QApplication.translate("Form", "Subtract", None, QtGui.QApplication.UnicodeUTF8))
self.normDivideRadio.setText(QtGui.QApplication.translate("Form", "Divide", None, QtGui.QApplication.UnicodeUTF8))
@ -178,11 +158,10 @@ class Ui_Form(object):
self.label_8.setText(QtGui.QApplication.translate("Form", "X", None, QtGui.QApplication.UnicodeUTF8))
self.label_9.setText(QtGui.QApplication.translate("Form", "Y", None, QtGui.QApplication.UnicodeUTF8))
self.label_10.setText(QtGui.QApplication.translate("Form", "T", None, QtGui.QApplication.UnicodeUTF8))
self.normStopLabel.setText(QtGui.QApplication.translate("Form", "Stop", None, QtGui.QApplication.UnicodeUTF8))
self.normStartLabel.setText(QtGui.QApplication.translate("Form", "Start", None, QtGui.QApplication.UnicodeUTF8))
self.normOffRadio.setText(QtGui.QApplication.translate("Form", "Off", None, QtGui.QApplication.UnicodeUTF8))
self.normTimeRangeCheck.setText(QtGui.QApplication.translate("Form", "Time range", None, QtGui.QApplication.UnicodeUTF8))
self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8))
from GraphicsView import GraphicsView
from pyqtgraph.GradientWidget import GradientWidget
from PlotWidget import PlotWidget

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>757</width>
<height>495</height>
<width>726</width>
<height>588</height>
</rect>
</property>
<property name="windowTitle">
@ -27,113 +27,77 @@
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0" rowspan="3">
<item row="1" column="0" rowspan="3">
<widget class="GraphicsView" name="graphicsView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
<horstretch>10</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<zorder>normGroup</zorder>
<zorder>normGroup</zorder>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="blackSlider">
<property name="maximum">
<number>4096</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>410</number>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QSlider" name="whiteSlider">
<property name="maximum">
<number>4096</number>
</property>
<property name="value">
<number>4096</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>B</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>W</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<item row="3" column="3">
<widget class="QPushButton" name="roiBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>ROI</string>
<string>R</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QSlider" name="timeSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="1" column="3">
<widget class="GradientWidget" name="gradientWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<item row="2" column="3">
<widget class="QPushButton" name="normBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Norm</string>
<string>N</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="4">
<item row="0" column="0" colspan="4">
<widget class="QGroupBox" name="normGroup">
<property name="title">
<string>Normalization</string>
@ -188,7 +152,7 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
@ -208,30 +172,10 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="6">
<widget class="QSlider" name="normStartSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="6">
<widget class="QSlider" name="normStopSlider">
<property name="maximum">
<number>65535</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="normXBlurSpin"/>
</item>
<item row="4" column="1">
<item row="2" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>X</string>
@ -241,7 +185,7 @@
</property>
</widget>
</item>
<item row="4" column="3">
<item row="2" column="3">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Y</string>
@ -251,10 +195,10 @@
</property>
</widget>
</item>
<item row="4" column="4">
<item row="2" column="4">
<widget class="QDoubleSpinBox" name="normYBlurSpin"/>
</item>
<item row="4" column="5">
<item row="2" column="5">
<widget class="QLabel" name="label_10">
<property name="text">
<string>T</string>
@ -264,20 +208,6 @@
</property>
</widget>
</item>
<item row="3" column="6">
<widget class="QLabel" name="normStopLabel">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QLabel" name="normStartLabel">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QRadioButton" name="normOffRadio">
<property name="text">
@ -302,7 +232,7 @@
</property>
</widget>
</item>
<item row="4" column="6">
<item row="2" column="6">
<widget class="QDoubleSpinBox" name="normTBlurSpin"/>
</item>
</layout>
@ -311,6 +241,12 @@
</layout>
</widget>
<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">
<size>
<width>0</width>
@ -323,6 +259,12 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>GradientWidget</class>
<extends>QWidget</extends>
<header>pyqtgraph.GradientWidget</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GraphicsView</class>
<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 plotConfigTemplate import *
from PyQt4 import QtGui, QtCore, QtSvg
#from ObjectWorkaround import *
#tryWorkaround(QtCore, QtGui)
import weakref
try:
@ -42,7 +44,7 @@ class PlotItem(QtGui.QGraphicsWidget):
lastFileDir = None
managers = {}
def __init__(self, parent=None):
def __init__(self, parent=None, name=None):
QtGui.QGraphicsWidget.__init__(self, parent)
## Set up control buttons
@ -56,6 +58,7 @@ class PlotItem(QtGui.QGraphicsWidget):
for b in [self.ctrlBtn, self.autoBtn]:
proxy = QtGui.QGraphicsProxyWidget(self)
proxy.setWidget(b)
proxy.setAcceptHoverEvents(False)
b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
@ -124,7 +127,7 @@ class PlotItem(QtGui.QGraphicsWidget):
## 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))
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.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.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
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.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
@ -216,6 +223,10 @@ class PlotItem(QtGui.QGraphicsWidget):
self.showScale('left', True)
self.showScale('bottom', True)
if name is not None:
self.registerPlot(name)
def __del__(self):
if self.manager is not None:
self.manager.removeWidget(self.name)
@ -251,6 +262,13 @@ class PlotItem(QtGui.QGraphicsWidget):
print " error during update. Referrers are:", refs
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):
"""return the screen geometry of the viewbox"""
v = self.scene().views()[0]
@ -261,6 +279,8 @@ class PlotItem(QtGui.QGraphicsWidget):
return wr
def viewChanged(self, *args):
self.emit(QtCore.SIGNAL('viewChanged'), *args)
@ -273,23 +293,31 @@ class PlotItem(QtGui.QGraphicsWidget):
def yLinkComboChanged(self):
self.setYLink(str(self.ctrl.yLinkCombo.currentText()))
def setXLink(self, plotName=None):
if self.manager is None:
return
if self.xLinkPlot is not None:
self.manager.unlinkX(self, self.xLinkPlot)
plot = self.manager.getWidget(plotName)
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:
return
if self.xLinkPlot is not None:
self.manager.unlinkX(self, self.xLinkPlot)
plot = self.manager.getWidget(plot)
if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
plot = plot.getPlotItem()
self.xLinkPlot = plot
if plot is not None:
self.setManualXScale()
self.manager.linkX(self, plot)
def setYLink(self, plotName=None):
if self.manager is None:
return
if self.yLinkPlot is not None:
self.manager.unlinkY(self, self.yLinkPlot)
plot = self.manager.getWidget(plotName)
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:
return
if self.yLinkPlot is not None:
self.manager.unlinkY(self, self.yLinkPlot)
plot = self.manager.getWidget(plot)
if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
plot = plot.getPlotItem()
self.yLinkPlot = plot
if plot is not None:
self.setManualYScale()
@ -339,6 +367,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.recomputeAverages()
def recomputeAverages(self):
if not self.ctrl.averageGroup.isChecked():
return
for k in self.avgCurves:
self.removeItem(self.avgCurves[k][1])
#Qwt.QwtPlotCurve.detach(self.avgCurves[k][1])
@ -406,6 +436,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.vb.setMouseEnabled(*state)
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.xMaxText.setText('%0.5g' % range[1])
@ -423,6 +455,8 @@ class PlotItem(QtGui.QGraphicsWidget):
self.emit(QtCore.SIGNAL('xRangeChanged'), 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.yMaxText.setText('%0.5g' % range[1])
@ -511,18 +545,25 @@ class PlotItem(QtGui.QGraphicsWidget):
if not item in self.items:
return
self.items.remove(item)
self.vb.removeItem(item)
if item.scene() is not None:
self.vb.removeItem(item)
if item in self.curves:
self.curves.remove(item)
self.updateDecimation()
self.updateParamList()
QtCore.QObject.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
item.connect(QtCore.SIGNAL('plotChanged'), self.plotChanged)
def clear(self):
for i in self.items[:]:
self.removeItem(i)
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):
if clear:
self.clear()
@ -549,6 +590,8 @@ class PlotItem(QtGui.QGraphicsWidget):
return curve
def addCurve(self, c, params=None):
if params is None:
params = {}
c.setMeta(params)
self.curves.append(c)
#Qwt.QwtPlotCurve.attach(c, self)
@ -569,7 +612,7 @@ class PlotItem(QtGui.QGraphicsWidget):
if self.ctrl.averageGroup.isChecked():
self.addAvgCurve(c)
QtCore.QObject.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
c.connect(QtCore.SIGNAL('plotChanged'), self.plotChanged)
self.plotChanged()
def plotChanged(self, curve=None):
@ -587,7 +630,7 @@ class PlotItem(QtGui.QGraphicsWidget):
mn = cmn
if mx is None or cmx > mx:
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
if mn == mx:
mn -= 1
@ -632,24 +675,58 @@ class PlotItem(QtGui.QGraphicsWidget):
if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName()
fileName = str(fileName)
PlotItem.lastFileDir = os.path.dirname(fileName)
self.svg = QtSvg.QSvgGenerator()
self.svg.setFileName(fileName)
res = 120.
self.svg.setResolution(res)
self.svg.setSize(QtCore.QSize(self.size().width(), self.size().height()))
painter = QtGui.QPainter(self.svg)
#self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect()))
items = self.scene().items()
self.scene().views()[0].drawItems(painter, len(items), items)
#bounds = self.mapRectToScene(self.boundingRect())
view = self.scene().views()[0]
bounds = view.viewport().rect()
bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())
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):
if fileName is None:
fileName = QtGui.QFileDialog.getSaveFileName()
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)
painter = QtGui.QPainter(self.png)
painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing)
@ -657,6 +734,29 @@ class PlotItem(QtGui.QGraphicsWidget):
painter.end()
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):
if not HAVE_WIDGETGROUP:
@ -672,8 +772,18 @@ class PlotItem(QtGui.QGraphicsWidget):
raise Exception("State save/restore requires WidgetGroup class.")
if 'paramList' in state:
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()
#print "\nRESTORE %s:\n" % str(self.name), state
#print "Restoring state. averageGroup.isChecked(): %s state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
#avg = self.ctrl.averageGroup.isChecked()
@ -787,7 +897,6 @@ class PlotItem(QtGui.QGraphicsWidget):
self.mouseScreenPos = ev.screenPos()
def ctrlBtnClicked(self):
#print self.mousePos
self.ctrlMenu.popup(self.mouseScreenPos)
#def _checkLabelKey(self, key):
@ -923,6 +1032,8 @@ class PlotItem(QtGui.QGraphicsWidget):
#self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
if PlotItem.lastFileDir is not None:
self.fileDialog.setDirectory(PlotItem.lastFileDir)
self.fileDialog.show()
QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
@ -934,11 +1045,23 @@ class PlotItem(QtGui.QGraphicsWidget):
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.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):
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
#self.writeImage(str(fileName))

View File

@ -11,17 +11,26 @@ import exceptions
class PlotWidget(GraphicsView):
"""Widget implementing a graphicsView with a single PlotItem inside."""
def __init__(self, parent=None):
def __init__(self, parent=None, **kargs):
GraphicsView.__init__(self, parent)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.enableMouse(False)
self.plotItem = PlotItem()
self.plotItem = PlotItem(**kargs)
self.setCentralItem(self.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))
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
if hasattr(self.plotItem, attr):
m = getattr(self.plotItem, attr)
@ -41,3 +50,5 @@ class PlotWidget(GraphicsView):
def restoreState(self, 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__), '..', '..'))
from pyqtgraph.ImageView import *
from numpy import random
from numpy import random, linspace
from PyQt4 import QtCore, QtGui
from scipy.ndimage import *
@ -17,14 +17,27 @@ win.setCentralWidget(imv)
win.show()
## Create random 3D data set
img = gaussian_filter(random.random((200, 200)), (5, 5)) * 5
data = random.random((100, 200, 200))
data += img
for i in range(data.shape[0]):
data[i] += exp(-(2.*i)/data.shape[0])
data += 10
img = gaussian_filter(random.normal(size=(200, 200)), (5, 5)) * 20 + 100
img = img[newaxis,:,:]
decay = exp(-linspace(0,0.3,100))[:,newaxis,newaxis]
data = random.normal(size=(100, 200, 200))
data += img * decay
#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
imv.setImage(data)
imv.setImage(data, xvals=linspace(1., 3., data.shape[0]))
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)))
pw.addItem(rect)
pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(255, 255, 255, 10)), 5)
pen.setCosmetic(True)
#pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(255, 255, 255, 50)), 5)
#pen.setCosmetic(True)
#pen.setJoinStyle(QtCore.Qt.MiterJoin)
p1.setShadowPen(pen)
p1.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255, 50)))
#p1.setShadowPen(pen)
p1.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255)))
#l1 = QtGui.QGraphicsLineItem(0, 2, 2, 3)
#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)
#pw.addItem(l1)
pw.addItem(l2)
#pw2.addItem(l2)
#pw.addItem(l3)
pw3.plot(array([100000]*100))
@ -72,12 +80,14 @@ updateData()
pw.autoRange()
t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
t.start(50)
for i in range(0, 5):
for j in range(0, 3):
yd, xd = rand(10000)
pw2.plot(yd*(j+1), xd, params={'iter': i, 'val': j})
app.exec_()
#app.exec_()

View File

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

View File

@ -30,7 +30,30 @@ vb = ViewBox()
p1 = PlotCurveItem()
vb.addItem(p1)
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)))
vb.addItem(rect)
@ -67,4 +90,4 @@ t = QtCore.QTimer()
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
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 PlotWidget import *
from ImageView import *
QAPP = None
class PlotWindow(QtGui.QMainWindow):
def __init__(self, title=None):
if QtGui.QApplication.instance() is None:
global QAPP
QAPP = QtGui.QApplication([])
QtGui.QMainWindow.__init__(self)
self.cw = PlotWidget()
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))
if title is not None:
self.setWindowTitle(title)
@ -22,10 +25,13 @@ class PlotWindow(QtGui.QMainWindow):
class ImageWindow(QtGui.QMainWindow):
def __init__(self, title=None):
if QtGui.QApplication.instance() is None:
global QAPP
QAPP = QtGui.QApplication([])
QtGui.QMainWindow.__init__(self)
self.cw = ImageView()
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))
if title is not None:
self.setWindowTitle(title)

View File

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

View File

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

View File

@ -10,25 +10,26 @@ of array data from ImageItems.
"""
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor
from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
from numpy.linalg import norm
import scipy.ndimage as ndimage
from Point import *
from math import cos, sin
from ObjectWorkaround import *
def rectStr(r):
return "[%f, %f] + [%f, %f]" % (r.x(), r.y(), r.width(), r.height())
## Multiple inheritance not allowed in PyQt. Retarded workaround:
class QObjectWorkaround:
def __init__(self):
self._qObj_ = QtCore.QObject()
def __getattr__(self, attr):
if attr == '_qObj_':
raise Exception("QObjectWorkaround not initialized!")
return getattr(self._qObj_, attr)
def connect(self, *args):
return QtCore.QObject.connect(self._qObj_, *args)
# Multiple inheritance not allowed in PyQt. Retarded workaround:
#class QObjectWorkaround:
#def __init__(self):
#self._qObj_ = QtCore.QObject()
#def __getattr__(self, attr):
#if attr == '_qObj_':
#raise Exception("QObjectWorkaround not initialized!")
#return getattr(self._qObj_, attr)
#def connect(self, *args):
#return QtCore.QObject.connect(self._qObj_, *args)
class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
@ -46,10 +47,10 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
self.state = {'pos': pos, 'size': size, 'angle': angle}
self.lastState = None
self.setPos(pos)
self.rotate(-angle)
self.rotate(-angle * 180. / pi)
self.setZValue(10)
self.handleSize = 4
self.handleSize = 5
self.invertible = invertible
self.maxBounds = maxBounds
@ -93,6 +94,10 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
pos = Point(pos)
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):
pos = Point(pos)
center = Point(center)
@ -238,6 +243,9 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
snap = Point(self.snapSize, self.snapSize)
self.translate(p1-p0, snap=snap, update=False)
elif h['type'] == 'f':
h['item'].setPos(self.mapFromScene(pos))
elif h['type'] == 's':
#c = h['center']
#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])
def paint(self, p, opt, widget):
p.save()
r = self.boundingRect()
p.setRenderHint(QtGui.QPainter.Antialiasing)
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):
"""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
self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
QtGui.QGraphicsItem.__init__(self, parent)
self.setFlag(self.ItemIgnoresTransformations)
self.setZValue(11)
self.roi = []
self.radius = radius
self.typ = typ
self.prepareGeometryChange()
self.pen = pen
self.pen.setWidth(0)
self.pen.setCosmetic(True)
if typ == 't':
self.sides = 4
self.startAng = pi/4
elif typ == 'f':
self.sides = 4
self.startAng = pi/4
elif typ == 's':
self.sides = 4
self.startAng = 0
@ -660,20 +677,24 @@ class Handle(QtGui.QGraphicsItem):
r[0].movePoint(r[1], pos, modifiers)
def paint(self, p, opt, widget):
m = p.transform()
mi = m.inverted()[0]
## determine rotation of transform
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
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 = mi.map(Point(self.radius, self.radius)) - mi.map(Point(0, 0))
#size = (size.x()*size.x() + size.y() * size.y()) ** 0.5
size = self.radius
bounds = QtCore.QRectF(-size, -size, size*2, size*2)
if bounds != self.bounds:
self.bounds = bounds
self.prepareGeometryChange()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setRenderHints(p.Antialiasing, True)
p.setPen(self.pen)
ang = self.startAng
ang = self.startAng + va
dt = 2*pi / self.sides
for i in range(0, self.sides):
x1 = size * cos(ang)
@ -757,9 +778,9 @@ class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
for l in self.lines:
l.translatable = False
#self.addToGroup(l)
l.connect(QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
l.connect(QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
l.connect(QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
l.connect(l, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
l.connect(l, QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
l.connect(l, QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
def paint(self, *args):
pass
@ -835,3 +856,34 @@ class CircleROI(EllipseROI):
#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])
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