Commit 661e4411 authored by Luke Campagnola's avatar Luke Campagnola
Browse files

Numerous fixes

added scatter plots
parent dbcdf7ec
......@@ -163,10 +163,16 @@ class TickSlider(QtGui.QGraphicsView):
class GradientWidget(TickSlider):
def __init__(self, *args, **kargs):
TickSlider.__init__(self, *args, **kargs)
self.currentTick = None
self.currentTickColor = None
self.rectSize = 15
self.gradRect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, -self.rectSize, 100, self.rectSize))
self.colorMode = 'rgb'
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)
#self.gradient = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(100,0))
self.scene.addItem(self.gradRect)
......@@ -200,16 +206,27 @@ class GradientWidget(TickSlider):
self.gradRect.setRect(0, -self.rectSize, newLen, self.rectSize)
self.updateGradient()
def currentColorChanged(self, color):
if color.isValid() and self.currentTick is not None:
self.setTickColor(self.currentTick, color)
self.updateGradient()
def currentColorRejected(self):
self.setTickColor(self.currentTick, self.currentTickColor)
self.updateGradient()
def tickClicked(self, tick, ev):
if ev.button() == QtCore.Qt.LeftButton:
if not tick.colorChangeAllowed:
return
color = QtGui.QColorDialog.getColor(tick.color, None, "Select Color", QtGui.QColorDialog.ShowAlphaChannel)
if color.isValid():
self.setTickColor(tick, color)
self.updateGradient()
self.currentTick = tick
self.currentTickColor = tick.color
self.colorDialog.setCurrentColor(tick.color)
self.colorDialog.open()
#color = QtGui.QColorDialog.getColor(tick.color, self, "Select Color", QtGui.QColorDialog.ShowAlphaChannel)
#if color.isValid():
#self.setTickColor(tick, color)
#self.updateGradient()
elif ev.button() == QtCore.Qt.RightButton:
if not tick.removeAllowed:
return
......@@ -267,7 +284,8 @@ class GradientWidget(TickSlider):
r = c1.red() * (1.-f) + c2.red() * f
g = c1.green() * (1.-f) + c2.green() * f
b = c1.blue() * (1.-f) + c2.blue() * f
return QtGui.QColor(r, g, b)
a = c1.alpha() * (1.-f) + c2.alpha() * f
return QtGui.QColor(r, g, b,a)
elif self.colorMode == 'hsv':
h1,s1,v1,_ = c1.getHsv()
h2,s2,v2,_ = c2.getHsv()
......@@ -292,6 +310,43 @@ class GradientWidget(TickSlider):
t.removeAllowed = True
return t
def saveState(self):
ticks = []
for t in self.ticks:
c = t.color
ticks.append((self.ticks[t], (c.red(), c.green(), c.blue(), c.alpha())))
state = {'mode': self.colorMode, 'ticks': ticks}
return state
def restoreState(self, state):
self.setColorMode(state['mode'])
for t in self.ticks.keys():
self.removeTick(t)
for t in state['ticks']:
c = QtGui.QColor(*t[1])
self.addTick(t[0], c)
self.updateGradient()
class BlackWhiteSlider(GradientWidget):
def __init__(self, parent):
GradientWidget.__init__(self, parent)
self.getTick(0).colorChangeAllowed = False
self.getTick(1).colorChangeAllowed = False
self.allowAdd = False
self.setTickColor(self.getTick(1), QtGui.QColor(255,255,255))
self.setOrientation('right')
def getLevels(self):
return (self.tickValue(0), self.tickValue(1))
def setLevels(self, black, white):
self.setTickValue(0, black)
self.setTickValue(1, white)
class GammaWidget(TickSlider):
pass
......@@ -361,4 +416,31 @@ class Tick(QtGui.QGraphicsPolygonItem):
#self.view.tickChanged(self)
if __name__ == '__main__':
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
w.show()
w.resize(400,400)
cw = QtGui.QWidget()
w.setCentralWidget(cw)
l = QtGui.QGridLayout()
l.setSpacing(0)
cw.setLayout(l)
w1 = GradientWidget(orientation='top')
w2 = GradientWidget(orientation='right', allowAdd=False)
w2.setTickColor(1, QtGui.QColor(255,255,255))
w3 = GradientWidget(orientation='bottom')
w4 = TickSlider(orientation='left')
l.addWidget(w1, 0, 1)
l.addWidget(w2, 1, 2)
l.addWidget(w3, 2, 1)
l.addWidget(w4, 1, 0)
\ No newline at end of file
# -*- coding: utf-8 -*-
from GradientWidget import *
from PyQt4 import QtGui
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
w.show()
w.resize(400,400)
cw = QtGui.QWidget()
w.setCentralWidget(cw)
l = QtGui.QGridLayout()
l.setSpacing(0)
cw.setLayout(l)
w1 = GradientWidget(orientation='top')
w2 = GradientWidget(orientation='right', allowAdd=False)
w2.setTickColor(1, QtGui.QColor(255,255,255))
w3 = GradientWidget(orientation='bottom')
w4 = TickSlider(orientation='left')
l.addWidget(w1, 0, 1)
l.addWidget(w2, 1, 2)
l.addWidget(w3, 2, 1)
l.addWidget(w4, 1, 0)
......@@ -27,6 +27,8 @@ class GraphicsView(QtGui.QGraphicsView):
enabled via enableMouse()."""
QtGui.QGraphicsView.__init__(self, parent)
if 'linux' in sys.platform: ## linux has bugs in opengl implementation
useOpenGL = False
self.useOpenGL(useOpenGL)
palette = QtGui.QPalette()
......@@ -139,6 +141,7 @@ class GraphicsView(QtGui.QGraphicsView):
#print " translate:", st
self.setMatrix(m)
self.currentScale = scale
self.emit(QtCore.SIGNAL('viewChanged'), self.range)
if propagate:
for v in self.lockedViewports:
......@@ -190,7 +193,6 @@ class GraphicsView(QtGui.QGraphicsView):
#print "New Range:", self.range
self.centralWidget.setGeometry(self.range)
self.updateMatrix(propagate)
self.emit(QtCore.SIGNAL('viewChanged'), self.range)
def lockXRange(self, v1):
......
......@@ -18,8 +18,9 @@ from graphicsItems import *
from widgets import ROI
from PyQt4 import QtCore, QtGui
import sys
from numpy import ndarray
#from numpy import ndarray
import ptime
import numpy as np
from SignalProxy import proxyConnect
......@@ -52,7 +53,7 @@ class ImageView(QtGui.QWidget):
self.ui.graphicsView.invertY()
self.ui.graphicsView.enableMouse()
self. ticks = [t[0] for t in self.ui.gradientWidget.listTicks()]
self.ticks = [t[0] for t in self.ui.gradientWidget.listTicks()]
self.ticks[0].colorChangeAllowed = False
self.ticks[1].colorChangeAllowed = False
self.ui.gradientWidget.allowAdd = False
......@@ -301,14 +302,14 @@ class ImageView(QtGui.QWidget):
axes = (1, 2)
else:
return
data = self.roi.getArrayRegion(image.view(ndarray), self.imageItem, axes)
data = self.roi.getArrayRegion(image.view(np.ndarray), self.imageItem, axes)
if data is not None:
while data.ndim > 1:
data = data.mean(axis=1)
self.roiCurve.setData(y=data, x=self.tVals)
#self.ui.roiPlot.replot()
def setImage(self, img, autoRange=True, autoLevels=True, levels=None, axes=None, xvals=None):
def setImage(self, img, autoRange=True, autoLevels=True, levels=None, axes=None, xvals=None, pos=None, scale=None):
"""Set the image to be displayed in the widget.
Options are:
img: ndarray; the image to be displayed.
......@@ -319,16 +320,19 @@ class ImageView(QtGui.QWidget):
This is only needed to override the default guess.
"""
if not isinstance(img, ndarray):
if not isinstance(img, np.ndarray):
raise Exception("Image must be specified as ndarray.")
self.image = img
if xvals is not None:
self.tVals = xvals
elif hasattr(img, 'xvals'):
self.tVals = img.xvals(0)
try:
self.tVals = img.xvals(0)
except:
self.tVals = np.arange(img.shape[0])
else:
self.tVals = arange(img.shape[0])
self.tVals = np.arange(img.shape[0])
#self.ui.timeSlider.setValue(0)
#self.ui.normStartSlider.setValue(0)
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
......@@ -346,13 +350,13 @@ class ImageView(QtGui.QWidget):
self.imageDisp = None
if autoRange:
self.autoRange()
if autoLevels:
self.autoLevels()
if levels is not None:
self.levelMax = levels[1]
self.levelMin = levels[0]
self.currentIndex = 0
self.updateImage()
if self.ui.roiBtn.isChecked():
self.roiChanged()
......@@ -361,6 +365,7 @@ class ImageView(QtGui.QWidget):
if self.axes['t'] is not None:
#self.ui.roiPlot.show()
self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
self.timeLine.setValue(0)
#self.ui.roiPlot.setMouseEnabled(False, False)
if len(self.tVals) > 1:
start = self.tVals.min()
......@@ -376,6 +381,14 @@ class ImageView(QtGui.QWidget):
#else:
#self.ui.roiPlot.hide()
self.imageItem.resetTransform()
if scale is not None:
self.imageItem.scale(*scale)
if scale is not None:
self.imageItem.setPos(*pos)
if autoRange:
self.autoRange()
self.roiClicked()
......@@ -389,10 +402,12 @@ class ImageView(QtGui.QWidget):
self.ui.gradientWidget.setTickValue(self.ticks[1], 1.0)
self.imageItem.setLevels(white=self.whiteLevel(), black=self.blackLevel())
def autoRange(self):
image = self.getProcessedImage()
self.ui.graphicsView.setRange(QtCore.QRectF(0, 0, image.shape[self.axes['x']], image.shape[self.axes['y']]), padding=0., lockAspect=True)
#self.ui.graphicsView.setRange(QtCore.QRectF(0, 0, image.shape[self.axes['x']], image.shape[self.axes['y']]), padding=0., lockAspect=True)
self.ui.graphicsView.setRange(self.imageItem.sceneBoundingRect(), padding=0., lockAspect=True)
def getProcessedImage(self):
if self.imageDisp is None:
......@@ -408,7 +423,7 @@ class ImageView(QtGui.QWidget):
return image
div = self.ui.normDivideRadio.isChecked()
norm = image.view(ndarray).copy()
norm = image.view(np.ndarray).copy()
#if div:
#norm = ones(image.shape)
#else:
......@@ -498,7 +513,7 @@ class ImageView(QtGui.QWidget):
return (0,0)
totTime = xv[-1] + (xv[-1]-xv[-2])
#t = f * totTime
inds = argwhere(xv < t)
inds = np.argwhere(xv < t)
if len(inds) < 1:
return (0,t)
ind = inds[-1,0]
......
This diff is collapsed.
......@@ -23,6 +23,7 @@ from PyQt4 import QtGui, QtCore, QtSvg
#from ObjectWorkaround import *
#tryWorkaround(QtCore, QtGui)
import weakref
import numpy as np
try:
from WidgetGroup import *
......@@ -37,8 +38,6 @@ except:
HAVE_METAARRAY = False
class PlotItem(QtGui.QGraphicsWidget):
"""Plot graphics item that can be added to any graphics scene. Implements axis titles, scales, interactive viewbox."""
lastFileDir = None
......@@ -132,6 +131,7 @@ class PlotItem(QtGui.QGraphicsWidget):
self.items = []
self.curves = []
self.dataItems = []
self.paramList = {}
self.avgCurves = {}
......@@ -436,7 +436,7 @@ class PlotItem(QtGui.QGraphicsWidget):
self.vb.setMouseEnabled(*state)
def xRangeChanged(self, _, range):
if any(isnan(range)) or any(isinf(range)):
if any(np.isnan(range)) or any(np.isinf(range)):
raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
self.ctrl.xMinText.setText('%0.5g' % range[0])
self.ctrl.xMaxText.setText('%0.5g' % range[1])
......@@ -455,7 +455,7 @@ class PlotItem(QtGui.QGraphicsWidget):
self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
def yRangeChanged(self, _, range):
if any(isnan(range)) or any(isinf(range)):
if any(np.isnan(range)) or any(np.isinf(range)):
raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
self.ctrl.yMinText.setText('%0.5g' % range[0])
self.ctrl.yMaxText.setText('%0.5g' % range[1])
......@@ -545,6 +545,9 @@ class PlotItem(QtGui.QGraphicsWidget):
if not item in self.items:
return
self.items.remove(item)
if item in self.dataItems:
self.dataItems.remove(item)
if item.scene() is not None:
self.vb.removeItem(item)
if item in self.curves:
......@@ -571,12 +574,12 @@ class PlotItem(QtGui.QGraphicsWidget):
params = {}
if HAVE_METAARRAY and isinstance(data, MetaArray):
curve = self._plotMetaArray(data, x=x)
elif isinstance(data, ndarray):
elif isinstance(data, np.ndarray):
curve = self._plotArray(data, x=x)
elif isinstance(data, list):
if x is not None:
x = array(x)
curve = self._plotArray(array(data), x=x)
x = np.array(x)
curve = self._plotArray(np.array(data), x=x)
elif data is None:
curve = PlotCurveItem()
else:
......@@ -589,6 +592,10 @@ class PlotItem(QtGui.QGraphicsWidget):
return curve
def addDataItem(self, item):
self.addItem(item)
self.dataItems.append(item)
def addCurve(self, c, params=None):
if params is None:
params = {}
......@@ -622,7 +629,7 @@ class PlotItem(QtGui.QGraphicsWidget):
percentScale = [self.ctrl.xAutoPercentSpin.value(), self.ctrl.yAutoPercentSpin.value()][ax] * 0.01
mn = None
mx = None
for c in self.curves + [c[1] for c in self.avgCurves.values()]:
for c in self.curves + [c[1] for c in self.avgCurves.values()] + self.dataItems:
if not c.isVisible():
continue
cmn, cmx = c.getRange(ax, percentScale)
......@@ -630,7 +637,7 @@ class PlotItem(QtGui.QGraphicsWidget):
mn = cmn
if mx is None or cmx > mx:
mx = cmx
if mn is None or mx is None or any(isnan([mn, mx])) or any(isinf([mn, mx])):
if mn is None or mx is None or any(np.isnan([mn, mx])) or any(np.isinf([mn, mx])):
continue
if mn == mx:
mn -= 1
......@@ -1013,7 +1020,7 @@ class PlotItem(QtGui.QGraphicsWidget):
else:
xv = x
c = PlotCurveItem()
c.setData(x=xv, y=arr.view(ndarray))
c.setData(x=xv, y=arr.view(np.ndarray))
if autoLabel:
name = arr._info[0].get('name', None)
......
......@@ -20,7 +20,10 @@ class Point(QtCore.QPointF):
def __init__(self, *args):
if len(args) == 1:
if hasattr(args[0], '__getitem__'):
if isinstance(args[0], QtCore.QSizeF):
QtCore.QPointF.__init__(self, float(args[0].width()), float(args[0].height()))
return
elif hasattr(args[0], '__getitem__'):
QtCore.QPointF.__init__(self, float(args[0][0]), float(args[0][1]))
return
elif type(args[0]) in [float, int]:
......@@ -31,6 +34,9 @@ class Point(QtCore.QPointF):
return
QtCore.QPointF.__init__(self, *args)
def __reduce__(self):
return (Point, (self.x(), self.y()))
def __getitem__(self, i):
if i == 0:
return self.x()
......
......@@ -24,14 +24,17 @@ class SignalProxy(QtCore.QObject):
self.args = None
self.timers = 0
self.signal = signal
self.block = False
def setDelay(self, delay):
self.delay = delay
def flush(self):
"""If there is a signal queued up, send it now."""
if self.args is None:
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
......@@ -39,6 +42,8 @@ class SignalProxy(QtCore.QObject):
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
......@@ -46,7 +51,7 @@ class SignalProxy(QtCore.QObject):
def tryEmit(self):
"""Emit signal if it has been long enougn since receiving the last signal."""
if self.args is None:
if self.args is None or self.block:
return False
self.timers -= 1
t = time()
......@@ -59,4 +64,6 @@ class SignalProxy(QtCore.QObject):
return True
def disconnect(self):
self.block = True
\ No newline at end of file
......@@ -14,6 +14,7 @@ app = QtGui.QApplication([])
## Create window with GraphicsView widget
win = QtGui.QMainWindow()
view = GraphicsView()
#view.useOpenGL(True)
win.setCentralWidget(view)
win.show()
......
# -*- coding: utf-8 -*-
## Add path to library (just for examples; you do not need this)
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
app = QtGui.QApplication([])
## Create window with GraphicsView widget
win = QtGui.QMainWindow()
view = GraphicsView()
#view.useOpenGL(True)
win.setCentralWidget(view)
win.show()
## Allow mouse scale/pan
view.enableMouse()
## ..But lock the aspect ratio
view.setAspectLocked(True)
## Create image item
img = ImageItem(np.zeros((200,200)))
view.scene().addItem(img)
## Set initial view bounds
view.setRange(QtCore.QRectF(0, 0, 200, 200))
img.setDrawKernel(1)
img.setLevels(10,0)
#app.exec_()
# -*- coding: utf-8 -*-
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from PyQt4 import QtGui, QtCore
from pyqtgraph.PlotWidget import *
from pyqtgraph.graphicsItems import *
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
cw = 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)))
pos = np.random.normal(size=(2,3000))
spots = [{'pos': pos[:,i]} for i in range(3000)]
s1.addPoints(spots)
cw.addDataItem(s1)
This diff is collapsed.
......@@ -10,7 +10,8 @@ of array data from ImageItems.
"""
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
#from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
import numpy as np
from numpy.linalg import norm
import scipy.ndimage as ndimage
from Point import *
......@@ -50,7 +51,7 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
self.state = {'pos': pos, 'size': size, 'angle': angle}
self.lastState = None
self.setPos(pos)
self.rotate(-angle * 180. / pi)
self.rotate(-angle * 180. / np.pi)
self.setZValue(10)
self.handleSize = 5
......@@ -62,7 +63,15 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
self.rotateSnap = rotateSnap
self.scaleSnap = scaleSnap
self.setFlag(self.ItemIsSelectable, True)
def getState(self):
return self.state.copy()
def setState(self, state):
self.setPos(state['pos'], update=False)
self.setSize(state['size'], update=False)
self.setAngle(state['angle'])
def setZValue(self, z):
QtGui.QGraphicsItem.setZValue(self, z)
for h in self.handles:
......@@ -79,10 +88,12 @@ class ROI(QtGui.QGraphicsItem, QObjectWorkaround):
self.update()
def setPos(self, pos, update=True):
#print "setPos() called."
pos = Point(pos)
self.state['pos'] = pos
QtGui.QGraphicsItem.setPos