Updates merged in from ACQ4:
- converted most old-style signals into new-style for PySide compatibility (beware: API changes) - removed ObjectWorkaround, now just using QGraphicsWidget - performance enhancements, particularly in ROI.getArrayRegion - numerous bugfixes
This commit is contained in:
parent
397a1c8a66
commit
7629bca34d
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
import weakref
|
||||
|
||||
class TickSlider(QtGui.QGraphicsView):
|
||||
def __init__(self, parent=None, orientation='bottom', allowAdd=True, **kargs):
|
||||
@ -161,6 +161,9 @@ class TickSlider(QtGui.QGraphicsView):
|
||||
|
||||
|
||||
class GradientWidget(TickSlider):
|
||||
|
||||
sigGradientChanged = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
TickSlider.__init__(self, *args, **kargs)
|
||||
self.currentTick = None
|
||||
@ -171,8 +174,10 @@ class GradientWidget(TickSlider):
|
||||
self.colorDialog = QtGui.QColorDialog()
|
||||
self.colorDialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True)
|
||||
self.colorDialog.setOption(QtGui.QColorDialog.DontUseNativeDialog, True)
|
||||
QtCore.QObject.connect(self.colorDialog, QtCore.SIGNAL('currentColorChanged(const QColor&)'), self.currentColorChanged)
|
||||
QtCore.QObject.connect(self.colorDialog, QtCore.SIGNAL('rejected()'), self.currentColorRejected)
|
||||
#QtCore.QObject.connect(self.colorDialog, QtCore.SIGNAL('currentColorChanged(const QColor&)'), self.currentColorChanged)
|
||||
self.colorDialog.currentColorChanged.connect(self.currentColorChanged)
|
||||
#QtCore.QObject.connect(self.colorDialog, QtCore.SIGNAL('rejected()'), self.currentColorRejected)
|
||||
self.colorDialog.rejected.connect(self.currentColorRejected)
|
||||
|
||||
#self.gradient = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(100,0))
|
||||
self.scene.addItem(self.gradRect)
|
||||
@ -199,7 +204,8 @@ class GradientWidget(TickSlider):
|
||||
def updateGradient(self):
|
||||
self.gradient = self.getGradient()
|
||||
self.gradRect.setBrush(QtGui.QBrush(self.gradient))
|
||||
self.emit(QtCore.SIGNAL('gradientChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('gradientChanged'), self)
|
||||
self.sigGradientChanged.emit(self)
|
||||
|
||||
def setLength(self, newLen):
|
||||
TickSlider.setLength(self, newLen)
|
||||
@ -356,7 +362,7 @@ class Tick(QtGui.QGraphicsPolygonItem):
|
||||
def __init__(self, view, pos, color, movable=True, scale=10):
|
||||
#QObjectWorkaround.__init__(self)
|
||||
self.movable = movable
|
||||
self.view = view
|
||||
self.view = weakref.ref(view)
|
||||
self.scale = scale
|
||||
self.color = color
|
||||
#self.endTick = endTick
|
||||
@ -385,7 +391,7 @@ class Tick(QtGui.QGraphicsPolygonItem):
|
||||
newPos.setY(self.pos().y())
|
||||
#newPos.setX(min(max(newPos.x(), 0), 100))
|
||||
self.setPos(newPos)
|
||||
self.view.tickMoved(self, newPos)
|
||||
self.view().tickMoved(self, newPos)
|
||||
self.movedSincePress = True
|
||||
#self.emit(QtCore.SIGNAL('tickChanged'), self)
|
||||
ev.accept()
|
||||
@ -405,7 +411,7 @@ class Tick(QtGui.QGraphicsPolygonItem):
|
||||
def mouseReleaseEvent(self, ev):
|
||||
#print self, "release", ev.scenePos()
|
||||
if not self.movedSincePress:
|
||||
self.view.tickClicked(self, ev)
|
||||
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)
|
||||
|
@ -11,9 +11,15 @@ from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
|
||||
from Point import *
|
||||
#from vector import *
|
||||
import sys
|
||||
|
||||
#import debug
|
||||
|
||||
class GraphicsView(QtGui.QGraphicsView):
|
||||
|
||||
sigRangeChanged = QtCore.Signal(object, object)
|
||||
sigMouseReleased = QtCore.Signal(object)
|
||||
sigSceneMouseMoved = QtCore.Signal(object)
|
||||
#sigRegionChanged = QtCore.Signal(object)
|
||||
|
||||
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
|
||||
@ -25,6 +31,7 @@ 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()."""
|
||||
self.closed = False
|
||||
|
||||
QtGui.QGraphicsView.__init__(self, parent)
|
||||
if 'linux' in sys.platform: ## linux has bugs in opengl implementation
|
||||
@ -42,7 +49,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
brush.setStyle(QtCore.Qt.SolidPattern)
|
||||
palette.setBrush(QtGui.QPalette.Disabled,QtGui.QPalette.Base,brush)
|
||||
self.setPalette(palette)
|
||||
self.setProperty("cursor",QtCore.QVariant(QtCore.Qt.ArrowCursor))
|
||||
#self.setProperty("cursor",QtCore.QVariant(QtCore.Qt.ArrowCursor))
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.setFrameShape(QtGui.QFrame.NoFrame)
|
||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
@ -78,6 +85,14 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False)
|
||||
self.clickAccepted = False
|
||||
|
||||
#def paintEvent(self, *args):
|
||||
#prof = debug.Profiler('GraphicsView.paintEvent '+str(id(self)), disabled=True)
|
||||
#QtGui.QGraphicsView.paintEvent(self, *args)
|
||||
#prof.finish()
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
def useOpenGL(self, b=True):
|
||||
if b:
|
||||
v = QtOpenGL.QGLWidget()
|
||||
@ -112,6 +127,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.lastButtonReleased = None
|
||||
|
||||
def resizeEvent(self, ev):
|
||||
if self.closed:
|
||||
return
|
||||
if self.autoPixelRange:
|
||||
self.range = QtCore.QRectF(0, 0, self.size().width(), self.size().height())
|
||||
self.setRange(self.range, padding=0, disableAutoPixel=False)
|
||||
@ -148,7 +165,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
#print " translate:", st
|
||||
self.setMatrix(m)
|
||||
self.currentScale = scale
|
||||
self.emit(QtCore.SIGNAL('viewChanged'), self.range)
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), self.range)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
|
||||
if propagate:
|
||||
for v in self.lockedViewports:
|
||||
@ -201,6 +219,16 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.centralWidget.setGeometry(self.range)
|
||||
self.updateMatrix(propagate)
|
||||
|
||||
def scaleToImage(self, image):
|
||||
"""Scales such that pixels in image are the same size as screen pixels. This may result in a significant performance increase."""
|
||||
pxSize = image.pixelSize()
|
||||
tl = image.sceneBoundingRect().topLeft()
|
||||
w = self.size().width() * pxSize[0]
|
||||
h = self.size().height() * pxSize[1]
|
||||
range = QtCore.QRectF(tl.x(), tl.y(), w, h)
|
||||
self.setRange(range, padding=0)
|
||||
|
||||
|
||||
|
||||
def lockXRange(self, v1):
|
||||
if not v1 in self.lockedViewports:
|
||||
@ -299,7 +327,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
if not self.mouseEnabled:
|
||||
return
|
||||
#self.mouseTrail.append(Point(self.mapToScene(ev.pos())))
|
||||
self.emit(QtCore.SIGNAL("mouseReleased"), ev)
|
||||
#self.emit(QtCore.SIGNAL("mouseReleased"), ev)
|
||||
self.sigMouseReleased.emit(ev)
|
||||
self.lastButtonReleased = ev.button()
|
||||
return ## Everything below disabled for now..
|
||||
|
||||
@ -320,7 +349,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
QtGui.QGraphicsView.mouseMoveEvent(self, ev)
|
||||
if not self.mouseEnabled:
|
||||
return
|
||||
self.emit(QtCore.SIGNAL("sceneMouseMoved(PyQt_PyObject)"), self.mapToScene(ev.pos()))
|
||||
#self.emit(QtCore.SIGNAL("sceneMouseMoved(PyQt_PyObject)"), self.mapToScene(ev.pos()))
|
||||
self.sigSceneMouseMoved.emit(self.mapToScene(ev.pos()))
|
||||
#print "moved. Grabber:", self.scene().mouseGrabberItem()
|
||||
|
||||
|
||||
@ -333,13 +363,15 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
#if self.yInverted:
|
||||
#scale[0] = 1. / scale[0]
|
||||
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
|
||||
self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
|
||||
elif ev.buttons() in [QtCore.Qt.MidButton, QtCore.Qt.LeftButton]: ## Allow panning by left or mid button.
|
||||
tr = -delta / self.currentScale
|
||||
|
||||
self.translate(tr[0], tr[1])
|
||||
self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
|
||||
#return ## Everything below disabled for now..
|
||||
|
||||
|
63
ImageView.py
63
ImageView.py
@ -31,6 +31,9 @@ class PlotROI(ROI):
|
||||
|
||||
|
||||
class ImageView(QtGui.QWidget):
|
||||
|
||||
sigTimeChanged = QtCore.Signal(object, object)
|
||||
|
||||
def __init__(self, parent=None, name="ImageView", *args):
|
||||
QtGui.QWidget.__init__(self, parent, *args)
|
||||
self.levelMax = 4096
|
||||
@ -106,25 +109,38 @@ class ImageView(QtGui.QWidget):
|
||||
setattr(self, fn, getattr(self.ui.graphicsView, fn))
|
||||
|
||||
#QtCore.QObject.connect(self.ui.timeSlider, QtCore.SIGNAL('valueChanged(int)'), self.timeChanged)
|
||||
self.timeLine.connect(self.timeLine, QtCore.SIGNAL('positionChanged'), self.timeLineChanged)
|
||||
#self.timeLine.connect(self.timeLine, QtCore.SIGNAL('positionChanged'), self.timeLineChanged)
|
||||
self.timeLine.sigPositionChanged.connect(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(self.roi, QtCore.SIGNAL('regionChanged'), self.roiChanged)
|
||||
QtCore.QObject.connect(self.ui.normBtn, QtCore.SIGNAL('toggled(bool)'), self.normToggled)
|
||||
QtCore.QObject.connect(self.ui.normDivideRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.ui.normSubtractRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.ui.normOffRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.ui.normROICheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.ui.normFrameCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.ui.normTimeRangeCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
QtCore.QObject.connect(self.playTimer, QtCore.SIGNAL('timeout()'), self.timeout)
|
||||
#QtCore.QObject.connect(self.ui.gradientWidget, QtCore.SIGNAL('gradientChanged'), self.updateImage)
|
||||
self.ui.gradientWidget.sigGradientChanged.connect(self.updateImage)
|
||||
#QtCore.QObject.connect(self.ui.roiBtn, QtCore.SIGNAL('clicked()'), self.roiClicked)
|
||||
self.ui.roiBtn.clicked.connect(self.roiClicked)
|
||||
#self.roi.connect(self.roi, QtCore.SIGNAL('regionChanged'), self.roiChanged)
|
||||
self.roi.sigRegionChanged.connect(self.roiChanged)
|
||||
#QtCore.QObject.connect(self.ui.normBtn, QtCore.SIGNAL('toggled(bool)'), self.normToggled)
|
||||
self.ui.normBtn.toggled.connect(self.normToggled)
|
||||
#QtCore.QObject.connect(self.ui.normDivideRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normDivideRadio.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.ui.normSubtractRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normSubtractRadio.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.ui.normOffRadio, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normOffRadio.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.ui.normROICheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normROICheck.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.ui.normFrameCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normFrameCheck.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.ui.normTimeRangeCheck, QtCore.SIGNAL('clicked()'), self.updateNorm)
|
||||
self.ui.normTimeRangeCheck.clicked.connect(self.updateNorm)
|
||||
#QtCore.QObject.connect(self.playTimer, QtCore.SIGNAL('timeout()'), self.timeout)
|
||||
self.playTimer.timeout.connect(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(self.normRoi, QtCore.SIGNAL('regionChangeFinished'), self.updateNorm)
|
||||
self.normProxy = proxyConnect(None, self.normRgn.sigRegionChanged, self.updateNorm)
|
||||
#self.normRoi.connect(self.normRoi, QtCore.SIGNAL('regionChangeFinished'), self.updateNorm)
|
||||
self.normRoi.sigRegionChangeFinished.connect(self.updateNorm)
|
||||
|
||||
self.ui.roiPlot.registerPlot(self.name + '_ROI')
|
||||
|
||||
@ -135,11 +151,16 @@ class ImageView(QtGui.QWidget):
|
||||
#self.quit()
|
||||
#QtGui.QWidget.__dtor__(self)
|
||||
|
||||
def quit(self):
|
||||
def close(self):
|
||||
self.ui.graphicsView.close()
|
||||
self.ui.gradientWidget.sigGradientChanged.disconnect(self.updateImage)
|
||||
self.scene.clear()
|
||||
del self.image
|
||||
del self.imageDisp
|
||||
|
||||
#self.image = None
|
||||
#self.imageDisp = None
|
||||
self.ui.roiPlot.close()
|
||||
self.setParent(None)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == QtCore.Qt.Key_Space:
|
||||
@ -319,7 +340,6 @@ class ImageView(QtGui.QWidget):
|
||||
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, np.ndarray):
|
||||
raise Exception("Image must be specified as ndarray.")
|
||||
self.image = img
|
||||
@ -348,7 +368,7 @@ class ImageView(QtGui.QWidget):
|
||||
elif img.ndim == 4:
|
||||
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
|
||||
else:
|
||||
raise Exception("Can not interpret image with dimensions %s" % (str(img)))
|
||||
raise Exception("Can not interpret image with dimensions %s" % (str(img.shape)))
|
||||
elif isinstance(axes, dict):
|
||||
self.axes = axes.copy()
|
||||
elif isinstance(axes, list) or isinstance(axes, tuple):
|
||||
@ -441,7 +461,7 @@ class ImageView(QtGui.QWidget):
|
||||
#else:
|
||||
#norm = zeros(image.shape)
|
||||
if div:
|
||||
norm = norm.astype(float32)
|
||||
norm = norm.astype(np.float32)
|
||||
|
||||
if self.ui.normTimeRangeCheck.isChecked() and image.ndim == 3:
|
||||
(sind, start) = self.timeIndex(self.normRgn.lines[0])
|
||||
@ -464,7 +484,7 @@ class ImageView(QtGui.QWidget):
|
||||
|
||||
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]
|
||||
n = n[:,np.newaxis,np.newaxis]
|
||||
#print start, end, sind, eind
|
||||
if div:
|
||||
norm /= n
|
||||
@ -483,7 +503,8 @@ class ImageView(QtGui.QWidget):
|
||||
self.currentIndex = ind
|
||||
self.updateImage()
|
||||
#self.timeLine.setPos(time)
|
||||
self.emit(QtCore.SIGNAL('timeChanged'), ind, time)
|
||||
#self.emit(QtCore.SIGNAL('timeChanged'), ind, time)
|
||||
self.sigTimeChanged.emit(ind, time)
|
||||
|
||||
def updateImage(self):
|
||||
## Redraw image on screen
|
||||
|
@ -63,4 +63,7 @@ class MultiPlotItem(QtGui.QGraphicsWidget):
|
||||
else:
|
||||
raise Exception("Data type %s not (yet?) supported for MultiPlot." % type(data))
|
||||
|
||||
def close(self):
|
||||
for p in self.plots:
|
||||
p[0].close()
|
||||
|
@ -37,3 +37,7 @@ class MultiPlotWidget(GraphicsView):
|
||||
def restoreState(self, state):
|
||||
pass
|
||||
#return self.plotItem.restoreState(state)
|
||||
|
||||
def close(self):
|
||||
self.mPlotItem.close()
|
||||
self.setParent(None)
|
@ -1,42 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from PyQt4 import QtGui, QtCore
|
||||
"""For circumventing PyQt's lack of multiple inheritance (just until PySide becomes stable)"""
|
||||
|
||||
|
||||
class Obj(QtCore.QObject):
|
||||
def event(self, ev):
|
||||
self.emit(QtCore.SIGNAL('event'), ev)
|
||||
return QtCore.QObject.event(self, ev)
|
||||
|
||||
class QObjectWorkaround:
|
||||
def __init__(self):
|
||||
self._qObj_ = Obj()
|
||||
self.connect(QtCore.SIGNAL('event'), self.event)
|
||||
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)
|
||||
def setProperty(self, prop, val):
|
||||
return self._qObj_.setProperty(prop, val)
|
||||
def property(self, prop):
|
||||
return self._qObj_.property(prop)
|
||||
def event(self, ev):
|
||||
pass
|
||||
|
||||
#class QGraphicsObject(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
#def __init__(self, *args):
|
||||
#QtGui.QGraphicsItem.__init__(self, *args)
|
||||
#QObjectWorkaround.__init__(self)
|
||||
|
||||
class QGraphicsObject(QtGui.QGraphicsWidget):
|
||||
def shape(self):
|
||||
return QtGui.QGraphicsItem.shape(self)
|
||||
|
||||
#QGraphicsObject = QtGui.QGraphicsObject
|
333
PlotItem.py
333
PlotItem.py
@ -25,6 +25,7 @@ from functions import *
|
||||
#tryWorkaround(QtCore, QtGui)
|
||||
import weakref
|
||||
import numpy as np
|
||||
#import debug
|
||||
|
||||
try:
|
||||
from WidgetGroup import *
|
||||
@ -40,6 +41,11 @@ except:
|
||||
|
||||
|
||||
class PlotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
sigYRangeChanged = QtCore.Signal(object, object)
|
||||
sigXRangeChanged = QtCore.Signal(object, object)
|
||||
sigRangeChanged = QtCore.Signal(object, object)
|
||||
|
||||
"""Plot graphics item that can be added to any graphics scene. Implements axis titles, scales, interactive viewbox."""
|
||||
lastFileDir = None
|
||||
managers = {}
|
||||
@ -60,8 +66,10 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
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)
|
||||
#QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
|
||||
self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
|
||||
#QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
|
||||
self.autoBtn.clicked.connect(self.enableAutoScale)
|
||||
|
||||
|
||||
self.layout = QtGui.QGraphicsGridLayout()
|
||||
@ -71,11 +79,15 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
self.layout.setVerticalSpacing(0)
|
||||
|
||||
self.vb = ViewBox()
|
||||
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
|
||||
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
|
||||
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
|
||||
#QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
|
||||
self.vb.sigXRangeChanged.connect(self.xRangeChanged)
|
||||
#QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
|
||||
self.vb.sigYRangeChanged.connect(self.yRangeChanged)
|
||||
#QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
|
||||
self.vb.sigRangeChangedManually.connect(self.enableManualScale)
|
||||
|
||||
QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
|
||||
#QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
|
||||
self.vb.sigRangeChanged.connect(self.viewRangeChanged)
|
||||
|
||||
self.layout.addItem(self.vb, 2, 1)
|
||||
self.alpha = 1.0
|
||||
@ -161,53 +173,81 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
## Connect control widgets
|
||||
QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
|
||||
QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
|
||||
QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
|
||||
QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
|
||||
#QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
|
||||
c.xMinText.editingFinished.connect(self.setManualXScale)
|
||||
#QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
|
||||
c.xMaxText.editingFinished.connect(self.setManualXScale)
|
||||
#QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
|
||||
c.yMinText.editingFinished.connect(self.setManualYScale)
|
||||
#QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
|
||||
c.yMaxText.editingFinished.connect(self.setManualYScale)
|
||||
|
||||
QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
|
||||
QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
|
||||
#QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
|
||||
c.xManualRadio.clicked.connect(lambda: self.updateXScale())
|
||||
#QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
|
||||
c.yManualRadio.clicked.connect(lambda: self.updateYScale())
|
||||
|
||||
QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
|
||||
QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
|
||||
#QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
|
||||
c.xAutoRadio.clicked.connect(self.updateXScale)
|
||||
#QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
|
||||
c.yAutoRadio.clicked.connect(self.updateYScale)
|
||||
|
||||
QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
|
||||
QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
|
||||
#QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
|
||||
c.xAutoPercentSpin.valueChanged.connect(self.replot)
|
||||
#QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
|
||||
c.yAutoPercentSpin.valueChanged.connect(self.replot)
|
||||
|
||||
#QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
|
||||
#QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)
|
||||
|
||||
QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
|
||||
QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
|
||||
QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
|
||||
#QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
|
||||
c.alphaGroup.toggled.connect(self.updateAlpha)
|
||||
#QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
|
||||
c.alphaSlider.valueChanged.connect(self.updateAlpha)
|
||||
#QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
|
||||
c.autoAlphaCheck.toggled.connect(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.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
|
||||
c.gridGroup.toggled.connect(self.updateGrid)
|
||||
#QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
|
||||
c.gridAlphaSlider.valueChanged.connect(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.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
|
||||
c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
|
||||
#QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
|
||||
c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
|
||||
#QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
|
||||
c.saveImgBtn.clicked.connect(self.saveImgClicked)
|
||||
#QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
|
||||
c.saveCsvBtn.clicked.connect(self.saveCsvClicked)
|
||||
|
||||
#QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
|
||||
#QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
|
||||
|
||||
QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
|
||||
QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
|
||||
#QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
|
||||
self.ctrl.xLinkCombo.currentIndexChanged.connect(self.xLinkComboChanged)
|
||||
#QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
|
||||
self.ctrl.yLinkCombo.currentIndexChanged.connect(self.yLinkComboChanged)
|
||||
|
||||
QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
|
||||
#QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
|
||||
c.downsampleSpin.valueChanged.connect(self.updateDownsampling)
|
||||
|
||||
QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
|
||||
QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
|
||||
#QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
|
||||
self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
|
||||
#QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
|
||||
self.ctrl.averageGroup.toggled.connect(self.avgToggled)
|
||||
|
||||
#QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
|
||||
#QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
|
||||
|
||||
QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
|
||||
QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
|
||||
QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
|
||||
QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
|
||||
#QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
|
||||
self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
|
||||
#QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
|
||||
self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
|
||||
#QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
|
||||
c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
|
||||
#QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
|
||||
c.yMouseCheck.toggled.connect(self.mouseCheckChanged)
|
||||
|
||||
self.xLinkPlot = None
|
||||
self.yLinkPlot = None
|
||||
@ -236,9 +276,16 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
if len(kargs) > 0:
|
||||
self.plot(**kargs)
|
||||
|
||||
#def paint(self, *args):
|
||||
#prof = debug.Profiler('PlotItem.paint', disabled=True)
|
||||
#QtGui.QGraphicsWidget.paint(self, *args)
|
||||
#prof.finish()
|
||||
|
||||
def __del__(self):
|
||||
|
||||
def close(self):
|
||||
#print "delete", self
|
||||
if self.manager is not None:
|
||||
self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
|
||||
self.manager.removeWidget(self.name)
|
||||
|
||||
def registerPlot(self, name):
|
||||
@ -249,7 +296,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
PlotItem.managers[win] = PlotWidgetManager()
|
||||
self.manager = PlotItem.managers[win]
|
||||
self.manager.addWidget(self, name)
|
||||
QtCore.QObject.connect(self.manager, QtCore.SIGNAL('widgetListChanged'), self.updatePlotList)
|
||||
#QtCore.QObject.connect(self.manager, QtCore.SIGNAL('widgetListChanged'), self.updatePlotList)
|
||||
self.manager.sigWidgetListChanged.connect(self.updatePlotList)
|
||||
self.updatePlotList()
|
||||
|
||||
def updatePlotList(self):
|
||||
@ -269,7 +317,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
except:
|
||||
import gc
|
||||
refs= gc.get_referrers(self)
|
||||
print " error during update. Referrers are:", refs
|
||||
print " error during update of", self
|
||||
print " Referrers are:", refs
|
||||
raise
|
||||
|
||||
def updateGrid(self, *args):
|
||||
@ -291,8 +340,9 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
|
||||
|
||||
def viewChanged(self, *args):
|
||||
self.emit(QtCore.SIGNAL('viewChanged'), *args)
|
||||
def viewRangeChanged(self, vb, range):
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), *args)
|
||||
self.sigRangeChanged.emit(self, range)
|
||||
|
||||
def blockLink(self, b):
|
||||
self.linksBlocked = b
|
||||
@ -466,7 +516,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
#self.setLabel(l, unitPrefix='')
|
||||
#self.getScale(l).setScale(1.0)
|
||||
|
||||
self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
|
||||
#self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
|
||||
self.sigXRangeChanged.emit(self, range)
|
||||
|
||||
def yRangeChanged(self, _, range):
|
||||
if any(np.isnan(range)) or any(np.isinf(range)):
|
||||
@ -484,7 +535,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
#else:
|
||||
#self.setLabel(l, unitPrefix='')
|
||||
#self.getScale(l).setScale(1.0)
|
||||
self.emit(QtCore.SIGNAL('yRangeChanged'), self, range)
|
||||
#self.emit(QtCore.SIGNAL('yRangeChanged'), self, range)
|
||||
self.sigYRangeChanged.emit(self, range)
|
||||
|
||||
|
||||
def enableAutoScale(self):
|
||||
@ -568,7 +620,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
self.curves.remove(item)
|
||||
self.updateDecimation()
|
||||
self.updateParamList()
|
||||
item.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
|
||||
#item.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
|
||||
item.sigPlotChanged.connect(self.plotChanged)
|
||||
|
||||
def clear(self):
|
||||
for i in self.items[:]:
|
||||
@ -644,7 +697,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
if self.ctrl.averageGroup.isChecked():
|
||||
self.addAvgCurve(c)
|
||||
|
||||
c.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
|
||||
#c.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
|
||||
c.sigPlotChanged.connect(self.plotChanged)
|
||||
self.plotChanged()
|
||||
|
||||
def plotChanged(self, curve=None):
|
||||
@ -704,60 +758,141 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
self.paramList[p] = (i.checkState() == QtCore.Qt.Checked)
|
||||
#print "paramList:", self.paramList
|
||||
|
||||
|
||||
## This is bullshit.
|
||||
def writeSvg(self, fileName=None):
|
||||
if fileName is None:
|
||||
fileName = QtGui.QFileDialog.getSaveFileName()
|
||||
if isinstance(fileName, tuple):
|
||||
raise Exception("Not implemented yet..")
|
||||
fileName = str(fileName)
|
||||
PlotItem.lastFileDir = os.path.dirname(fileName)
|
||||
|
||||
self.svg = QtSvg.QSvgGenerator()
|
||||
self.svg.setFileName(fileName)
|
||||
res = 120.
|
||||
#bounds = self.mapRectToScene(self.boundingRect())
|
||||
view = self.scene().views()[0]
|
||||
bounds = view.viewport().rect()
|
||||
bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())
|
||||
rect = self.vb.viewRect()
|
||||
xRange = rect.left(), rect.right()
|
||||
|
||||
self.svg.setResolution(res)
|
||||
#self.svg.setSize(QtCore.QSize(self.size().width(), self.size().height()))
|
||||
self.svg.setViewBox(bounds)
|
||||
svg = ""
|
||||
fh = open(fileName, 'w')
|
||||
|
||||
self.svg.setSize(QtCore.QSize(bounds.width(), bounds.height()))
|
||||
dx = max(rect.right(),0) - min(rect.left(),0)
|
||||
ymn = min(rect.top(), rect.bottom())
|
||||
ymx = max(rect.top(), rect.bottom())
|
||||
dy = max(ymx,0) - min(ymn,0)
|
||||
sx = 1.
|
||||
sy = 1.
|
||||
while dx*sx < 10:
|
||||
sx *= 1000
|
||||
while dy*sy < 10:
|
||||
sy *= 1000
|
||||
sy *= -1
|
||||
|
||||
painter = QtGui.QPainter(self.svg)
|
||||
#self.scene().render(painter, QtCore.QRectF(), view.mapToScene(bounds).boundingRect())
|
||||
#fh.write('<svg viewBox="%f %f %f %f">\n' % (rect.left()*sx, rect.top()*sx, rect.width()*sy, rect.height()*sy))
|
||||
fh.write('<svg>\n')
|
||||
fh.write('<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M%f,0 L%f,0"/>\n' % (rect.left()*sx, rect.right()*sx))
|
||||
fh.write('<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M0,%f L0,%f"/>\n' % (rect.top()*sy, rect.bottom()*sy))
|
||||
|
||||
#items = self.scene().items()
|
||||
#self.scene().views()[0].drawItems(painter, len(items), items)
|
||||
|
||||
#print view, type(view)
|
||||
view.render(painter, bounds)
|
||||
for item in self.curves:
|
||||
if isinstance(item, PlotCurveItem):
|
||||
color = colorStr(item.pen.color())
|
||||
opacity = item.pen.color().alpha() / 255.
|
||||
color = color[:6]
|
||||
x, y = item.getData()
|
||||
mask = (x > xRange[0]) * (x < xRange[1])
|
||||
mask[:-1] += mask[1:]
|
||||
m2 = mask.copy()
|
||||
mask[1:] += m2[:-1]
|
||||
x = x[mask]
|
||||
y = y[mask]
|
||||
|
||||
painter.end()
|
||||
x *= sx
|
||||
y *= sy
|
||||
|
||||
## 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:
|
||||
#fh.write('<g fill="none" stroke="#%s" stroke-opacity="1" stroke-width="1">\n' % color)
|
||||
fh.write('<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f ' % (color, opacity, x[0], y[0]))
|
||||
for i in xrange(1, len(x)):
|
||||
fh.write('L%f,%f ' % (x[i], y[i]))
|
||||
|
||||
fh.write('"/>')
|
||||
#fh.write("</g>")
|
||||
for item in self.dataItems:
|
||||
if isinstance(item, ScatterPlotItem):
|
||||
|
||||
pRect = item.boundingRect()
|
||||
vRect = pRect.intersected(rect)
|
||||
|
||||
for point in item.points():
|
||||
pos = point.pos()
|
||||
if not rect.contains(pos):
|
||||
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))
|
||||
color = colorStr(point.brush.color())
|
||||
opacity = point.brush.color().alpha() / 255.
|
||||
color = color[:6]
|
||||
x = pos.x() * sx
|
||||
y = pos.y() * sy
|
||||
|
||||
fh.write('<circle cx="%f" cy="%f" r="1" fill="#%s" stroke="none" fill-opacity="%f"/>\n' % (x, y, color, opacity))
|
||||
#fh.write('<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f ' % (color, opacity, x[0], y[0]))
|
||||
#for i in xrange(1, len(x)):
|
||||
#fh.write('L%f,%f ' % (x[i], y[i]))
|
||||
|
||||
#fh.write('"/>')
|
||||
|
||||
## get list of curves, scatter plots
|
||||
|
||||
|
||||
fh.write("</svg>\n")
|
||||
|
||||
|
||||
|
||||
#def writeSvg(self, fileName=None):
|
||||
#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.
|
||||
#view = self.scene().views()[0]
|
||||
#bounds = view.viewport().rect()
|
||||
#bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())
|
||||
|
||||
#self.svg.setResolution(res)
|
||||
#self.svg.setViewBox(bounds)
|
||||
|
||||
#self.svg.setSize(QtCore.QSize(bounds.width(), bounds.height()))
|
||||
|
||||
#painter = QtGui.QPainter(self.svg)
|
||||
#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()
|
||||
if isinstance(fileName, tuple):
|
||||
raise Exception("Not implemented yet..")
|
||||
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)
|
||||
@ -997,7 +1132,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
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)
|
||||
#QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
|
||||
self.fileDialog.fileSelected.connect(self.writeSvg)
|
||||
|
||||
#def svgFileSelected(self, fileName):
|
||||
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
|
||||
@ -1012,7 +1148,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
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)
|
||||
#QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage)
|
||||
self.fileDialog.fileSelected.connect(self.writeImage)
|
||||
|
||||
def saveCsvClicked(self):
|
||||
self.fileDialog = QtGui.QFileDialog()
|
||||
@ -1023,13 +1160,17 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
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)
|
||||
#QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeCsv)
|
||||
self.fileDialog.fileSelected.connect(self.writeCsv)
|
||||
#def imgFileSelected(self, fileName):
|
||||
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
|
||||
#self.writeImage(str(fileName))
|
||||
|
||||
|
||||
class PlotWidgetManager(QtCore.QObject):
|
||||
|
||||
sigWidgetListChanged = QtCore.Signal(object)
|
||||
|
||||
"""Used for managing communication between PlotWidgets"""
|
||||
def __init__(self):
|
||||
QtCore.QObject.__init__(self)
|
||||
@ -1037,12 +1178,14 @@ class PlotWidgetManager(QtCore.QObject):
|
||||
|
||||
def addWidget(self, w, name):
|
||||
self.widgets[name] = w
|
||||
self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
||||
#self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
||||
self.sigWidgetListChanged.emit(self.widgets.keys())
|
||||
|
||||
def removeWidget(self, name):
|
||||
if name in self.widgets:
|
||||
del self.widgets[name]
|
||||
self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
||||
#self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
||||
self.sigWidgetListChanged.emit(self.widgets.keys())
|
||||
|
||||
|
||||
def listWidgets(self):
|
||||
@ -1055,21 +1198,29 @@ class PlotWidgetManager(QtCore.QObject):
|
||||
return self.widgets[name]
|
||||
|
||||
def linkX(self, p1, p2):
|
||||
QtCore.QObject.connect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
|
||||
QtCore.QObject.connect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
|
||||
#QtCore.QObject.connect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
|
||||
p1.sigXRangeChanged.connect(p2.linkXChanged)
|
||||
#QtCore.QObject.connect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
|
||||
p2.sigXRangeChanged.connect(p1.linkXChanged)
|
||||
p1.linkXChanged(p2)
|
||||
#p2.setManualXScale()
|
||||
|
||||
def unlinkX(self, p1, p2):
|
||||
QtCore.QObject.disconnect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
|
||||
QtCore.QObject.disconnect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
|
||||
#QtCore.QObject.disconnect(p1, QtCore.SIGNAL('xRangeChanged'), p2.linkXChanged)
|
||||
p1.sigXRangeChanged.disconnect(p2.linkXChanged)
|
||||
#QtCore.QObject.disconnect(p2, QtCore.SIGNAL('xRangeChanged'), p1.linkXChanged)
|
||||
p2.sigXRangeChanged.disconnect(p1.linkXChanged)
|
||||
|
||||
def linkY(self, p1, p2):
|
||||
QtCore.QObject.connect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
|
||||
QtCore.QObject.connect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)
|
||||
#QtCore.QObject.connect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
|
||||
p1.sigYRangeChanged.connect(p2.linkYChanged)
|
||||
#QtCore.QObject.connect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)
|
||||
p2.sigYRangeChanged.connect(p1.linkYChanged)
|
||||
p1.linkYChanged(p2)
|
||||
#p2.setManualYScale()
|
||||
|
||||
def unlinkY(self, p1, p2):
|
||||
QtCore.QObject.disconnect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
|
||||
QtCore.QObject.disconnect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)
|
||||
#QtCore.QObject.disconnect(p1, QtCore.SIGNAL('yRangeChanged'), p2.linkYChanged)
|
||||
p1.sigYRangeChanged.disconnect(p2.linkYChanged)
|
||||
#QtCore.QObject.disconnect(p2, QtCore.SIGNAL('yRangeChanged'), p1.linkYChanged)
|
||||
p2.sigYRangeChanged.disconnect(p1.linkYChanged)
|
||||
|
@ -10,6 +10,9 @@ from PlotItem import *
|
||||
import exceptions
|
||||
|
||||
class PlotWidget(GraphicsView):
|
||||
|
||||
sigRangeChanged = QtCore.Signal(object, object)
|
||||
|
||||
"""Widget implementing a graphicsView with a single PlotItem inside."""
|
||||
def __init__(self, parent=None, **kargs):
|
||||
GraphicsView.__init__(self, parent)
|
||||
@ -20,16 +23,22 @@ class PlotWidget(GraphicsView):
|
||||
## Explicitly wrap methods from plotItem
|
||||
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)
|
||||
#QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged)
|
||||
self.plotItem.sigRangeChanged.connect(self.viewRangeChanged)
|
||||
|
||||
#def __dtor__(self):
|
||||
##print "Called plotWidget sip destructor"
|
||||
#self.quit()
|
||||
|
||||
|
||||
def quit(self):
|
||||
self.plotItem.clear()
|
||||
self.scene().clear()
|
||||
#def quit(self):
|
||||
|
||||
def close(self):
|
||||
self.plotItem.close()
|
||||
self.plotItem = None
|
||||
#self.scene().clear()
|
||||
#self.mPlotItem.close()
|
||||
self.setParent(None)
|
||||
|
||||
def __getattr__(self, attr): ## implicitly wrap methods from plotItem
|
||||
if hasattr(self.plotItem, attr):
|
||||
@ -38,8 +47,9 @@ class PlotWidget(GraphicsView):
|
||||
return m
|
||||
raise exceptions.NameError(attr)
|
||||
|
||||
def viewChanged(self, *args):
|
||||
self.emit(QtCore.SIGNAL('viewChanged'), *args)
|
||||
def viewRangeChanged(self, view, range):
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), *args)
|
||||
self.sigRangeChanged.emit(self, range)
|
||||
|
||||
def widgetGroupInterface(self):
|
||||
return (None, PlotWidget.saveState, PlotWidget.restoreState)
|
||||
|
3
Point.py
3
Point.py
@ -34,6 +34,9 @@ class Point(QtCore.QPointF):
|
||||
return
|
||||
QtCore.QPointF.__init__(self, *args)
|
||||
|
||||
def __len__(self):
|
||||
return 2
|
||||
|
||||
def __reduce__(self):
|
||||
return (Point, (self.x(), self.y()))
|
||||
|
||||
|
@ -2,14 +2,6 @@
|
||||
from PyQt4 import QtCore
|
||||
from ptime import time
|
||||
|
||||
def proxyConnect(source, signal, slot, delay=0.3):
|
||||
"""Connect a signal to a slot with delay. Returns the SignalProxy
|
||||
object that was created. Be sure to store this object so it is not
|
||||
garbage-collected immediately."""
|
||||
sp = SignalProxy(source, signal, delay)
|
||||
sp.connect(sp, signal, slot)
|
||||
return sp
|
||||
|
||||
class SignalProxy(QtCore.QObject):
|
||||
"""Object which collects rapid-fire signals and condenses them
|
||||
into a single signal. Used, for example, to prevent a SpinBox
|
||||
@ -17,53 +9,70 @@ class SignalProxy(QtCore.QObject):
|
||||
over it."""
|
||||
|
||||
def __init__(self, source, signal, delay=0.3):
|
||||
"""Initialization arguments:
|
||||
source - Any QObject that will emit signal, or None if signal is new style
|
||||
signal - Output of QtCore.SIGNAL(...), or obj.signal for new style
|
||||
delay - Time (in seconds) to wait for signals to stop before emitting (default 0.3s)"""
|
||||
|
||||
QtCore.QObject.__init__(self)
|
||||
source.connect(source, signal, self.signal)
|
||||
self.delay = delay
|
||||
self.waitUntil = 0
|
||||
self.args = None
|
||||
self.timers = 0
|
||||
if source is None:
|
||||
signal.connect(self.signalReceived)
|
||||
self.signal = QtCore.SIGNAL('signal')
|
||||
else:
|
||||
source.connect(source, signal, self.signalReceived)
|
||||
self.signal = signal
|
||||
self.delay = delay
|
||||
self.args = None
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.flush)
|
||||
self.block = False
|
||||
|
||||
def setDelay(self, delay):
|
||||
self.delay = delay
|
||||
|
||||
def signalReceived(self, *args):
|
||||
"""Received signal. Cancel previous timer and store args to be forwarded later."""
|
||||
if self.block:
|
||||
return
|
||||
self.args = args
|
||||
self.timer.stop()
|
||||
self.timer.start((self.delay*1000)+1)
|
||||
|
||||
def flush(self):
|
||||
"""If there is a signal queued up, send it now."""
|
||||
if self.args is None or self.block:
|
||||
return False
|
||||
if self.block:
|
||||
return
|
||||
self.emit(self.signal, *self.args)
|
||||
self.args = None
|
||||
return True
|
||||
|
||||
|
||||
def signal(self, *args):
|
||||
"""Received signal, queue to be forwarded later."""
|
||||
if self.block:
|
||||
return
|
||||
self.waitUntil = time() + self.delay
|
||||
self.args = args
|
||||
self.timers += 1
|
||||
QtCore.QTimer.singleShot((self.delay*1000)+1, self.tryEmit)
|
||||
|
||||
def tryEmit(self):
|
||||
"""Emit signal if it has been long enougn since receiving the last signal."""
|
||||
if self.args is None or self.block:
|
||||
return False
|
||||
self.timers -= 1
|
||||
t = time()
|
||||
if t >= self.waitUntil:
|
||||
return self.flush()
|
||||
else:
|
||||
if self.timers == 0:
|
||||
self.timers += 1
|
||||
QtCore.QTimer.singleShot((self.waitUntil - t) * 1000, self.tryEmit)
|
||||
return True
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
self.block = True
|
||||
|
||||
|
||||
def proxyConnect(source, signal, slot, delay=0.3):
|
||||
"""Connect a signal to a slot with delay. Returns the SignalProxy
|
||||
object that was created. Be sure to store this object so it is not
|
||||
garbage-collected immediately."""
|
||||
sp = SignalProxy(source, signal, delay)
|
||||
if source is None:
|
||||
sp.connect(sp, QtCore.SIGNAL('signal'), slot)
|
||||
else:
|
||||
sp.connect(sp, signal, slot)
|
||||
return sp
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4 import QtGui
|
||||
app = QtGui.QApplication([])
|
||||
win = QtGui.QMainWindow()
|
||||
spin = QtGui.QSpinBox()
|
||||
win.setCentralWidget(spin)
|
||||
win.show()
|
||||
|
||||
def fn(*args):
|
||||
print "Got signal:", args
|
||||
|
||||
proxy = proxyConnect(spin, QtCore.SIGNAL('valueChanged(int)'), fn)
|
||||
|
||||
|
@ -3,17 +3,16 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from pyqtgraph.GraphicsView import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
from numpy import random
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from scipy.ndimage import *
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
## Create window with GraphicsView widget
|
||||
win = QtGui.QMainWindow()
|
||||
view = GraphicsView()
|
||||
view = pg.GraphicsView()
|
||||
#view.useOpenGL(True)
|
||||
win.setCentralWidget(view)
|
||||
win.show()
|
||||
@ -25,26 +24,29 @@ view.enableMouse()
|
||||
view.setAspectLocked(True)
|
||||
|
||||
## Create image item
|
||||
img = ImageItem()
|
||||
img = pg.ImageItem()
|
||||
view.scene().addItem(img)
|
||||
|
||||
## Set initial view bounds
|
||||
view.setRange(QtCore.QRectF(0, 0, 200, 200))
|
||||
|
||||
## Create random image
|
||||
data = np.random.normal(size=(50, 200, 200))
|
||||
i = 0
|
||||
|
||||
def updateData():
|
||||
global img
|
||||
## Create random image
|
||||
data = random.random((200, 200))
|
||||
global img, data, i
|
||||
|
||||
## Display the data
|
||||
img.updateImage(data)
|
||||
img.updateImage(data[i])
|
||||
i = (i+1) % data.shape[0]
|
||||
|
||||
|
||||
# update image data every 20ms (or so)
|
||||
t = QtCore.QTimer()
|
||||
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
|
||||
t.timeout.connect(updateData)
|
||||
t.start(20)
|
||||
|
||||
|
||||
|
||||
app.exec_()
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
@ -3,24 +3,25 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from pyqtgraph.ImageView import *
|
||||
from numpy import random, linspace
|
||||
|
||||
import numpy as np
|
||||
import scipy
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from scipy.ndimage import *
|
||||
import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
## Create window with ImageView widget
|
||||
win = QtGui.QMainWindow()
|
||||
imv = ImageView()
|
||||
imv = pg.ImageView()
|
||||
win.setCentralWidget(imv)
|
||||
win.show()
|
||||
|
||||
## Create random 3D data set
|
||||
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))
|
||||
## Create random 3D data set with noisy signals
|
||||
img = scipy.ndimage.gaussian_filter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
|
||||
img = img[np.newaxis,:,:]
|
||||
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
|
||||
data = np.random.normal(size=(100, 200, 200))
|
||||
data += img * decay
|
||||
|
||||
#for i in range(data.shape[0]):
|
||||
@ -28,16 +29,18 @@ data += img * decay
|
||||
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 = np.zeros(data.shape[0])
|
||||
sig[30:] += np.exp(-np.linspace(1,10, 70))
|
||||
sig[40:] += np.exp(-np.linspace(1,10, 60))
|
||||
sig[70:] += np.exp(-np.linspace(1,10, 30))
|
||||
|
||||
sig = sig[:,newaxis,newaxis] * 3
|
||||
sig = sig[:,np.newaxis,np.newaxis] * 3
|
||||
data[:,50:60,50:60] += sig
|
||||
|
||||
|
||||
## Display the data
|
||||
imv.setImage(data, xvals=linspace(1., 3., data.shape[0]))
|
||||
imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))
|
||||
|
||||
app.exec_()
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
@ -4,14 +4,16 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
|
||||
from scipy import random
|
||||
from numpy import linspace
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from pyqtgraph.MultiPlotWidget import *
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.MultiPlotWidget import MultiPlotWidget
|
||||
try:
|
||||
from metaarray import *
|
||||
except:
|
||||
print "MultiPlot is only used with MetaArray for now (and you do not have the metaarray module)"
|
||||
print "MultiPlot is only used with MetaArray for now (and you do not have the metaarray package)"
|
||||
exit()
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
@ -23,4 +25,7 @@ mw.show()
|
||||
ma = MetaArray(random.random((3, 1000)), info=[{'name': 'Signal', 'cols': [{'name': 'Col1'}, {'name': 'Col2'}, {'name': 'Col3'}]}, {'name': 'Time', 'vals': linspace(0., 1., 1000)}])
|
||||
pw.plot(ma)
|
||||
|
||||
app.exec_()
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
||||
|
@ -4,11 +4,10 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from scipy import random
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from pyqtgraph.PlotWidget import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
@ -17,77 +16,56 @@ mw.setCentralWidget(cw)
|
||||
l = QtGui.QVBoxLayout()
|
||||
cw.setLayout(l)
|
||||
|
||||
pw = PlotWidget()
|
||||
pw = pg.PlotWidget(name='Plot1') ## giving the plots names allows us to link their axes together
|
||||
l.addWidget(pw)
|
||||
pw2 = PlotWidget()
|
||||
pw2 = pg.PlotWidget(name='Plot2')
|
||||
l.addWidget(pw2)
|
||||
pw3 = PlotWidget()
|
||||
pw3 = pg.PlotWidget()
|
||||
l.addWidget(pw3)
|
||||
|
||||
pw.registerPlot('Plot1')
|
||||
pw2.registerPlot('Plot2')
|
||||
mw.show()
|
||||
|
||||
#p1 = PlotCurveItem()
|
||||
#pw.addItem(p1)
|
||||
## Create an empty plot curve to be filled later, set its pen
|
||||
p1 = pw.plot()
|
||||
p1.setPen((200,200,100))
|
||||
|
||||
## Add in some extra graphics
|
||||
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, 50)), 5)
|
||||
#pen.setCosmetic(True)
|
||||
#pen.setJoinStyle(QtCore.Qt.MiterJoin)
|
||||
#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(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)
|
||||
#pw2.addItem(l2)
|
||||
#pw.addItem(l3)
|
||||
|
||||
pw3.plot(array([100000]*100))
|
||||
|
||||
|
||||
mw.show()
|
||||
|
||||
|
||||
def rand(n):
|
||||
data = random.random(n)
|
||||
data = np.random.random(n)
|
||||
data[int(n*0.1):int(n*0.13)] += .5
|
||||
data[int(n*0.18)] += 2
|
||||
data[int(n*0.1):int(n*0.13)] *= 5
|
||||
data[int(n*0.18)] *= 20
|
||||
return data, arange(n, n+len(data)) / float(n)
|
||||
data *= 1e-12
|
||||
return data, np.arange(n, n+len(data)) / float(n)
|
||||
|
||||
|
||||
def updateData():
|
||||
yd, xd = rand(10000)
|
||||
p1.updateData(yd, x=xd)
|
||||
|
||||
yd, xd = rand(10000)
|
||||
updateData()
|
||||
pw.autoRange()
|
||||
|
||||
## Start a timer to rapidly update the plot in pw
|
||||
t = QtCore.QTimer()
|
||||
|
||||
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
|
||||
t.timeout.connect(updateData)
|
||||
t.start(50)
|
||||
|
||||
|
||||
## Multiple parameterized plots--we can autogenerate averages for these.
|
||||
for i in range(0, 5):
|
||||
for j in range(0, 3):
|
||||
yd, xd = rand(10000)
|
||||
pw2.plot(y=yd*(j+1), x=xd, params={'iter': i, 'val': j})
|
||||
|
||||
#app.exec_()
|
||||
## Test large numbers
|
||||
curve = pw3.plot(np.random.normal(size=100)*1e6)
|
||||
curve.setPen('w') ## white pen
|
||||
curve.setShadowPen(pg.mkPen((70,70,30), width=6, cosmetic=True))
|
||||
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
@ -4,32 +4,26 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from numpy import random
|
||||
from scipy import zeros, ones
|
||||
from pyqtgraph.graphicsWindows import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
from pyqtgraph.widgets import *
|
||||
from pyqtgraph.PlotWidget import *
|
||||
from pyqtgraph.functions import mkPen
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
## create GUI
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
class Win(QtGui.QMainWindow):
|
||||
pass
|
||||
|
||||
w = Win()
|
||||
v = GraphicsView(useOpenGL=False)
|
||||
v.invertY(True)
|
||||
w = QtGui.QMainWindow()
|
||||
v = pg.GraphicsView()
|
||||
v.invertY(True) ## Images usually have their Y-axis pointing downward
|
||||
v.setAspectLocked(True)
|
||||
v.enableMouse(True)
|
||||
v.autoPixelScale = False
|
||||
|
||||
w.setCentralWidget(v)
|
||||
s = v.scene()
|
||||
v.setRange(QtCore.QRect(-2, -2, 220, 220))
|
||||
w.show()
|
||||
|
||||
arr = ones((100, 100), dtype=float)
|
||||
## Create image to display
|
||||
arr = np.ones((100, 100), dtype=float)
|
||||
arr[45:55, 45:55] = 0
|
||||
arr[25, :] = 5
|
||||
arr[:, 25] = 5
|
||||
@ -38,21 +32,23 @@ arr[:, 75] = 5
|
||||
arr[50, :] = 10
|
||||
arr[:, 50] = 10
|
||||
|
||||
im1 = ImageItem(arr)
|
||||
im2 = ImageItem(arr)
|
||||
## Create image items, add to scene and set position
|
||||
im1 = pg.ImageItem(arr)
|
||||
im2 = pg.ImageItem(arr)
|
||||
s.addItem(im1)
|
||||
s.addItem(im2)
|
||||
im2.moveBy(110, 20)
|
||||
im3 = ImageItem()
|
||||
im3 = pg.ImageItem()
|
||||
s.addItem(im3)
|
||||
im3.moveBy(0, 130)
|
||||
im3.setZValue(10)
|
||||
im4 = ImageItem()
|
||||
im4 = pg.ImageItem()
|
||||
s.addItem(im4)
|
||||
im4.moveBy(110, 130)
|
||||
im4.setZValue(10)
|
||||
|
||||
pi1 = PlotItem()
|
||||
## create the plot
|
||||
pi1 = pg.PlotItem()
|
||||
s.addItem(pi1)
|
||||
pi1.scale(0.5, 0.5)
|
||||
pi1.setGeometry(0, 170, 300, 100)
|
||||
@ -76,34 +72,39 @@ def updateRoiPlot(roi, data=None):
|
||||
if data is not None:
|
||||
roi.curve.updateData(data.mean(axis=1))
|
||||
|
||||
#def updatePlot(roi)
|
||||
|
||||
## Create a variety of different ROI types
|
||||
rois = []
|
||||
rois.append(TestROI([0, 0], [20, 20], maxBounds=QtCore.QRectF(-10, -10, 230, 140), pen=mkPen(0)))
|
||||
rois.append(LineROI([0, 0], [20, 20], width=5, pen=mkPen(1)))
|
||||
rois.append(MultiLineROI([[0, 50], [50, 60], [60, 30]], width=5, pen=mkPen(2)))
|
||||
rois.append(EllipseROI([110, 10], [30, 20], pen=mkPen(3)))
|
||||
rois.append(CircleROI([110, 50], [20, 20], pen=mkPen(4)))
|
||||
rois.append(PolygonROI([[2,0], [2.1,0], [2,.1]], pen=mkPen(5)))
|
||||
rois.append(pg.widgets.TestROI([0, 0], [20, 20], maxBounds=QtCore.QRectF(-10, -10, 230, 140), pen=(0,9)))
|
||||
rois.append(pg.widgets.LineROI([0, 0], [20, 20], width=5, pen=(1,9)))
|
||||
rois.append(pg.widgets.MultiLineROI([[0, 50], [50, 60], [60, 30]], width=5, pen=(2,9)))
|
||||
rois.append(pg.widgets.EllipseROI([110, 10], [30, 20], pen=(3,9)))
|
||||
rois.append(pg.widgets.CircleROI([110, 50], [20, 20], pen=(4,9)))
|
||||
rois.append(pg.widgets.PolygonROI([[2,0], [2.1,0], [2,.1]], pen=(5,9)))
|
||||
#rois.append(SpiralROI([20,30], [1,1], pen=mkPen(0)))
|
||||
|
||||
## Add each ROI to the scene and link its data to a plot curve with the same color
|
||||
for r in rois:
|
||||
s.addItem(r)
|
||||
c = pi1.plot(pen=r.pen)
|
||||
r.curve = c
|
||||
r.connect(r, QtCore.SIGNAL('regionChanged'), updateRoi)
|
||||
r.sigRegionChanged.connect(updateRoi)
|
||||
|
||||
def updateImage():
|
||||
global im1, arr, lastRoi
|
||||
r = abs(random.normal(loc=0, scale=(arr.max()-arr.min())*0.1, size=arr.shape))
|
||||
r = abs(np.random.normal(loc=0, scale=(arr.max()-arr.min())*0.1, size=arr.shape))
|
||||
im1.updateImage(arr + r)
|
||||
updateRoi(lastRoi)
|
||||
for r in rois:
|
||||
updateRoiPlot(r)
|
||||
|
||||
|
||||
## Rapidly update one of the images with random noise
|
||||
t = QtCore.QTimer()
|
||||
t.connect(t, QtCore.SIGNAL('timeout()'), updateImage)
|
||||
t.timeout.connect(updateImage)
|
||||
t.start(50)
|
||||
|
||||
w.show()
|
||||
app.exec_()
|
||||
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
@ -3,18 +3,16 @@
|
||||
import sys, os
|
||||
sys.path = [os.path.join(os.path.dirname(__file__), '..', '..')] + sys.path
|
||||
|
||||
from pyqtgraph.GraphicsView import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
#from numpy import random
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from scipy.ndimage import *
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
## Create window with GraphicsView widget
|
||||
win = QtGui.QMainWindow()
|
||||
view = GraphicsView()
|
||||
view = pg.GraphicsView()
|
||||
#view.useOpenGL(True)
|
||||
win.setCentralWidget(view)
|
||||
win.show()
|
||||
@ -26,7 +24,7 @@ view.enableMouse()
|
||||
view.setAspectLocked(True)
|
||||
|
||||
## Create image item
|
||||
img = ImageItem(np.zeros((200,200)))
|
||||
img = pg.ImageItem(np.zeros((200,200)))
|
||||
view.scene().addItem(img)
|
||||
|
||||
## Set initial view bounds
|
||||
@ -35,4 +33,6 @@ view.setRange(QtCore.QRectF(0, 0, 200, 200))
|
||||
img.setDrawKernel(1)
|
||||
img.setLevels(10,0)
|
||||
|
||||
#app.exec_()
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
@ -1,32 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, os
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from pyqtgraph.PlotWidget import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
cw = PlotWidget()
|
||||
cw = pg.PlotWidget()
|
||||
mw.setCentralWidget(cw)
|
||||
mw.show()
|
||||
|
||||
|
||||
#s1 = SpotItem(5, pxMode=True, brush=QtGui.QBrush(QtGui.QColor(0, 0, 200)), pen=QtGui.QPen(QtGui.QColor(100,100,100)))
|
||||
#s1.setPos(1, 0)
|
||||
#s2 = SpotItem(.1, pxMode=False, brush=QtGui.QBrush(QtGui.QColor(0, 200, 0)), pen=QtGui.QPen(QtGui.QColor(100,100,100)))
|
||||
#s2.setPos(0, 1)
|
||||
#cw.addItem(s1)
|
||||
#cw.addItem(s2)
|
||||
|
||||
import numpy as np
|
||||
s1 = ScatterPlotItem(size=10, pen=QtGui.QPen(QtCore.Qt.NoPen), brush=QtGui.QBrush(QtGui.QColor(255, 255, 255, 20)))
|
||||
s1 = pg.ScatterPlotItem(size=10, pen=QtGui.QPen(QtCore.Qt.NoPen), brush=QtGui.QBrush(QtGui.QColor(255, 255, 255, 20)))
|
||||
pos = np.random.normal(size=(2,3000))
|
||||
spots = [{'pos': pos[:,i]} for i in range(3000)]
|
||||
s1.addPoints(spots)
|
||||
|
||||
cw.addDataItem(s1)
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
||||
|
@ -4,10 +4,12 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from scipy import random
|
||||
## This example uses a ViewBox to create a PlotWidget-like interface
|
||||
|
||||
#from scipy import random
|
||||
import numpy as np
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from pyqtgraph.GraphicsView import *
|
||||
from pyqtgraph.graphicsItems import *
|
||||
import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
@ -19,15 +21,15 @@ mw.show()
|
||||
mw.resize(800, 600)
|
||||
|
||||
|
||||
gv = GraphicsView(cw)
|
||||
gv.enableMouse(False)
|
||||
gv = pg.GraphicsView(cw)
|
||||
gv.enableMouse(False) ## Mouse interaction will be handled by the ViewBox
|
||||
l = QtGui.QGraphicsGridLayout()
|
||||
l.setHorizontalSpacing(0)
|
||||
l.setVerticalSpacing(0)
|
||||
|
||||
|
||||
vb = ViewBox()
|
||||
p1 = PlotCurveItem()
|
||||
vb = pg.ViewBox()
|
||||
p1 = pg.PlotCurveItem()
|
||||
vb.addItem(p1)
|
||||
vl.addWidget(gv)
|
||||
|
||||
@ -61,21 +63,21 @@ l.addItem(vb, 0, 1)
|
||||
gv.centralWidget.setLayout(l)
|
||||
|
||||
|
||||
xScale = ScaleItem(orientation='bottom', linkView=vb)
|
||||
xScale = pg.ScaleItem(orientation='bottom', linkView=vb)
|
||||
l.addItem(xScale, 1, 1)
|
||||
yScale = ScaleItem(orientation='left', linkView=vb)
|
||||
yScale = pg.ScaleItem(orientation='left', linkView=vb)
|
||||
l.addItem(yScale, 0, 0)
|
||||
|
||||
xScale.setLabel(text=u"<span style='color: #ff0000; font-weight: bold'>X</span> <i>Axis</i>", units="s")
|
||||
yScale.setLabel('Y Axis', units='V')
|
||||
|
||||
def rand(n):
|
||||
data = random.random(n)
|
||||
data = np.random.random(n)
|
||||
data[int(n*0.1):int(n*0.13)] += .5
|
||||
data[int(n*0.18)] += 2
|
||||
data[int(n*0.1):int(n*0.13)] *= 5
|
||||
data[int(n*0.18)] *= 20
|
||||
return data, arange(n, n+len(data)) / float(n)
|
||||
return data, np.arange(n, n+len(data)) / float(n)
|
||||
|
||||
|
||||
def updateData():
|
||||
@ -87,7 +89,9 @@ updateData()
|
||||
vb.autoRange()
|
||||
|
||||
t = QtCore.QTimer()
|
||||
QtCore.QObject.connect(t, QtCore.SIGNAL('timeout()'), updateData)
|
||||
t.timeout.connect(updateData)
|
||||
t.start(50)
|
||||
|
||||
#app.exec_()
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
90
functions.py
90
functions.py
@ -18,7 +18,8 @@ colorAbbrev = {
|
||||
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from numpy import clip, floor, log
|
||||
import numpy as np
|
||||
import scipy.ndimage
|
||||
|
||||
## Copied from acq4/lib/util/functions
|
||||
SI_PREFIXES = u'yzafpnµm kMGTPEZY'
|
||||
@ -28,7 +29,7 @@ def siScale(x, minVal=1e-25):
|
||||
m = 0
|
||||
x = 0
|
||||
else:
|
||||
m = int(clip(floor(log(abs(x))/log(1000)), -9.0, 9.0))
|
||||
m = int(np.clip(np.floor(np.log(abs(x))/np.log(1000)), -9.0, 9.0))
|
||||
if m == 0:
|
||||
pref = ''
|
||||
elif m < -8 or m > 8:
|
||||
@ -145,13 +146,13 @@ def mkColor(*args):
|
||||
return QtGui.QColor(r, g, b, a)
|
||||
|
||||
def colorTuple(c):
|
||||
return (c.red(), c.blue(), c.green(), c.alpha())
|
||||
return (c.red(), c.green(), c.blue(), c.alpha())
|
||||
|
||||
def colorStr(c):
|
||||
"""Generate a hex string code from a QColor"""
|
||||
return ('%02x'*4) % colorTuple(c)
|
||||
|
||||
def intColor(index, hues=9, values=3, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255):
|
||||
def intColor(index, hues=9, values=1, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255, alpha=255, **kargs):
|
||||
"""Creates a QColor from a single index. Useful for stepping through a predefined list of colors.
|
||||
- The argument "index" determines which color from the set will be returned
|
||||
- All other arguments determine what the set of predefined colors will be
|
||||
@ -163,9 +164,90 @@ def intColor(index, hues=9, values=3, maxValue=255, minValue=150, maxHue=360, mi
|
||||
ind = int(index) % (hues * values)
|
||||
indh = ind % hues
|
||||
indv = ind / hues
|
||||
if values > 1:
|
||||
v = minValue + indv * ((maxValue-minValue) / (values-1))
|
||||
else:
|
||||
v = maxValue
|
||||
h = minHue + (indh * (maxHue-minHue)) / hues
|
||||
|
||||
c = QtGui.QColor()
|
||||
c.setHsv(h, sat, v)
|
||||
c.setAlpha(alpha)
|
||||
return c
|
||||
|
||||
|
||||
def affineSlice(data, shape, origin, vectors, axes, **kargs):
|
||||
"""Take an arbitrary slice through an array.
|
||||
Parameters:
|
||||
data: the original dataset
|
||||
shape: the shape of the slice to take (Note the return value may have more dimensions than len(shape))
|
||||
origin: the location in the original dataset that will become the origin in the sliced data.
|
||||
vectors: list of unit vectors which point in the direction of the slice axes
|
||||
each vector must be the same length as axes
|
||||
If the vectors are not unit length, the result will be scaled.
|
||||
If the vectors are not orthogonal, the result will be sheared.
|
||||
axes: the axes in the original dataset which correspond to the slice vectors
|
||||
|
||||
Example: start with a 4D data set, take a diagonal-planar slice out of the last 3 axes
|
||||
- data = array with dims (time, x, y, z) = (100, 40, 40, 40)
|
||||
- The plane to pull out is perpendicular to the vector (x,y,z) = (1,1,1)
|
||||
- The origin of the slice will be at (x,y,z) = (40, 0, 0)
|
||||
- The we will slice a 20x20 plane from each timepoint, giving a final shape (100, 20, 20)
|
||||
affineSlice(data, shape=(20,20), origin=(40,0,0), vectors=((-1, 1, 0), (-1, 0, 1)), axes=(1,2,3))
|
||||
|
||||
Note the following:
|
||||
len(shape) == len(vectors)
|
||||
len(origin) == len(axes) == len(vectors[0])
|
||||
"""
|
||||
|
||||
# sanity check
|
||||
if len(shape) != len(vectors):
|
||||
raise Exception("shape and vectors must have same length.")
|
||||
if len(origin) != len(axes):
|
||||
raise Exception("origin and axes must have same length.")
|
||||
for v in vectors:
|
||||
if len(v) != len(axes):
|
||||
raise Exception("each vector must be same length as axes.")
|
||||
shape = (np.ceil(shape[0]), np.ceil(shape[1]))
|
||||
|
||||
## transpose data so slice axes come first
|
||||
trAx = range(data.ndim)
|
||||
for x in axes:
|
||||
trAx.remove(x)
|
||||
tr1 = tuple(axes) + tuple(trAx)
|
||||
data = data.transpose(tr1)
|
||||
#print "tr1:", tr1
|
||||
## dims are now [(slice axes), (other axes)]
|
||||
|
||||
|
||||
## make sure vectors are arrays
|
||||
vectors = np.array(vectors)
|
||||
origin = np.array(origin)
|
||||
origin.shape = (len(axes),) + (1,)*len(shape)
|
||||
|
||||
## Build array of sample locations.
|
||||
grid = np.mgrid[tuple([slice(0,x) for x in shape])] ## mesh grid of indexes
|
||||
#print shape, grid.shape
|
||||
x = (grid[np.newaxis,...] * vectors.transpose()[(Ellipsis,) + (np.newaxis,)*len(shape)]).sum(axis=1) ## magic
|
||||
x += origin
|
||||
#print "X values:"
|
||||
#print x
|
||||
## iterate manually over unused axes since map_coordinates won't do it for us
|
||||
extraShape = data.shape[len(axes):]
|
||||
output = np.empty(tuple(shape) + extraShape, dtype=data.dtype)
|
||||
for inds in np.ndindex(*extraShape):
|
||||
ind = (Ellipsis,) + inds
|
||||
#print data[ind].shape, x.shape, output[ind].shape, output.shape
|
||||
output[ind] = scipy.ndimage.map_coordinates(data[ind], x, **kargs)
|
||||
|
||||
tr = range(output.ndim)
|
||||
trb = []
|
||||
for i in range(min(axes)):
|
||||
ind = tr1.index(i) + (len(shape)-len(axes))
|
||||
tr.remove(ind)
|
||||
trb.append(ind)
|
||||
tr2 = tuple(trb+tr)
|
||||
|
||||
## Untranspose array before returning
|
||||
return output.transpose(tr2)
|
||||
|
||||
|
263
graphicsItems.py
263
graphicsItems.py
@ -11,7 +11,7 @@ Provides ImageItem, PlotCurveItem, and ViewBox, amongst others.
|
||||
from PyQt4 import QtGui, QtCore
|
||||
if not hasattr(QtCore, 'Signal'):
|
||||
QtCore.Signal = QtCore.pyqtSignal
|
||||
from ObjectWorkaround import *
|
||||
#from ObjectWorkaround import *
|
||||
#tryWorkaround(QtCore, QtGui)
|
||||
#from numpy import *
|
||||
import numpy as np
|
||||
@ -28,8 +28,16 @@ from Point import *
|
||||
from functions import *
|
||||
import types, sys, struct
|
||||
import weakref
|
||||
#import debug
|
||||
#from debug import *
|
||||
|
||||
## QGraphicsObject didn't appear until 4.6; this is for compatibility with 4.5
|
||||
if not hasattr(QtGui, 'QGraphicsObject'):
|
||||
class QGraphicsObject(QtGui.QGraphicsWidget):
|
||||
def shape(self):
|
||||
return QtGui.QGraphicsItem.shape(self)
|
||||
QtGui.QGraphicsObject = QGraphicsObject
|
||||
|
||||
|
||||
## Should probably just use QGraphicsGroupItem and instruct it to pass events on to children..
|
||||
class ItemGroup(QtGui.QGraphicsItem):
|
||||
@ -68,12 +76,12 @@ class ItemGroup(QtGui.QGraphicsItem):
|
||||
|
||||
|
||||
|
||||
class GraphicsObject(QGraphicsObject):
|
||||
class GraphicsObject(QtGui.QGraphicsObject):
|
||||
"""Extends QGraphicsObject with a few important functions.
|
||||
(Most of these assume that the object is in a scene with a single view)"""
|
||||
|
||||
def __init__(self, *args):
|
||||
QGraphicsObject.__init__(self, *args)
|
||||
QtGui.QGraphicsObject.__init__(self, *args)
|
||||
self._view = None
|
||||
|
||||
def getViewWidget(self):
|
||||
@ -174,14 +182,19 @@ class GraphicsObject(QGraphicsObject):
|
||||
|
||||
|
||||
|
||||
class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
class ImageItem(QtGui.QGraphicsObject):
|
||||
|
||||
sigImageChanged = QtCore.Signal()
|
||||
|
||||
if 'linux' not in sys.platform: ## disable weave optimization on linux--broken there.
|
||||
useWeave = True
|
||||
else:
|
||||
useWeave = False
|
||||
|
||||
def __init__(self, image=None, copy=True, parent=None, border=None, *args):
|
||||
QObjectWorkaround.__init__(self)
|
||||
#QObjectWorkaround.__init__(self)
|
||||
QtGui.QGraphicsObject.__init__(self)
|
||||
#self.pixmapItem = QtGui.QGraphicsPixmapItem(self)
|
||||
self.qimage = QtGui.QImage()
|
||||
self.pixmap = None
|
||||
#self.useWeave = True
|
||||
@ -195,7 +208,7 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
border = mkPen(border)
|
||||
self.border = border
|
||||
|
||||
QtGui.QGraphicsPixmapItem.__init__(self, parent, *args)
|
||||
#QtGui.QGraphicsPixmapItem.__init__(self, parent, *args)
|
||||
#self.pixmapItem = QtGui.QGraphicsPixmapItem(self)
|
||||
if image is not None:
|
||||
self.updateImage(image, copy, autoRange=True)
|
||||
@ -219,6 +232,11 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
return None
|
||||
return self.pixmap.height()
|
||||
|
||||
def boundingRect(self):
|
||||
if self.pixmap is None:
|
||||
return QtCore.QRectF(0., 0., 0., 0.)
|
||||
return QtCore.QRectF(0., 0., float(self.width()), float(self.height()))
|
||||
|
||||
def setClipLevel(self, level=None):
|
||||
self.clipLevel = level
|
||||
|
||||
@ -252,6 +270,8 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
return
|
||||
else:
|
||||
gotNewData = True
|
||||
if self.image is None or image.shape != self.image.shape:
|
||||
self.prepareGeometryChange()
|
||||
if copy:
|
||||
self.image = image.view(np.ndarray).copy()
|
||||
else:
|
||||
@ -323,7 +343,7 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
im2 = im.transpose(axh['y'], axh['x'], axh['c'])
|
||||
|
||||
for i in range(0, im.shape[axh['c']]):
|
||||
im1[..., i] = im2[..., i]
|
||||
im1[..., 2-i] = im2[..., i] ## for some reason, the colors line up as BGR in the final image.
|
||||
|
||||
for i in range(im.shape[axh['c']], 3):
|
||||
im1[..., i] = 0
|
||||
@ -345,14 +365,16 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
im1[..., 2][mask] = 255
|
||||
#print "Final image:", im1.dtype, im1.min(), im1.max(), im1.shape
|
||||
self.ims = im1.tostring() ## Must be held in memory here because qImage won't do it for us :(
|
||||
qimage = QtGui.QImage(self.ims, im1.shape[1], im1.shape[0], QtGui.QImage.Format_ARGB32)
|
||||
qimage = QtGui.QImage(buffer(self.ims), im1.shape[1], im1.shape[0], QtGui.QImage.Format_ARGB32)
|
||||
self.pixmap = QtGui.QPixmap.fromImage(qimage)
|
||||
##del self.ims
|
||||
self.setPixmap(self.pixmap)
|
||||
#self.pixmapItem.setPixmap(self.pixmap)
|
||||
|
||||
self.update()
|
||||
|
||||
if gotNewData:
|
||||
self.emit(QtCore.SIGNAL('imageChanged'))
|
||||
#self.emit(QtCore.SIGNAL('imageChanged'))
|
||||
self.sigImageChanged.emit()
|
||||
|
||||
def getPixmap(self):
|
||||
return self.pixmap.copy()
|
||||
@ -397,8 +419,15 @@ class ImageItem(QtGui.QGraphicsPixmapItem, QObjectWorkaround):
|
||||
p.setPen(self.border)
|
||||
p.drawRect(self.boundingRect())
|
||||
|
||||
def pixelSize(self):
|
||||
"""return size of a single pixel in the image"""
|
||||
br = self.sceneBoundingRect()
|
||||
return br.width()/self.pixmap.width(), br.height()/self.pixmap.height()
|
||||
|
||||
class PlotCurveItem(GraphicsObject):
|
||||
|
||||
sigPlotChanged = QtCore.Signal(object)
|
||||
|
||||
"""Class representing a single plot curve."""
|
||||
|
||||
sigClicked = QtCore.Signal(object)
|
||||
@ -565,6 +594,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
self.updateData(y, x, copy)
|
||||
|
||||
def updateData(self, data, x=None, copy=False):
|
||||
#prof = debug.Profiler('PlotCurveItem.updateData', disabled=True)
|
||||
if isinstance(data, list):
|
||||
data = np.array(data)
|
||||
if isinstance(x, list):
|
||||
@ -591,7 +621,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
x = data[tuple(ind)]
|
||||
elif data.ndim == 1:
|
||||
y = data
|
||||
|
||||
#prof.mark("data checks")
|
||||
self.prepareGeometryChange()
|
||||
if copy:
|
||||
self.yData = y.copy()
|
||||
@ -602,6 +632,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
self.xData = x.copy()
|
||||
else:
|
||||
self.xData = x
|
||||
#prof.mark('copy')
|
||||
|
||||
if x is None:
|
||||
self.xData = np.arange(0, self.yData.shape[0])
|
||||
@ -610,10 +641,15 @@ class PlotCurveItem(GraphicsObject):
|
||||
raise Exception("X and Y arrays must be the same shape--got %s and %s." % (str(x.shape), str(y.shape)))
|
||||
|
||||
self.path = None
|
||||
#self.specPath = None
|
||||
self.xDisp = self.yDisp = None
|
||||
|
||||
#prof.mark('set')
|
||||
self.update()
|
||||
self.emit(QtCore.SIGNAL('plotChanged'), self)
|
||||
#prof.mark('update')
|
||||
#self.emit(QtCore.SIGNAL('plotChanged'), self)
|
||||
self.sigPlotChanged.emit(self)
|
||||
#prof.mark('emit')
|
||||
#prof.finish()
|
||||
|
||||
def generatePath(self, x, y):
|
||||
path = QtGui.QPainterPath()
|
||||
@ -634,25 +670,32 @@ class PlotCurveItem(GraphicsObject):
|
||||
## 0(i4)
|
||||
##
|
||||
## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')
|
||||
#
|
||||
|
||||
#prof = debug.Profiler('PlotCurveItem.generatePath', disabled=True)
|
||||
|
||||
n = x.shape[0]
|
||||
# create empty array, pad with extra space on either end
|
||||
arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
|
||||
#prof.mark('create empty')
|
||||
# write first two integers
|
||||
arr.data[12:20] = struct.pack('>ii', n, 0)
|
||||
# Fill array with vertex values
|
||||
arr[1:-1]['x'] = x
|
||||
arr[1:-1]['y'] = y
|
||||
arr[1:-1]['c'] = 1
|
||||
#prof.mark('fill array')
|
||||
# write last 0
|
||||
lastInd = 20*(n+1)
|
||||
arr.data[lastInd:lastInd+4] = struct.pack('>i', 0)
|
||||
|
||||
# create datastream object and stream into path
|
||||
buf = QtCore.QByteArray(arr.data[12:lastInd+4]) # I think one unnecessary copy happens here
|
||||
#prof.mark('create buffer')
|
||||
ds = QtCore.QDataStream(buf)
|
||||
#prof.mark('create dataStream')
|
||||
ds >> path
|
||||
|
||||
#prof.mark('load path')
|
||||
#prof.finish()
|
||||
return path
|
||||
|
||||
def boundingRect(self):
|
||||
@ -677,6 +720,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
return QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
#prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True)
|
||||
if self.xData is None:
|
||||
return
|
||||
#if self.opts['spectrumMode']:
|
||||
@ -688,6 +732,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
if self.path is None:
|
||||
self.path = self.generatePath(*self.getData())
|
||||
path = self.path
|
||||
#prof.mark('generate path')
|
||||
|
||||
if self.shadow is not None:
|
||||
sp = QtGui.QPen(self.shadow)
|
||||
@ -709,7 +754,9 @@ class PlotCurveItem(GraphicsObject):
|
||||
p.drawPath(path)
|
||||
p.setPen(cp)
|
||||
p.drawPath(path)
|
||||
#prof.mark('drawPath')
|
||||
|
||||
#prof.finish()
|
||||
#p.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
|
||||
#p.drawRect(self.boundingRect())
|
||||
|
||||
@ -742,7 +789,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
self.sigClicked.emit(self)
|
||||
|
||||
|
||||
class CurvePoint(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
class CurvePoint(QtGui.QGraphicsObject):
|
||||
"""A GraphicsItem that sets its location to a point on a PlotCurveItem.
|
||||
The position along the curve is a property, and thus can be easily animated."""
|
||||
|
||||
@ -750,17 +797,16 @@ class CurvePoint(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
"""Position can be set either as an index referring to the sample number or
|
||||
the position 0.0 - 1.0"""
|
||||
|
||||
QtGui.QGraphicsItem.__init__(self)
|
||||
QObjectWorkaround.__init__(self)
|
||||
self.curve = None
|
||||
QtGui.QGraphicsObject.__init__(self)
|
||||
#QObjectWorkaround.__init__(self)
|
||||
self.curve = weakref.ref(curve)
|
||||
self.setParentItem(curve)
|
||||
self.setProperty('position', 0.0)
|
||||
self.setProperty('index', 0)
|
||||
|
||||
if hasattr(self, 'ItemHasNoContents'):
|
||||
self.setFlags(self.flags() | self.ItemHasNoContents)
|
||||
|
||||
self.curve = curve
|
||||
self.setParentItem(curve)
|
||||
if pos is not None:
|
||||
self.setPos(pos)
|
||||
else:
|
||||
@ -773,22 +819,22 @@ class CurvePoint(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
self.setProperty('index', index)
|
||||
|
||||
def event(self, ev):
|
||||
if not isinstance(ev, QtCore.QDynamicPropertyChangeEvent) or self.curve is None:
|
||||
return
|
||||
if not isinstance(ev, QtCore.QDynamicPropertyChangeEvent) or self.curve() is None:
|
||||
return False
|
||||
|
||||
if ev.propertyName() == 'index':
|
||||
index = self.property('index').toInt()[0]
|
||||
elif ev.propertyName() == 'position':
|
||||
index = None
|
||||
else:
|
||||
return
|
||||
return False
|
||||
|
||||
(x, y) = self.curve.getData()
|
||||
(x, y) = self.curve().getData()
|
||||
if index is None:
|
||||
#print self.property('position').toDouble()[0], self.property('position').typeName()
|
||||
index = (len(x)-1) * clip(self.property('position').toDouble()[0], 0.0, 1.0)
|
||||
|
||||
if index != int(index):
|
||||
if index != int(index): ## interpolate floating-point values
|
||||
i1 = int(index)
|
||||
i2 = clip(i1+1, 0, len(x)-1)
|
||||
s2 = index-i1
|
||||
@ -806,6 +852,7 @@ class CurvePoint(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
self.resetTransform()
|
||||
self.rotate(180+ ang * 180 / np.pi)
|
||||
QtGui.QGraphicsItem.setPos(self, *newPos)
|
||||
return True
|
||||
|
||||
def boundingRect(self):
|
||||
return QtCore.QRectF()
|
||||
@ -814,7 +861,7 @@ class CurvePoint(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
pass
|
||||
|
||||
def makeAnimation(self, prop='position', start=0.0, end=1.0, duration=10000, loop=1):
|
||||
anim = QtCore.QPropertyAnimation(self._qObj_, prop)
|
||||
anim = QtCore.QPropertyAnimation(self, prop)
|
||||
anim.setDuration(duration)
|
||||
anim.setStartValue(start)
|
||||
anim.setEndValue(end)
|
||||
@ -939,22 +986,27 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
|
||||
for s in spots:
|
||||
pos = Point(s['pos'])
|
||||
size = s.get('size', self.size)
|
||||
if self.pxMode:
|
||||
psize = 0
|
||||
else:
|
||||
psize = size
|
||||
if xmn is None:
|
||||
xmn = pos[0]-psize
|
||||
xmx = pos[0]+psize
|
||||
ymn = pos[1]-psize
|
||||
ymx = pos[1]+psize
|
||||
else:
|
||||
xmn = min(xmn, pos[0]-psize)
|
||||
xmx = max(xmx, pos[0]+psize)
|
||||
ymn = min(ymn, pos[1]-psize)
|
||||
ymx = max(ymx, pos[1]+psize)
|
||||
#print pos, xmn, xmx, ymn, ymx
|
||||
brush = s.get('brush', self.brush)
|
||||
pen = s.get('pen', self.pen)
|
||||
pen.setCosmetic(True)
|
||||
data = s.get('data', None)
|
||||
item = self.mkSpot(pos, size, self.pxMode, brush, pen, data)
|
||||
self.spots.append(item)
|
||||
if xmn is None:
|
||||
xmn = pos[0]-size
|
||||
xmx = pos[0]+size
|
||||
ymn = pos[1]-size
|
||||
ymx = pos[1]+size
|
||||
else:
|
||||
xmn = min(xmn, pos[0]-size)
|
||||
xmx = max(xmx, pos[0]+size)
|
||||
ymn = min(ymn, pos[1]-size)
|
||||
ymx = max(ymx, pos[1]+size)
|
||||
self.range = [[xmn, xmx], [ymn, ymx]]
|
||||
|
||||
|
||||
@ -967,7 +1019,8 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
def boundingRect(self):
|
||||
((xmn, xmx), (ymn, ymx)) = self.range
|
||||
|
||||
if xmn is None or xmx is None or ymn is None or ymx is None:
|
||||
return QtCore.QRectF()
|
||||
return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
|
||||
|
||||
def paint(self, p, *args):
|
||||
@ -985,13 +1038,15 @@ class SpotItem(QtGui.QGraphicsWidget):
|
||||
def __init__(self, size, pxMode, brush, pen, data):
|
||||
QtGui.QGraphicsWidget.__init__(self)
|
||||
if pxMode:
|
||||
self.setCacheMode(self.DeviceCoordinateCache)
|
||||
self.setFlags(self.flags() | self.ItemIgnoresTransformations)
|
||||
#self.setCacheMode(self.DeviceCoordinateCache) ## causes crash on linux
|
||||
self.pen = pen
|
||||
self.brush = brush
|
||||
self.path = QtGui.QPainterPath()
|
||||
s2 = size/2.
|
||||
self.path.addEllipse(QtCore.QRectF(-s2, -s2, size, size))
|
||||
#s2 = size/2.
|
||||
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||
self.scale(size, size)
|
||||
self.data = data
|
||||
|
||||
def setBrush(self, brush):
|
||||
@ -1036,6 +1091,7 @@ class SpotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
|
||||
class ROIPlotItem(PlotCurveItem):
|
||||
"""Plot curve that monitors an ROI and image for changes to automatically replot."""
|
||||
def __init__(self, roi, data, img, axes=(0,1), xVals=None, color=None):
|
||||
self.roi = roi
|
||||
self.roiData = data
|
||||
@ -1043,7 +1099,8 @@ class ROIPlotItem(PlotCurveItem):
|
||||
self.axes = axes
|
||||
self.xVals = xVals
|
||||
PlotCurveItem.__init__(self, self.getRoiData(), x=self.xVals, color=color)
|
||||
roi.connect(roi, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
|
||||
#roi.connect(roi, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
|
||||
roi.sigRegionChanged.connect(self.roiChangedEvent)
|
||||
#self.roiChangedEvent()
|
||||
|
||||
def getRoiData(self):
|
||||
@ -1073,7 +1130,8 @@ class UIGraphicsItem(GraphicsObject):
|
||||
self._viewRect = self._view().rect()
|
||||
self._viewTransform = self.viewTransform()
|
||||
self.setNewBounds()
|
||||
QtCore.QObject.connect(view, QtCore.SIGNAL('viewChanged'), self.viewChangedEvent)
|
||||
#QtCore.QObject.connect(view, QtCore.SIGNAL('viewChanged'), self.viewChangedEvent)
|
||||
view.sigRangeChanged.connect(self.viewRangeChanged)
|
||||
|
||||
def viewRect(self):
|
||||
"""Return the viewport widget rect"""
|
||||
@ -1110,7 +1168,7 @@ class UIGraphicsItem(GraphicsObject):
|
||||
self.bounds = self.viewTransform().inverted()[0].mapRect(bounds)
|
||||
self.prepareGeometryChange()
|
||||
|
||||
def viewChangedEvent(self):
|
||||
def viewRangeChanged(self):
|
||||
"""Called when the view widget is resized"""
|
||||
self.boundingRect()
|
||||
self.update()
|
||||
@ -1378,23 +1436,31 @@ class ScaleItem(QtGui.QGraphicsWidget):
|
||||
|
||||
def linkToView(self, view):
|
||||
if self.orientation in ['right', 'left']:
|
||||
signal = QtCore.SIGNAL('yRangeChanged')
|
||||
if self.linkedView is not None and self.linkedView() is not None:
|
||||
#view.sigYRangeChanged.disconnect(self.linkedViewChanged)
|
||||
## should be this instead?
|
||||
self.linkedView().sigYRangeChanged.disconnect(self.linkedViewChanged)
|
||||
self.linkedView = weakref.ref(view)
|
||||
view.sigYRangeChanged.connect(self.linkedViewChanged)
|
||||
#signal = QtCore.SIGNAL('yRangeChanged')
|
||||
else:
|
||||
signal = QtCore.SIGNAL('xRangeChanged')
|
||||
if self.linkedView is not None and self.linkedView() is not None:
|
||||
#view.sigYRangeChanged.disconnect(self.linkedViewChanged)
|
||||
## should be this instead?
|
||||
self.linkedView().sigXRangeChanged.disconnect(self.linkedViewChanged)
|
||||
self.linkedView = weakref.ref(view)
|
||||
view.sigXRangeChanged.connect(self.linkedViewChanged)
|
||||
#signal = QtCore.SIGNAL('xRangeChanged')
|
||||
|
||||
if self.linkedView is not None:
|
||||
QtCore.QObject.disconnect(view, signal, self.linkedViewChanged)
|
||||
self.linkedView = view
|
||||
QtCore.QObject.connect(view, signal, self.linkedViewChanged)
|
||||
|
||||
def linkedViewChanged(self, _, newRange):
|
||||
def linkedViewChanged(self, view, newRange):
|
||||
self.setRange(*newRange)
|
||||
|
||||
def boundingRect(self):
|
||||
if self.linkedView is None or self.grid is False:
|
||||
if self.linkedView is None or self.linkedView() is None or self.grid is False:
|
||||
return self.mapRectFromParent(self.geometry())
|
||||
else:
|
||||
return self.mapRectFromParent(self.geometry()) | self.mapRectFromScene(self.linkedView.mapRectToScene(self.linkedView.boundingRect()))
|
||||
return self.mapRectFromParent(self.geometry()) | self.mapRectFromScene(self.linkedView().mapRectToScene(self.linkedView().boundingRect()))
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
p.setPen(self.pen)
|
||||
@ -1402,10 +1468,10 @@ class ScaleItem(QtGui.QGraphicsWidget):
|
||||
#bounds = self.boundingRect()
|
||||
bounds = self.mapRectFromParent(self.geometry())
|
||||
|
||||
if self.linkedView is None or self.grid is False:
|
||||
if self.linkedView is None or self.linkedView() is None or self.grid is False:
|
||||
tbounds = bounds
|
||||
else:
|
||||
tbounds = self.mapRectFromScene(self.linkedView.mapRectToScene(self.linkedView.boundingRect()))
|
||||
tbounds = self.mapRectFromScene(self.linkedView().mapRectToScene(self.linkedView().boundingRect()))
|
||||
|
||||
if self.orientation == 'left':
|
||||
p.drawLine(bounds.topRight(), bounds.bottomRight())
|
||||
@ -1577,6 +1643,12 @@ class ScaleItem(QtGui.QGraphicsWidget):
|
||||
|
||||
|
||||
class ViewBox(QtGui.QGraphicsWidget):
|
||||
|
||||
sigYRangeChanged = QtCore.Signal(object, object)
|
||||
sigXRangeChanged = QtCore.Signal(object, object)
|
||||
sigRangeChangedManually = QtCore.Signal(object)
|
||||
sigRangeChanged = QtCore.Signal(object, object)
|
||||
|
||||
"""Box that allows internal scaling/panning of children by mouse drag. Not compatible with GraphicsView having the same functionality."""
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QGraphicsWidget.__init__(self, parent)
|
||||
@ -1745,7 +1817,8 @@ class ViewBox(QtGui.QGraphicsWidget):
|
||||
mask *= np.array([1, -1])
|
||||
tr = dif*mask
|
||||
self.translateBy(tr, viewCoords=True)
|
||||
self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
|
||||
#self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
|
||||
self.sigRangeChangedManually.emit(self.mouseEnabled)
|
||||
ev.accept()
|
||||
elif ev.buttons() & QtCore.Qt.RightButton:
|
||||
dif = ev.screenPos() - ev.lastScreenPos()
|
||||
@ -1755,7 +1828,8 @@ class ViewBox(QtGui.QGraphicsWidget):
|
||||
#print mask, dif, s
|
||||
center = Point(self.childGroup.transform().inverted()[0].map(ev.buttonDownPos(QtCore.Qt.RightButton)))
|
||||
self.scaleBy(s, center)
|
||||
self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
|
||||
#self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
|
||||
self.sigRangeChangedManually.emit(self.mouseEnabled)
|
||||
ev.accept()
|
||||
else:
|
||||
ev.ignore()
|
||||
@ -1802,8 +1876,10 @@ class ViewBox(QtGui.QGraphicsWidget):
|
||||
self.range[1] = [min, max]
|
||||
#self.ctrl.yMinText.setText('%g' % min)
|
||||
#self.ctrl.yMaxText.setText('%g' % max)
|
||||
self.emit(QtCore.SIGNAL('yRangeChanged'), self, (min, max))
|
||||
self.emit(QtCore.SIGNAL('viewChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('yRangeChanged'), self, (min, max))
|
||||
self.sigYRangeChanged.emit(self, (min, max))
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), self)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
if update:
|
||||
self.updateMatrix()
|
||||
|
||||
@ -1827,8 +1903,10 @@ class ViewBox(QtGui.QGraphicsWidget):
|
||||
self.range[0] = [min, max]
|
||||
#self.ctrl.xMinText.setText('%g' % min)
|
||||
#self.ctrl.xMaxText.setText('%g' % max)
|
||||
self.emit(QtCore.SIGNAL('xRangeChanged'), self, (min, max))
|
||||
self.emit(QtCore.SIGNAL('viewChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('xRangeChanged'), self, (min, max))
|
||||
self.sigXRangeChanged.emit(self, (min, max))
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), self)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
if update:
|
||||
self.updateMatrix()
|
||||
|
||||
@ -1852,6 +1930,11 @@ class ViewBox(QtGui.QGraphicsWidget):
|
||||
|
||||
|
||||
class InfiniteLine(GraphicsObject):
|
||||
|
||||
sigDragged = QtCore.Signal(object)
|
||||
sigPositionChangeFinished = QtCore.Signal(object)
|
||||
sigPositionChanged = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, view, pos=0, angle=90, pen=None, movable=False, bounds=None):
|
||||
GraphicsObject.__init__(self)
|
||||
self.bounds = QtCore.QRectF() ## graphicsitem boundary
|
||||
@ -1877,7 +1960,8 @@ class InfiniteLine(GraphicsObject):
|
||||
#self.setFlag(self.ItemSendsScenePositionChanges)
|
||||
#for p in self.getBoundingParents():
|
||||
#QtCore.QObject.connect(p, QtCore.SIGNAL('viewChanged'), self.updateLine)
|
||||
QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.updateLine)
|
||||
#QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.updateLine)
|
||||
self.view().sigRangeChanged.connect(self.updateLine)
|
||||
|
||||
def setMovable(self, m):
|
||||
self.movable = m
|
||||
@ -1935,7 +2019,8 @@ class InfiniteLine(GraphicsObject):
|
||||
if self.p != newPos:
|
||||
self.p = newPos
|
||||
self.updateLine()
|
||||
self.emit(QtCore.SIGNAL('positionChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('positionChanged'), self)
|
||||
self.sigPositionChanged.emit(self)
|
||||
|
||||
def getXPos(self):
|
||||
return self.p[0]
|
||||
@ -2048,17 +2133,23 @@ class InfiniteLine(GraphicsObject):
|
||||
|
||||
def mouseMoveEvent(self, ev):
|
||||
self.setPos(self.mapToParent(ev.pos()) - self.pressDelta)
|
||||
self.emit(QtCore.SIGNAL('dragged'), self)
|
||||
#self.emit(QtCore.SIGNAL('dragged'), self)
|
||||
self.sigDragged.emit(self)
|
||||
self.hasMoved = True
|
||||
|
||||
def mouseReleaseEvent(self, ev):
|
||||
if self.hasMoved and ev.button() == QtCore.Qt.LeftButton:
|
||||
self.hasMoved = False
|
||||
self.emit(QtCore.SIGNAL('positionChangeFinished'), self)
|
||||
#self.emit(QtCore.SIGNAL('positionChangeFinished'), self)
|
||||
self.sigPositionChangeFinished.emit(self)
|
||||
|
||||
|
||||
|
||||
class LinearRegionItem(GraphicsObject):
|
||||
|
||||
sigRegionChangeFinished = QtCore.Signal(object)
|
||||
sigRegionChanged = QtCore.Signal(object)
|
||||
|
||||
"""Used for marking a horizontal or vertical region in plots."""
|
||||
def __init__(self, view, orientation="vertical", vals=[0,1], brush=None, movable=True, bounds=None):
|
||||
GraphicsObject.__init__(self)
|
||||
@ -2080,12 +2171,15 @@ class LinearRegionItem(GraphicsObject):
|
||||
self.lines = [
|
||||
InfiniteLine(view, QtCore.QPointF(vals[0], 0), 90, movable=movable, bounds=bounds),
|
||||
InfiniteLine(view, QtCore.QPointF(vals[1], 0), 90, movable=movable, bounds=bounds)]
|
||||
QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.updateBounds)
|
||||
#QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.updateBounds)
|
||||
self.view().sigRangeChanged.connect(self.updateBounds)
|
||||
|
||||
for l in self.lines:
|
||||
l.setParentItem(self)
|
||||
l.connect(l, QtCore.SIGNAL('positionChangeFinished'), self.lineMoveFinished)
|
||||
l.connect(l, QtCore.SIGNAL('positionChanged'), self.lineMoved)
|
||||
#l.connect(l, QtCore.SIGNAL('positionChangeFinished'), self.lineMoveFinished)
|
||||
l.sigPositionChangeFinished.connect(self.lineMoveFinished)
|
||||
#l.connect(l, QtCore.SIGNAL('positionChanged'), self.lineMoved)
|
||||
l.sigPositionChanged.connect(self.lineMoved)
|
||||
|
||||
if brush is None:
|
||||
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50))
|
||||
@ -2106,10 +2200,12 @@ class LinearRegionItem(GraphicsObject):
|
||||
|
||||
def lineMoved(self):
|
||||
self.updateBounds()
|
||||
self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
self.sigRegionChanged.emit(self)
|
||||
|
||||
def lineMoveFinished(self):
|
||||
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
self.sigRegionChangeFinished.emit(self)
|
||||
|
||||
|
||||
def updateBounds(self):
|
||||
@ -2200,11 +2296,13 @@ class VTickGroup(QtGui.QGraphicsPathItem):
|
||||
if self.view is not None:
|
||||
if relative:
|
||||
#QtCore.QObject.connect(self.view, QtCore.SIGNAL('viewChanged'), self.rebuildTicks)
|
||||
QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.rescale)
|
||||
#QtCore.QObject.connect(self.view(), QtCore.SIGNAL('viewChanged'), self.rescale)
|
||||
self.view().sigRangeChanged.connect(self.rescale)
|
||||
else:
|
||||
try:
|
||||
#QtCore.QObject.disconnect(self.view, QtCore.SIGNAL('viewChanged'), self.rebuildTicks)
|
||||
QtCore.QObject.disconnect(self.view(), QtCore.SIGNAL('viewChanged'), self.rescale)
|
||||
#QtCore.QObject.disconnect(self.view(), QtCore.SIGNAL('viewChanged'), self.rescale)
|
||||
self.view().sigRangeChanged.disconnect(self.rescale)
|
||||
except:
|
||||
pass
|
||||
self.rebuildTicks()
|
||||
@ -2268,6 +2366,8 @@ class VTickGroup(QtGui.QGraphicsPathItem):
|
||||
|
||||
|
||||
class GridItem(UIGraphicsItem):
|
||||
"""Class used to make square grids in plots. NOT the grid used for running scanner sequences."""
|
||||
|
||||
def __init__(self, view, bounds=None, *args):
|
||||
UIGraphicsItem.__init__(self, view, bounds)
|
||||
#QtGui.QGraphicsItem.__init__(self, *args)
|
||||
@ -2277,9 +2377,9 @@ class GridItem(UIGraphicsItem):
|
||||
self.picture = None
|
||||
|
||||
|
||||
def viewChangedEvent(self):
|
||||
def viewRangeChanged(self):
|
||||
self.picture = None
|
||||
UIGraphicsItem.viewChangedEvent(self)
|
||||
UIGraphicsItem.viewRangeChanged(self)
|
||||
#self.update()
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
@ -2417,7 +2517,20 @@ class ColorScaleBar(UIGraphicsItem):
|
||||
self.gradient = g
|
||||
self.update()
|
||||
|
||||
def setIntColorScale(self, minVal, maxVal, *args, **kargs):
|
||||
colors = [intColor(i, maxVal-minVal, *args, **kargs) for i in range(minVal, maxVal)]
|
||||
g = QtGui.QLinearGradient()
|
||||
for i in range(len(colors)):
|
||||
x = float(i)/len(colors)
|
||||
g.setColorAt(x, colors[i])
|
||||
self.setGradient(g)
|
||||
if 'labels' not in kargs:
|
||||
self.setLabels({str(minVal/10.): 0, str(maxVal): 1})
|
||||
else:
|
||||
self.setLabels({kargs['labels'][0]:0, kargs['labels'][1]:1})
|
||||
|
||||
def setLabels(self, l):
|
||||
"""Defines labels to appear next to the color scale"""
|
||||
self.labels = l
|
||||
self.update()
|
||||
|
||||
@ -2429,7 +2542,7 @@ class ColorScaleBar(UIGraphicsItem):
|
||||
labelWidth = 0
|
||||
labelHeight = 0
|
||||
for k in self.labels:
|
||||
b = p.boundingRect(QtCore.QRectF(0, 0, 0, 0), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, k)
|
||||
b = p.boundingRect(QtCore.QRectF(0, 0, 0, 0), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(k))
|
||||
labelWidth = max(labelWidth, b.width())
|
||||
labelHeight = max(labelHeight, b.height())
|
||||
|
||||
@ -2484,6 +2597,6 @@ class ColorScaleBar(UIGraphicsItem):
|
||||
lh = labelHeight/unit.height()
|
||||
for k in self.labels:
|
||||
y = y1 + self.labels[k] * (y2-y1)
|
||||
p.drawText(QtCore.QRectF(tx/unit.width(), y/unit.height() - lh/2.0, 1000, lh), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, k)
|
||||
p.drawText(QtCore.QRectF(tx/unit.width(), y/unit.height() - lh/2.0, 1000, lh), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(k))
|
||||
|
||||
|
||||
|
271
widgets.py
271
widgets.py
@ -16,7 +16,8 @@ from numpy.linalg import norm
|
||||
import scipy.ndimage as ndimage
|
||||
from Point import *
|
||||
from math import cos, sin
|
||||
from ObjectWorkaround import *
|
||||
import functions as fn
|
||||
#from ObjectWorkaround import *
|
||||
|
||||
def rectStr(r):
|
||||
return "[%f, %f] + [%f, %f]" % (r.x(), r.y(), r.width(), r.height())
|
||||
@ -33,10 +34,15 @@ def rectStr(r):
|
||||
#return QtCore.QObject.connect(self._qObj_, *args)
|
||||
|
||||
|
||||
class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
class ROI(QtGui.QGraphicsObject):
|
||||
|
||||
sigRegionChangeFinished = QtCore.Signal(object)
|
||||
sigRegionChangeStarted = QtCore.Signal(object)
|
||||
sigRegionChanged = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, pos, size=Point(1, 1), angle=0.0, invertible=False, maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None, pen=None):
|
||||
QObjectWorkaround.__init__(self)
|
||||
QtGui.QGraphicsItem.__init__(self, parent)
|
||||
#QObjectWorkaround.__init__(self)
|
||||
QtGui.QGraphicsObject.__init__(self, parent)
|
||||
pos = Point(pos)
|
||||
size = Point(size)
|
||||
self.aspectLocked = False
|
||||
@ -45,7 +51,7 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
if pen is None:
|
||||
self.pen = QtGui.QPen(QtGui.QColor(255, 255, 255))
|
||||
else:
|
||||
self.pen = pen
|
||||
self.pen = fn.mkPen(pen)
|
||||
self.handlePen = QtGui.QPen(QtGui.QColor(150, 255, 255))
|
||||
self.handles = []
|
||||
self.state = {'pos': pos, 'size': size, 'angle': angle}
|
||||
@ -212,7 +218,8 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
self.isMoving = True
|
||||
self.preMoveState = self.getState()
|
||||
self.cursorOffset = self.scenePos() - ev.scenePos()
|
||||
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
self.sigRegionChangeStarted.emit(self)
|
||||
ev.accept()
|
||||
elif ev.button() == QtCore.Qt.RightButton:
|
||||
if self.isMoving:
|
||||
@ -236,7 +243,8 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
def mouseReleaseEvent(self, ev):
|
||||
if self.translatable:
|
||||
self.isMoving = False
|
||||
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
self.sigRegionChangeFinished.emit(self)
|
||||
|
||||
def cancelMove(self):
|
||||
self.isMoving = False
|
||||
@ -247,14 +255,16 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
self.isMoving = True
|
||||
self.preMoveState = self.getState()
|
||||
|
||||
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
self.sigRegionChangeStarted.emit(self)
|
||||
#self.pressPos = self.mapFromScene(ev.scenePos())
|
||||
#self.pressHandlePos = self.handles[pt]['item'].pos()
|
||||
|
||||
def pointReleaseEvent(self, pt, ev):
|
||||
#print "release"
|
||||
self.isMoving = False
|
||||
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
self.sigRegionChangeFinished.emit(self)
|
||||
|
||||
def stateCopy(self):
|
||||
sc = {}
|
||||
@ -316,7 +326,8 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
|
||||
elif h['type'] == 'f':
|
||||
h['item'].setPos(self.mapFromScene(pos))
|
||||
self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
self.sigRegionChanged.emit(self)
|
||||
|
||||
elif h['type'] == 's':
|
||||
#c = h['center']
|
||||
@ -509,7 +520,8 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
if changed:
|
||||
#print "handle changed."
|
||||
self.update()
|
||||
self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
self.sigRegionChanged.emit(self)
|
||||
|
||||
|
||||
def scale(self, s, center=[0,0]):
|
||||
@ -628,104 +640,116 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
|
||||
|
||||
def getArrayRegion(self, data, img, axes=(0,1)):
|
||||
shape = self.state['size']
|
||||
|
||||
## transpose data so x and y are the first 2 axes
|
||||
trAx = range(0, data.ndim)
|
||||
trAx.remove(axes[0])
|
||||
trAx.remove(axes[1])
|
||||
tr1 = tuple(axes) + tuple(trAx)
|
||||
arr = data.transpose(tr1)
|
||||
origin = self.mapToItem(img, QtCore.QPointF(0, 0))
|
||||
|
||||
## Determine the minimal area of the data we will need
|
||||
(dataBounds, roiDataTransform) = self.getArraySlice(data, img, returnSlice=False, axes=axes)
|
||||
vx = self.mapToItem(img, QtCore.QPointF(1, 0)) - origin
|
||||
vy = self.mapToItem(img, QtCore.QPointF(0, 1)) - origin
|
||||
vectors = ((vx.x(), vx.y()), (vy.x(), vy.y()))
|
||||
origin = (origin.x(), origin.y())
|
||||
|
||||
## Pad data boundaries by 1px if possible
|
||||
dataBounds = (
|
||||
(max(dataBounds[0][0]-1, 0), min(dataBounds[0][1]+1, arr.shape[0])),
|
||||
(max(dataBounds[1][0]-1, 0), min(dataBounds[1][1]+1, arr.shape[1]))
|
||||
)
|
||||
#print "shape", shape, "vectors", vectors, "origin", origin
|
||||
|
||||
## Extract minimal data from array
|
||||
arr1 = arr[dataBounds[0][0]:dataBounds[0][1], dataBounds[1][0]:dataBounds[1][1]]
|
||||
return fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, order=1)
|
||||
|
||||
## Update roiDataTransform to reflect this extraction
|
||||
roiDataTransform *= QtGui.QTransform().translate(-dataBounds[0][0], -dataBounds[1][0])
|
||||
### (roiDataTransform now maps from ROI coords to extracted data coords)
|
||||
### transpose data so x and y are the first 2 axes
|
||||
#trAx = range(0, data.ndim)
|
||||
#trAx.remove(axes[0])
|
||||
#trAx.remove(axes[1])
|
||||
#tr1 = tuple(axes) + tuple(trAx)
|
||||
#arr = data.transpose(tr1)
|
||||
|
||||
### Determine the minimal area of the data we will need
|
||||
#(dataBounds, roiDataTransform) = self.getArraySlice(data, img, returnSlice=False, axes=axes)
|
||||
|
||||
### Pad data boundaries by 1px if possible
|
||||
#dataBounds = (
|
||||
#(max(dataBounds[0][0]-1, 0), min(dataBounds[0][1]+1, arr.shape[0])),
|
||||
#(max(dataBounds[1][0]-1, 0), min(dataBounds[1][1]+1, arr.shape[1]))
|
||||
#)
|
||||
|
||||
### Extract minimal data from array
|
||||
#arr1 = arr[dataBounds[0][0]:dataBounds[0][1], dataBounds[1][0]:dataBounds[1][1]]
|
||||
|
||||
### Update roiDataTransform to reflect this extraction
|
||||
#roiDataTransform *= QtGui.QTransform().translate(-dataBounds[0][0], -dataBounds[1][0])
|
||||
#### (roiDataTransform now maps from ROI coords to extracted data coords)
|
||||
|
||||
|
||||
## Rotate array
|
||||
if abs(self.state['angle']) > 1e-5:
|
||||
arr2 = ndimage.rotate(arr1, self.state['angle'] * 180 / np.pi, order=1)
|
||||
### Rotate array
|
||||
#if abs(self.state['angle']) > 1e-5:
|
||||
#arr2 = ndimage.rotate(arr1, self.state['angle'] * 180 / np.pi, order=1)
|
||||
|
||||
## update data transforms to reflect this rotation
|
||||
rot = QtGui.QTransform().rotate(self.state['angle'] * 180 / np.pi)
|
||||
roiDataTransform *= rot
|
||||
### update data transforms to reflect this rotation
|
||||
#rot = QtGui.QTransform().rotate(self.state['angle'] * 180 / np.pi)
|
||||
#roiDataTransform *= rot
|
||||
|
||||
## The rotation also causes a shift which must be accounted for:
|
||||
dataBound = QtCore.QRectF(0, 0, arr1.shape[0], arr1.shape[1])
|
||||
rotBound = rot.mapRect(dataBound)
|
||||
roiDataTransform *= QtGui.QTransform().translate(-rotBound.left(), -rotBound.top())
|
||||
### The rotation also causes a shift which must be accounted for:
|
||||
#dataBound = QtCore.QRectF(0, 0, arr1.shape[0], arr1.shape[1])
|
||||
#rotBound = rot.mapRect(dataBound)
|
||||
#roiDataTransform *= QtGui.QTransform().translate(-rotBound.left(), -rotBound.top())
|
||||
|
||||
else:
|
||||
arr2 = arr1
|
||||
#else:
|
||||
#arr2 = arr1
|
||||
|
||||
|
||||
|
||||
### Shift off partial pixels
|
||||
# 1. map ROI into current data space
|
||||
roiBounds = roiDataTransform.mapRect(self.boundingRect())
|
||||
#### Shift off partial pixels
|
||||
## 1. map ROI into current data space
|
||||
#roiBounds = roiDataTransform.mapRect(self.boundingRect())
|
||||
|
||||
# 2. Determine amount to shift data
|
||||
shift = (int(roiBounds.left()) - roiBounds.left(), int(roiBounds.bottom()) - roiBounds.bottom())
|
||||
if abs(shift[0]) > 1e-6 or abs(shift[1]) > 1e-6:
|
||||
# 3. pad array with 0s before shifting
|
||||
arr2a = np.zeros((arr2.shape[0]+2, arr2.shape[1]+2) + arr2.shape[2:], dtype=arr2.dtype)
|
||||
arr2a[1:-1, 1:-1] = arr2
|
||||
## 2. Determine amount to shift data
|
||||
#shift = (int(roiBounds.left()) - roiBounds.left(), int(roiBounds.bottom()) - roiBounds.bottom())
|
||||
#if abs(shift[0]) > 1e-6 or abs(shift[1]) > 1e-6:
|
||||
## 3. pad array with 0s before shifting
|
||||
#arr2a = np.zeros((arr2.shape[0]+2, arr2.shape[1]+2) + arr2.shape[2:], dtype=arr2.dtype)
|
||||
#arr2a[1:-1, 1:-1] = arr2
|
||||
|
||||
# 4. shift array and udpate transforms
|
||||
arr3 = ndimage.shift(arr2a, shift + (0,)*(arr2.ndim-2), order=1)
|
||||
roiDataTransform *= QtGui.QTransform().translate(1+shift[0], 1+shift[1])
|
||||
else:
|
||||
arr3 = arr2
|
||||
## 4. shift array and udpate transforms
|
||||
#arr3 = ndimage.shift(arr2a, shift + (0,)*(arr2.ndim-2), order=1)
|
||||
#roiDataTransform *= QtGui.QTransform().translate(1+shift[0], 1+shift[1])
|
||||
#else:
|
||||
#arr3 = arr2
|
||||
|
||||
|
||||
### Extract needed region from rotated/shifted array
|
||||
# 1. map ROI into current data space (round these values off--they should be exact integer values at this point)
|
||||
roiBounds = roiDataTransform.mapRect(self.boundingRect())
|
||||
#print self, roiBounds.height()
|
||||
#import traceback
|
||||
#traceback.print_stack()
|
||||
#### Extract needed region from rotated/shifted array
|
||||
## 1. map ROI into current data space (round these values off--they should be exact integer values at this point)
|
||||
#roiBounds = roiDataTransform.mapRect(self.boundingRect())
|
||||
##print self, roiBounds.height()
|
||||
##import traceback
|
||||
##traceback.print_stack()
|
||||
|
||||
roiBounds = QtCore.QRect(round(roiBounds.left()), round(roiBounds.top()), round(roiBounds.width()), round(roiBounds.height()))
|
||||
#roiBounds = QtCore.QRect(round(roiBounds.left()), round(roiBounds.top()), round(roiBounds.width()), round(roiBounds.height()))
|
||||
|
||||
#2. intersect ROI with data bounds
|
||||
dataBounds = roiBounds.intersect(QtCore.QRect(0, 0, arr3.shape[0], arr3.shape[1]))
|
||||
##2. intersect ROI with data bounds
|
||||
#dataBounds = roiBounds.intersect(QtCore.QRect(0, 0, arr3.shape[0], arr3.shape[1]))
|
||||
|
||||
#3. Extract data from array
|
||||
db = dataBounds
|
||||
bounds = (
|
||||
(db.left(), db.right()+1),
|
||||
(db.top(), db.bottom()+1)
|
||||
)
|
||||
arr4 = arr3[bounds[0][0]:bounds[0][1], bounds[1][0]:bounds[1][1]]
|
||||
##3. Extract data from array
|
||||
#db = dataBounds
|
||||
#bounds = (
|
||||
#(db.left(), db.right()+1),
|
||||
#(db.top(), db.bottom()+1)
|
||||
#)
|
||||
#arr4 = arr3[bounds[0][0]:bounds[0][1], bounds[1][0]:bounds[1][1]]
|
||||
|
||||
### Create zero array in size of ROI
|
||||
arr5 = np.zeros((roiBounds.width(), roiBounds.height()) + arr4.shape[2:], dtype=arr4.dtype)
|
||||
#### Create zero array in size of ROI
|
||||
#arr5 = np.zeros((roiBounds.width(), roiBounds.height()) + arr4.shape[2:], dtype=arr4.dtype)
|
||||
|
||||
## Fill array with ROI data
|
||||
orig = Point(dataBounds.topLeft() - roiBounds.topLeft())
|
||||
subArr = arr5[orig[0]:orig[0]+arr4.shape[0], orig[1]:orig[1]+arr4.shape[1]]
|
||||
subArr[:] = arr4[:subArr.shape[0], :subArr.shape[1]]
|
||||
### Fill array with ROI data
|
||||
#orig = Point(dataBounds.topLeft() - roiBounds.topLeft())
|
||||
#subArr = arr5[orig[0]:orig[0]+arr4.shape[0], orig[1]:orig[1]+arr4.shape[1]]
|
||||
#subArr[:] = arr4[:subArr.shape[0], :subArr.shape[1]]
|
||||
|
||||
|
||||
## figure out the reverse transpose order
|
||||
tr2 = np.array(tr1)
|
||||
for i in range(0, len(tr2)):
|
||||
tr2[tr1[i]] = i
|
||||
tr2 = tuple(tr2)
|
||||
### figure out the reverse transpose order
|
||||
#tr2 = np.array(tr1)
|
||||
#for i in range(0, len(tr2)):
|
||||
#tr2[tr1[i]] = i
|
||||
#tr2 = tuple(tr2)
|
||||
|
||||
## Untranspose array before returning
|
||||
return arr5.transpose(tr2)
|
||||
### Untranspose array before returning
|
||||
#return arr5.transpose(tr2)
|
||||
|
||||
|
||||
|
||||
@ -887,10 +911,14 @@ class LineROI(ROI):
|
||||
self.addScaleHandle([0.5, 1], [0.5, 0.5])
|
||||
|
||||
|
||||
class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
class MultiLineROI(QtGui.QGraphicsObject):
|
||||
|
||||
sigRegionChangeFinished = QtCore.Signal(object)
|
||||
sigRegionChangeStarted = QtCore.Signal(object)
|
||||
sigRegionChanged = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, points, width, pen=None, **args):
|
||||
QObjectWorkaround.__init__(self)
|
||||
QtGui.QGraphicsItem.__init__(self)
|
||||
QtGui.QGraphicsObject.__init__(self)
|
||||
self.pen = pen
|
||||
self.roiArgs = args
|
||||
if len(points) < 2:
|
||||
@ -912,9 +940,12 @@ class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
for l in self.lines:
|
||||
l.translatable = False
|
||||
#self.addToGroup(l)
|
||||
l.connect(l, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
|
||||
l.connect(l, QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
|
||||
l.connect(l, QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
|
||||
#l.connect(l, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
|
||||
l.sigRegionChanged.connect(self.roiChangedEvent)
|
||||
#l.connect(l, QtCore.SIGNAL('regionChangeStarted'), self.roiChangeStartedEvent)
|
||||
l.sigRegionChangeStarted.connect(self.roiChangeStartedEvent)
|
||||
#l.connect(l, QtCore.SIGNAL('regionChangeFinished'), self.roiChangeFinishedEvent)
|
||||
l.sigRegionChangeFinished.connect(self.roiChangeFinishedEvent)
|
||||
|
||||
def paint(self, *args):
|
||||
pass
|
||||
@ -927,13 +958,16 @@ class MultiLineROI(QtGui.QGraphicsItem, QObjectWorkaround):
|
||||
for l in self.lines[1:]:
|
||||
w0 = l.state['size'][1]
|
||||
l.scale([1.0, w/w0], center=[0.5,0.5])
|
||||
self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||
self.sigRegionChanged.emit(self)
|
||||
|
||||
def roiChangeStartedEvent(self):
|
||||
self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
||||
self.sigRegionChangeStarted.emit(self)
|
||||
|
||||
def roiChangeFinishedEvent(self):
|
||||
self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||
self.sigRegionChangeFinished.emit(self)
|
||||
|
||||
|
||||
def getArrayRegion(self, arr, img=None):
|
||||
@ -1038,6 +1072,57 @@ class PolygonROI(ROI):
|
||||
#sc['handles'] = self.handles
|
||||
return sc
|
||||
|
||||
|
||||
class LineSegmentROI(ROI):
|
||||
def __init__(self, positions, pos=None, **args):
|
||||
if pos is None:
|
||||
pos = [0,0]
|
||||
ROI.__init__(self, pos, [1,1], **args)
|
||||
#ROI.__init__(self, positions[0])
|
||||
for p in positions:
|
||||
self.addFreeHandle(p)
|
||||
self.setZValue(1000)
|
||||
|
||||
def listPoints(self):
|
||||
return [p['item'].pos() for p in self.handles]
|
||||
|
||||
def movePoint(self, *args, **kargs):
|
||||
ROI.movePoint(self, *args, **kargs)
|
||||
self.prepareGeometryChange()
|
||||
for h in self.handles:
|
||||
h['pos'] = h['item'].pos()
|
||||
|
||||
def paint(self, p, *args):
|
||||
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
p.setPen(self.pen)
|
||||
for i in range(len(self.handles)-1):
|
||||
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() ## |= gives the union of the two QRectFs
|
||||
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
|
||||
|
||||
def stateCopy(self):
|
||||
sc = {}
|
||||
sc['pos'] = Point(self.state['pos'])
|
||||
sc['size'] = Point(self.state['size'])
|
||||
sc['angle'] = self.state['angle']
|
||||
#sc['handles'] = self.handles
|
||||
return sc
|
||||
|
||||
|
||||
|
||||
class SpiralROI(ROI):
|
||||
def __init__(self, pos=None, size=None, **args):
|
||||
if size == None:
|
||||
|
Loading…
Reference in New Issue
Block a user