merged many changes from acq4
This commit is contained in:
parent
f029e7893e
commit
8828892e55
@ -10,6 +10,9 @@ import pyqtgraph as pg
|
|||||||
from pyqtgraph.Qt import QtCore, QtGui
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
# Enable antialiasing for prettier plots
|
||||||
|
pg.setConfigOptions(antialias=True)
|
||||||
|
|
||||||
w = pg.GraphicsWindow()
|
w = pg.GraphicsWindow()
|
||||||
w.setWindowTitle('pyqtgraph example: GraphItem')
|
w.setWindowTitle('pyqtgraph example: GraphItem')
|
||||||
v = w.addViewBox()
|
v = w.addViewBox()
|
||||||
|
@ -21,7 +21,8 @@ win = pg.GraphicsWindow(title="Basic plotting examples")
|
|||||||
win.resize(1000,600)
|
win.resize(1000,600)
|
||||||
win.setWindowTitle('pyqtgraph example: Plotting')
|
win.setWindowTitle('pyqtgraph example: Plotting')
|
||||||
|
|
||||||
|
# Enable antialiasing for prettier plots
|
||||||
|
pg.setConfigOptions(antialias=True)
|
||||||
|
|
||||||
p1 = win.addPlot(title="Basic array plotting", y=np.random.normal(size=100))
|
p1 = win.addPlot(title="Basic array plotting", y=np.random.normal(size=100))
|
||||||
|
|
||||||
|
31
examples/ScaleBar.py
Normal file
31
examples/ScaleBar.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Demonstrates ScaleBar
|
||||||
|
"""
|
||||||
|
import initExample ## Add path to library (just for examples; you do not need this)
|
||||||
|
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
pg.mkQApp()
|
||||||
|
win = pg.GraphicsWindow()
|
||||||
|
win.setWindowTitle('pyqtgraph example: ScaleBar')
|
||||||
|
|
||||||
|
vb = win.addViewBox()
|
||||||
|
vb.setAspectLocked()
|
||||||
|
|
||||||
|
img = pg.ImageItem()
|
||||||
|
img.setImage(np.random.normal(size=(100,100)))
|
||||||
|
img.scale(0.01, 0.01)
|
||||||
|
vb.addItem(img)
|
||||||
|
|
||||||
|
scale = pg.ScaleBar(size=0.1)
|
||||||
|
scale.setParentItem(vb)
|
||||||
|
scale.anchor((1, 1), (1, 1), offset=(-20, -20))
|
||||||
|
|
||||||
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||||
|
QtGui.QApplication.instance().exec_()
|
@ -16,8 +16,22 @@ data = np.array([
|
|||||||
(3, 2, 5, 2, 'z'),
|
(3, 2, 5, 2, 'z'),
|
||||||
(4, 4, 6, 9, 'z'),
|
(4, 4, 6, 9, 'z'),
|
||||||
(5, 3, 6, 7, 'x'),
|
(5, 3, 6, 7, 'x'),
|
||||||
(6, 5, 2, 6, 'y'),
|
(6, 5, 4, 6, 'x'),
|
||||||
(7, 5, 7, 2, 'z'),
|
(7, 5, 8, 2, 'z'),
|
||||||
|
(8, 1, 2, 4, 'x'),
|
||||||
|
(9, 2, 3, 7, 'z'),
|
||||||
|
(0, 6, 0, 2, 'z'),
|
||||||
|
(1, 3, 1, 2, 'z'),
|
||||||
|
(2, 5, 4, 6, 'y'),
|
||||||
|
(3, 4, 8, 1, 'y'),
|
||||||
|
(4, 7, 6, 8, 'z'),
|
||||||
|
(5, 8, 7, 4, 'y'),
|
||||||
|
(6, 1, 2, 3, 'y'),
|
||||||
|
(7, 5, 3, 9, 'z'),
|
||||||
|
(8, 9, 3, 1, 'x'),
|
||||||
|
(9, 2, 6, 2, 'z'),
|
||||||
|
(0, 3, 4, 6, 'x'),
|
||||||
|
(1, 5, 9, 3, 'y'),
|
||||||
], dtype=[('col1', float), ('col2', float), ('col3', int), ('col4', int), ('col5', 'S10')])
|
], dtype=[('col1', float), ('col2', float), ('col3', int), ('col4', int), ('col5', 'S10')])
|
||||||
|
|
||||||
spw.setFields([
|
spw.setFields([
|
||||||
|
@ -3,9 +3,12 @@ import initExample ## Add path to library (just for examples; you do not need th
|
|||||||
from pyqtgraph.Qt import QtGui, QtCore
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
pg.plot(np.random.normal(size=100000), title="Simplest possible plotting example")
|
plt = pg.plot(np.random.normal(size=100), title="Simplest possible plotting example")
|
||||||
|
plt.getAxis('bottom').setTicks([[(x*20, str(x*20)) for x in range(6)]])
|
||||||
## Start Qt event loop unless running in interactive mode or using pyside.
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||||
|
ex = pg.exporters.SVGExporter.SVGExporter(plt.plotItem.scene())
|
||||||
|
ex.export('/home/luke/tmp/test.svg')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
|
if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||||
|
38
examples/beeswarm.py
Normal file
38
examples/beeswarm.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Example beeswarm / bar chart
|
||||||
|
"""
|
||||||
|
import initExample ## Add path to library (just for examples; you do not need this)
|
||||||
|
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
win = pg.plot()
|
||||||
|
win.setWindowTitle('pyqtgraph example: beeswarm')
|
||||||
|
|
||||||
|
data = np.random.normal(size=(4,20))
|
||||||
|
data[0] += 5
|
||||||
|
data[1] += 7
|
||||||
|
data[2] += 5
|
||||||
|
data[3] = 10 + data[3] * 2
|
||||||
|
|
||||||
|
## Make bar graph
|
||||||
|
#bar = pg.BarGraphItem(x=range(4), height=data.mean(axis=1), width=0.5, brush=0.4)
|
||||||
|
#win.addItem(bar)
|
||||||
|
|
||||||
|
## add scatter plots on top
|
||||||
|
for i in range(4):
|
||||||
|
xvals = pg.pseudoScatter(data[i], spacing=0.4, bidir=True) * 0.2
|
||||||
|
win.plot(x=xvals+i, y=data[i], pen=None, symbol='o', symbolBrush=pg.intColor(i,6,maxValue=128))
|
||||||
|
|
||||||
|
## Make error bars
|
||||||
|
err = pg.ErrorBarItem(x=np.arange(4), y=data.mean(axis=1), height=data.std(axis=1), beam=0.5, pen={'color':'w', 'width':2})
|
||||||
|
win.addItem(err)
|
||||||
|
|
||||||
|
|
||||||
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||||
|
QtGui.QApplication.instance().exec_()
|
@ -304,7 +304,36 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
|
|
||||||
def correctCoordinates(node, item):
|
def correctCoordinates(node, item):
|
||||||
## Remove transformation matrices from <g> tags by applying matrix to coordinates inside.
|
## Remove transformation matrices from <g> tags by applying matrix to coordinates inside.
|
||||||
|
## Each item is represented by a single top-level group with one or more groups inside.
|
||||||
|
## Each inner group contains one or more drawing primitives, possibly of different types.
|
||||||
groups = node.getElementsByTagName('g')
|
groups = node.getElementsByTagName('g')
|
||||||
|
|
||||||
|
## Since we leave text unchanged, groups which combine text and non-text primitives must be split apart.
|
||||||
|
## (if at some point we start correcting text transforms as well, then it should be safe to remove this)
|
||||||
|
groups2 = []
|
||||||
|
for grp in groups:
|
||||||
|
subGroups = [grp.cloneNode(deep=False)]
|
||||||
|
textGroup = None
|
||||||
|
for ch in grp.childNodes[:]:
|
||||||
|
if isinstance(ch, xml.Element):
|
||||||
|
if textGroup is None:
|
||||||
|
textGroup = ch.tagName == 'text'
|
||||||
|
if ch.tagName == 'text':
|
||||||
|
if textGroup is False:
|
||||||
|
subGroups.append(grp.cloneNode(deep=False))
|
||||||
|
textGroup = True
|
||||||
|
else:
|
||||||
|
if textGroup is True:
|
||||||
|
subGroups.append(grp.cloneNode(deep=False))
|
||||||
|
textGroup = False
|
||||||
|
subGroups[-1].appendChild(ch)
|
||||||
|
groups2.extend(subGroups)
|
||||||
|
for sg in subGroups:
|
||||||
|
node.insertBefore(sg, grp)
|
||||||
|
node.removeChild(grp)
|
||||||
|
groups = groups2
|
||||||
|
|
||||||
|
|
||||||
for grp in groups:
|
for grp in groups:
|
||||||
matrix = grp.getAttribute('transform')
|
matrix = grp.getAttribute('transform')
|
||||||
match = re.match(r'matrix\((.*)\)', matrix)
|
match = re.match(r'matrix\((.*)\)', matrix)
|
||||||
@ -374,7 +403,6 @@ def correctCoordinates(node, item):
|
|||||||
|
|
||||||
if removeTransform:
|
if removeTransform:
|
||||||
grp.removeAttribute('transform')
|
grp.removeAttribute('transform')
|
||||||
|
|
||||||
|
|
||||||
def itemTransform(item, root):
|
def itemTransform(item, root):
|
||||||
## Return the transformation mapping item to root
|
## Return the transformation mapping item to root
|
||||||
|
@ -1930,9 +1930,9 @@ def invertQTransform(tr):
|
|||||||
return QtGui.QTransform(inv[0,0], inv[0,1], inv[0,2], inv[1,0], inv[1,1], inv[1,2], inv[2,0], inv[2,1])
|
return QtGui.QTransform(inv[0,0], inv[0,1], inv[0,2], inv[1,0], inv[1,1], inv[1,2], inv[2,0], inv[2,1])
|
||||||
|
|
||||||
|
|
||||||
def pseudoScatter(data, spacing=None, shuffle=True):
|
def pseudoScatter(data, spacing=None, shuffle=True, bidir=False):
|
||||||
"""
|
"""
|
||||||
Used for examining the distribution of values in a set.
|
Used for examining the distribution of values in a set. Produces scattering as in beeswarm or column scatter plots.
|
||||||
|
|
||||||
Given a list of x-values, construct a set of y-values such that an x,y scatter-plot
|
Given a list of x-values, construct a set of y-values such that an x,y scatter-plot
|
||||||
will not have overlapping points (it will look similar to a histogram).
|
will not have overlapping points (it will look similar to a histogram).
|
||||||
@ -1959,23 +1959,41 @@ def pseudoScatter(data, spacing=None, shuffle=True):
|
|||||||
xmask = dx < s2 # exclude anything too far away
|
xmask = dx < s2 # exclude anything too far away
|
||||||
|
|
||||||
if xmask.sum() > 0:
|
if xmask.sum() > 0:
|
||||||
dx = dx[xmask]
|
if bidir:
|
||||||
dy = (s2 - dx)**0.5
|
dirs = [-1, 1]
|
||||||
limits = np.empty((2,len(dy))) # ranges of y-values to exclude
|
else:
|
||||||
limits[0] = y0[xmask] - dy
|
dirs = [1]
|
||||||
limits[1] = y0[xmask] + dy
|
yopts = []
|
||||||
|
for direction in dirs:
|
||||||
while True:
|
y = 0
|
||||||
# ignore anything below this y-value
|
dx2 = dx[xmask]
|
||||||
mask = limits[1] >= y
|
dy = (s2 - dx2)**0.5
|
||||||
limits = limits[:,mask]
|
limits = np.empty((2,len(dy))) # ranges of y-values to exclude
|
||||||
|
limits[0] = y0[xmask] - dy
|
||||||
# are we inside an excluded region?
|
limits[1] = y0[xmask] + dy
|
||||||
mask = (limits[0] < y) & (limits[1] > y)
|
while True:
|
||||||
if mask.sum() == 0:
|
# ignore anything below this y-value
|
||||||
break
|
if direction > 0:
|
||||||
y = limits[:,mask].max()
|
mask = limits[1] >= y
|
||||||
|
else:
|
||||||
|
mask = limits[0] <= y
|
||||||
|
|
||||||
|
limits2 = limits[:,mask]
|
||||||
|
|
||||||
|
# are we inside an excluded region?
|
||||||
|
mask = (limits2[0] < y) & (limits2[1] > y)
|
||||||
|
if mask.sum() == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
if direction > 0:
|
||||||
|
y = limits2[:,mask].max()
|
||||||
|
else:
|
||||||
|
y = limits2[:,mask].min()
|
||||||
|
yopts.append(y)
|
||||||
|
if bidir:
|
||||||
|
y = yopts[0] if -yopts[0] < yopts[1] else yopts[1]
|
||||||
|
else:
|
||||||
|
y = yopts[0]
|
||||||
yvals[i] = y
|
yvals[i] = y
|
||||||
|
|
||||||
return yvals[np.argsort(inds)] ## un-shuffle values before returning
|
return yvals[np.argsort(inds)] ## un-shuffle values before returning
|
||||||
|
149
pyqtgraph/graphicsItems/BarGraphItem.py
Normal file
149
pyqtgraph/graphicsItems/BarGraphItem.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
|
from .GraphicsObject import GraphicsObject
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
__all__ = ['BarGraphItem']
|
||||||
|
|
||||||
|
class BarGraphItem(GraphicsObject):
|
||||||
|
def __init__(self, **opts):
|
||||||
|
"""
|
||||||
|
Valid keyword options are:
|
||||||
|
x, x0, x1, y, y0, y1, width, height, pen, brush
|
||||||
|
|
||||||
|
x specifies the x-position of the center of the bar.
|
||||||
|
x0, x1 specify left and right edges of the bar, respectively.
|
||||||
|
width specifies distance from x0 to x1.
|
||||||
|
You may specify any combination:
|
||||||
|
|
||||||
|
x, width
|
||||||
|
x0, width
|
||||||
|
x1, width
|
||||||
|
x0, x1
|
||||||
|
|
||||||
|
Likewise y, y0, y1, and height.
|
||||||
|
If only height is specified, then y0 will be set to 0
|
||||||
|
|
||||||
|
Example uses:
|
||||||
|
|
||||||
|
BarGraphItem(x=range(5), height=[1,5,2,4,3], width=0.5)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
GraphicsObject.__init__(self)
|
||||||
|
self.opts = dict(
|
||||||
|
x=None,
|
||||||
|
y=None,
|
||||||
|
x0=None,
|
||||||
|
y0=None,
|
||||||
|
x1=None,
|
||||||
|
y1=None,
|
||||||
|
height=None,
|
||||||
|
width=None,
|
||||||
|
pen=None,
|
||||||
|
brush=None,
|
||||||
|
pens=None,
|
||||||
|
brushes=None,
|
||||||
|
)
|
||||||
|
self.setOpts(**opts)
|
||||||
|
|
||||||
|
def setOpts(self, **opts):
|
||||||
|
self.opts.update(opts)
|
||||||
|
self.picture = None
|
||||||
|
self.update()
|
||||||
|
self.informViewBoundsChanged()
|
||||||
|
|
||||||
|
def drawPicture(self):
|
||||||
|
self.picture = QtGui.QPicture()
|
||||||
|
p = QtGui.QPainter(self.picture)
|
||||||
|
|
||||||
|
pen = self.opts['pen']
|
||||||
|
pens = self.opts['pens']
|
||||||
|
|
||||||
|
if pen is None and pens is None:
|
||||||
|
pen = pg.getConfigOption('foreground')
|
||||||
|
|
||||||
|
brush = self.opts['brush']
|
||||||
|
brushes = self.opts['brushes']
|
||||||
|
if brush is None and brushes is None:
|
||||||
|
brush = (128, 128, 128)
|
||||||
|
|
||||||
|
def asarray(x):
|
||||||
|
if x is None or np.isscalar(x) or isinstance(x, np.ndarray):
|
||||||
|
return x
|
||||||
|
return np.array(x)
|
||||||
|
|
||||||
|
|
||||||
|
x = asarray(self.opts.get('x'))
|
||||||
|
x0 = asarray(self.opts.get('x0'))
|
||||||
|
x1 = asarray(self.opts.get('x1'))
|
||||||
|
width = asarray(self.opts.get('width'))
|
||||||
|
|
||||||
|
if x0 is None:
|
||||||
|
if width is None:
|
||||||
|
raise Exception('must specify either x0 or width')
|
||||||
|
if x1 is not None:
|
||||||
|
x0 = x1 - width
|
||||||
|
elif x is not None:
|
||||||
|
x0 = x - width/2.
|
||||||
|
else:
|
||||||
|
raise Exception('must specify at least one of x, x0, or x1')
|
||||||
|
if width is None:
|
||||||
|
if x1 is None:
|
||||||
|
raise Exception('must specify either x1 or width')
|
||||||
|
width = x1 - x0
|
||||||
|
|
||||||
|
y = asarray(self.opts.get('y'))
|
||||||
|
y0 = asarray(self.opts.get('y0'))
|
||||||
|
y1 = asarray(self.opts.get('y1'))
|
||||||
|
height = asarray(self.opts.get('height'))
|
||||||
|
|
||||||
|
if y0 is None:
|
||||||
|
if height is None:
|
||||||
|
y0 = 0
|
||||||
|
elif y1 is not None:
|
||||||
|
y0 = y1 - height
|
||||||
|
elif y is not None:
|
||||||
|
y0 = y - height/2.
|
||||||
|
else:
|
||||||
|
y0 = 0
|
||||||
|
if height is None:
|
||||||
|
if y1 is None:
|
||||||
|
raise Exception('must specify either y1 or height')
|
||||||
|
height = y1 - y0
|
||||||
|
|
||||||
|
p.setPen(pg.mkPen(pen))
|
||||||
|
p.setBrush(pg.mkBrush(brush))
|
||||||
|
for i in range(len(x0)):
|
||||||
|
if pens is not None:
|
||||||
|
p.setPen(pg.mkPen(pens[i]))
|
||||||
|
if brushes is not None:
|
||||||
|
p.setBrush(pg.mkBrush(brushes[i]))
|
||||||
|
|
||||||
|
if np.isscalar(y0):
|
||||||
|
y = y0
|
||||||
|
else:
|
||||||
|
y = y0[i]
|
||||||
|
if np.isscalar(width):
|
||||||
|
w = width
|
||||||
|
else:
|
||||||
|
w = width[i]
|
||||||
|
|
||||||
|
p.drawRect(QtCore.QRectF(x0[i], y, w, height[i]))
|
||||||
|
|
||||||
|
|
||||||
|
p.end()
|
||||||
|
self.prepareGeometryChange()
|
||||||
|
|
||||||
|
|
||||||
|
def paint(self, p, *args):
|
||||||
|
if self.picture is None:
|
||||||
|
self.drawPicture()
|
||||||
|
self.picture.play(p)
|
||||||
|
|
||||||
|
def boundingRect(self):
|
||||||
|
if self.picture is None:
|
||||||
|
self.drawPicture()
|
||||||
|
return QtCore.QRectF(self.picture.boundingRect())
|
||||||
|
|
||||||
|
|
@ -103,6 +103,8 @@ class GraphItem(GraphicsObject):
|
|||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
if self.picture == None:
|
if self.picture == None:
|
||||||
self.generatePicture()
|
self.generatePicture()
|
||||||
|
if pg.getConfigOption('antialias') is True:
|
||||||
|
p.setRenderHint(p.Antialiasing)
|
||||||
self.picture.play(p)
|
self.picture.play(p)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
|
@ -446,6 +446,14 @@ class GraphicsItem(object):
|
|||||||
#print " --> ", ch2.scene()
|
#print " --> ", ch2.scene()
|
||||||
#self.setChildScene(ch2)
|
#self.setChildScene(ch2)
|
||||||
|
|
||||||
|
def parentChanged(self):
|
||||||
|
"""Called when the item's parent has changed.
|
||||||
|
This method handles connecting / disconnecting from ViewBox signals
|
||||||
|
to make sure viewRangeChanged works properly. It should generally be
|
||||||
|
extended, not overridden."""
|
||||||
|
self._updateView()
|
||||||
|
|
||||||
|
|
||||||
def _updateView(self):
|
def _updateView(self):
|
||||||
## called to see whether this item has a new view to connect to
|
## called to see whether this item has a new view to connect to
|
||||||
## NOTE: This is called from GraphicsObject.itemChange or GraphicsWidget.itemChange.
|
## NOTE: This is called from GraphicsObject.itemChange or GraphicsWidget.itemChange.
|
||||||
@ -496,6 +504,12 @@ class GraphicsItem(object):
|
|||||||
## inform children that their view might have changed
|
## inform children that their view might have changed
|
||||||
self._replaceView(oldView)
|
self._replaceView(oldView)
|
||||||
|
|
||||||
|
self.viewChanged(view, oldView)
|
||||||
|
|
||||||
|
def viewChanged(self, view, oldView):
|
||||||
|
"""Called when this item's view has changed
|
||||||
|
(ie, the item has been added to or removed from a ViewBox)"""
|
||||||
|
pass
|
||||||
|
|
||||||
def _replaceView(self, oldView, item=None):
|
def _replaceView(self, oldView, item=None):
|
||||||
if item is None:
|
if item is None:
|
||||||
|
@ -19,7 +19,7 @@ class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject):
|
|||||||
def itemChange(self, change, value):
|
def itemChange(self, change, value):
|
||||||
ret = QtGui.QGraphicsObject.itemChange(self, change, value)
|
ret = QtGui.QGraphicsObject.itemChange(self, change, value)
|
||||||
if change in [self.ItemParentHasChanged, self.ItemSceneHasChanged]:
|
if change in [self.ItemParentHasChanged, self.ItemSceneHasChanged]:
|
||||||
self._updateView()
|
self.parentChanged()
|
||||||
if change in [self.ItemPositionHasChanged, self.ItemTransformHasChanged]:
|
if change in [self.ItemPositionHasChanged, self.ItemTransformHasChanged]:
|
||||||
self.informViewBoundsChanged()
|
self.informViewBoundsChanged()
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ from ..Point import Point
|
|||||||
class GraphicsWidgetAnchor(object):
|
class GraphicsWidgetAnchor(object):
|
||||||
"""
|
"""
|
||||||
Class used to allow GraphicsWidgets to anchor to a specific position on their
|
Class used to allow GraphicsWidgets to anchor to a specific position on their
|
||||||
parent.
|
parent. The item will be automatically repositioned if the parent is resized.
|
||||||
|
This is used, for example, to anchor a LegendItem to a corner of its parent
|
||||||
|
PlotItem.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@ from pyqtgraph.Qt import QtGui, QtCore
|
|||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from .GraphicsWidget import GraphicsWidget
|
from .GraphicsWidget import GraphicsWidget
|
||||||
|
from .GraphicsWidgetAnchor import GraphicsWidgetAnchor
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['LabelItem']
|
__all__ = ['LabelItem']
|
||||||
|
|
||||||
class LabelItem(GraphicsWidget):
|
class LabelItem(GraphicsWidget, GraphicsWidgetAnchor):
|
||||||
"""
|
"""
|
||||||
GraphicsWidget displaying text.
|
GraphicsWidget displaying text.
|
||||||
Used mainly as axis labels, titles, etc.
|
Used mainly as axis labels, titles, etc.
|
||||||
@ -17,6 +18,7 @@ class LabelItem(GraphicsWidget):
|
|||||||
|
|
||||||
def __init__(self, text=' ', parent=None, angle=0, **args):
|
def __init__(self, text=' ', parent=None, angle=0, **args):
|
||||||
GraphicsWidget.__init__(self, parent)
|
GraphicsWidget.__init__(self, parent)
|
||||||
|
GraphicsWidgetAnchor.__init__(self)
|
||||||
self.item = QtGui.QGraphicsTextItem(self)
|
self.item = QtGui.QGraphicsTextItem(self)
|
||||||
self.opts = {
|
self.opts = {
|
||||||
'color': None,
|
'color': None,
|
||||||
|
@ -402,7 +402,6 @@ class PlotCurveItem(GraphicsObject):
|
|||||||
aa = self.opts['antialias']
|
aa = self.opts['antialias']
|
||||||
|
|
||||||
p.setRenderHint(p.Antialiasing, aa)
|
p.setRenderHint(p.Antialiasing, aa)
|
||||||
|
|
||||||
|
|
||||||
if self.opts['brush'] is not None and self.opts['fillLevel'] is not None:
|
if self.opts['brush'] is not None and self.opts['fillLevel'] is not None:
|
||||||
if self.fillPath is None:
|
if self.fillPath is None:
|
||||||
|
@ -1,50 +1,104 @@
|
|||||||
from pyqtgraph.Qt import QtGui, QtCore
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
from .UIGraphicsItem import *
|
from .GraphicsObject import *
|
||||||
|
from .GraphicsWidgetAnchor import *
|
||||||
|
from .TextItem import TextItem
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
|
import pyqtgraph as pg
|
||||||
|
|
||||||
__all__ = ['ScaleBar']
|
__all__ = ['ScaleBar']
|
||||||
class ScaleBar(UIGraphicsItem):
|
|
||||||
|
class ScaleBar(GraphicsObject, GraphicsWidgetAnchor):
|
||||||
"""
|
"""
|
||||||
Displays a rectangular bar with 10 divisions to indicate the relative scale of objects on the view.
|
Displays a rectangular bar to indicate the relative scale of objects on the view.
|
||||||
"""
|
"""
|
||||||
def __init__(self, size, width=5, color=(100, 100, 255)):
|
def __init__(self, size, width=5, brush=None, pen=None, suffix='m'):
|
||||||
UIGraphicsItem.__init__(self)
|
GraphicsObject.__init__(self)
|
||||||
|
GraphicsWidgetAnchor.__init__(self)
|
||||||
|
self.setFlag(self.ItemHasNoContents)
|
||||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||||
|
|
||||||
self.brush = fn.mkBrush(color)
|
if brush is None:
|
||||||
self.pen = fn.mkPen((0,0,0))
|
brush = pg.getConfigOption('foreground')
|
||||||
|
self.brush = fn.mkBrush(brush)
|
||||||
|
self.pen = fn.mkPen(pen)
|
||||||
self._width = width
|
self._width = width
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
def paint(self, p, opt, widget):
|
self.bar = QtGui.QGraphicsRectItem()
|
||||||
UIGraphicsItem.paint(self, p, opt, widget)
|
self.bar.setPen(self.pen)
|
||||||
|
self.bar.setBrush(self.brush)
|
||||||
|
self.bar.setParentItem(self)
|
||||||
|
|
||||||
rect = self.boundingRect()
|
self.text = TextItem(text=fn.siFormat(size, suffix=suffix), anchor=(0.5,1))
|
||||||
unit = self.pixelSize()
|
self.text.setParentItem(self)
|
||||||
y = rect.top() + (rect.bottom()-rect.top()) * 0.02
|
|
||||||
y1 = y + unit[1]*self._width
|
def parentChanged(self):
|
||||||
x = rect.right() + (rect.left()-rect.right()) * 0.02
|
view = self.parentItem()
|
||||||
x1 = x - self.size
|
if view is None:
|
||||||
|
return
|
||||||
|
view.sigRangeChanged.connect(self.updateBar)
|
||||||
|
self.updateBar()
|
||||||
|
|
||||||
p.setPen(self.pen)
|
|
||||||
p.setBrush(self.brush)
|
|
||||||
rect = QtCore.QRectF(
|
|
||||||
QtCore.QPointF(x1, y1),
|
|
||||||
QtCore.QPointF(x, y)
|
|
||||||
)
|
|
||||||
p.translate(x1, y1)
|
|
||||||
p.scale(rect.width(), rect.height())
|
|
||||||
p.drawRect(0, 0, 1, 1)
|
|
||||||
|
|
||||||
alpha = np.clip(((self.size/unit[0]) - 40.) * 255. / 80., 0, 255)
|
def updateBar(self):
|
||||||
p.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, alpha)))
|
view = self.parentItem()
|
||||||
for i in range(1, 10):
|
if view is None:
|
||||||
#x2 = x + (x1-x) * 0.1 * i
|
return
|
||||||
x2 = 0.1 * i
|
p1 = view.mapFromViewToItem(self, QtCore.QPointF(0,0))
|
||||||
p.drawLine(QtCore.QPointF(x2, 0), QtCore.QPointF(x2, 1))
|
p2 = view.mapFromViewToItem(self, QtCore.QPointF(self.size,0))
|
||||||
|
w = (p2-p1).x()
|
||||||
|
self.bar.setRect(QtCore.QRectF(-w, 0, w, self._width))
|
||||||
|
self.text.setPos(-w/2., 0)
|
||||||
|
|
||||||
|
def boundingRect(self):
|
||||||
|
return QtCore.QRectF()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#class ScaleBar(UIGraphicsItem):
|
||||||
|
#"""
|
||||||
|
#Displays a rectangular bar with 10 divisions to indicate the relative scale of objects on the view.
|
||||||
|
#"""
|
||||||
|
#def __init__(self, size, width=5, color=(100, 100, 255)):
|
||||||
|
#UIGraphicsItem.__init__(self)
|
||||||
|
#self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||||
|
|
||||||
|
#self.brush = fn.mkBrush(color)
|
||||||
|
#self.pen = fn.mkPen((0,0,0))
|
||||||
|
#self._width = width
|
||||||
|
#self.size = size
|
||||||
|
|
||||||
|
#def paint(self, p, opt, widget):
|
||||||
|
#UIGraphicsItem.paint(self, p, opt, widget)
|
||||||
|
|
||||||
|
#rect = self.boundingRect()
|
||||||
|
#unit = self.pixelSize()
|
||||||
|
#y = rect.top() + (rect.bottom()-rect.top()) * 0.02
|
||||||
|
#y1 = y + unit[1]*self._width
|
||||||
|
#x = rect.right() + (rect.left()-rect.right()) * 0.02
|
||||||
|
#x1 = x - self.size
|
||||||
|
|
||||||
|
#p.setPen(self.pen)
|
||||||
|
#p.setBrush(self.brush)
|
||||||
|
#rect = QtCore.QRectF(
|
||||||
|
#QtCore.QPointF(x1, y1),
|
||||||
|
#QtCore.QPointF(x, y)
|
||||||
|
#)
|
||||||
|
#p.translate(x1, y1)
|
||||||
|
#p.scale(rect.width(), rect.height())
|
||||||
|
#p.drawRect(0, 0, 1, 1)
|
||||||
|
|
||||||
|
#alpha = np.clip(((self.size/unit[0]) - 40.) * 255. / 80., 0, 255)
|
||||||
|
#p.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, alpha)))
|
||||||
|
#for i in range(1, 10):
|
||||||
|
##x2 = x + (x1-x) * 0.1 * i
|
||||||
|
#x2 = 0.1 * i
|
||||||
|
#p.drawLine(QtCore.QPointF(x2, 0), QtCore.QPointF(x2, 1))
|
||||||
|
|
||||||
|
|
||||||
def setSize(self, s):
|
#def setSize(self, s):
|
||||||
self.size = s
|
#self.size = s
|
||||||
|
|
||||||
|
@ -740,6 +740,7 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
drawSymbol(p2, *self.getSpotOpts(rec, scale))
|
drawSymbol(p2, *self.getSpotOpts(rec, scale))
|
||||||
p2.end()
|
p2.end()
|
||||||
|
|
||||||
|
p.setRenderHint(p.Antialiasing, aa)
|
||||||
self.picture.play(p)
|
self.picture.play(p)
|
||||||
|
|
||||||
def points(self):
|
def points(self):
|
||||||
|
@ -524,12 +524,13 @@ class ViewBox(GraphicsWidget):
|
|||||||
if t is not None:
|
if t is not None:
|
||||||
t = Point(t)
|
t = Point(t)
|
||||||
self.setRange(vr.translated(t), padding=0)
|
self.setRange(vr.translated(t), padding=0)
|
||||||
elif x is not None:
|
else:
|
||||||
x1, x2 = vr.left()+x, vr.right()+x
|
if x is not None:
|
||||||
self.setXRange(x1, x2, padding=0)
|
x1, x2 = vr.left()+x, vr.right()+x
|
||||||
elif y is not None:
|
self.setXRange(x1, x2, padding=0)
|
||||||
y1, y2 = vr.top()+y, vr.bottom()+y
|
if y is not None:
|
||||||
self.setYRange(y1, y2, padding=0)
|
y1, y2 = vr.top()+y, vr.bottom()+y
|
||||||
|
self.setYRange(y1, y2, padding=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1090,10 +1091,10 @@ class ViewBox(GraphicsWidget):
|
|||||||
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
|
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
|
||||||
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
|
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
|
||||||
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
|
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
|
||||||
if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any():
|
if xr is None or xr == (None, None) or np.isnan(xr).any() or np.isinf(xr).any():
|
||||||
useX = False
|
useX = False
|
||||||
xr = (0,0)
|
xr = (0,0)
|
||||||
if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any():
|
if yr is None or yr == (None, None) or np.isnan(yr).any() or np.isinf(yr).any():
|
||||||
useY = False
|
useY = False
|
||||||
yr = (0,0)
|
yr = (0,0)
|
||||||
|
|
||||||
|
@ -72,7 +72,8 @@ class ColorMapParameter(ptree.types.GroupParameter):
|
|||||||
(see *values* option).
|
(see *values* option).
|
||||||
units String indicating the units of the data for this field.
|
units String indicating the units of the data for this field.
|
||||||
values List of unique values for which the user may assign a
|
values List of unique values for which the user may assign a
|
||||||
color when mode=='enum'.
|
color when mode=='enum'. Optionally may specify a dict
|
||||||
|
instead {value: name}.
|
||||||
============== ============================================================
|
============== ============================================================
|
||||||
"""
|
"""
|
||||||
self.fields = OrderedDict(fields)
|
self.fields = OrderedDict(fields)
|
||||||
@ -168,12 +169,14 @@ class EnumColorMapItem(ptree.types.GroupParameter):
|
|||||||
def __init__(self, name, opts):
|
def __init__(self, name, opts):
|
||||||
self.fieldName = name
|
self.fieldName = name
|
||||||
vals = opts.get('values', [])
|
vals = opts.get('values', [])
|
||||||
|
if isinstance(vals, list):
|
||||||
|
vals = OrderedDict([(v,str(v)) for v in vals])
|
||||||
childs = [{'name': v, 'type': 'color'} for v in vals]
|
childs = [{'name': v, 'type': 'color'} for v in vals]
|
||||||
|
|
||||||
childs = []
|
childs = []
|
||||||
for v in vals:
|
for val,vname in vals.items():
|
||||||
ch = ptree.Parameter.create(name=str(v), type='color')
|
ch = ptree.Parameter.create(name=vname, type='color')
|
||||||
ch.maskValue = v
|
ch.maskValue = val
|
||||||
childs.append(ch)
|
childs.append(ch)
|
||||||
|
|
||||||
ptree.types.GroupParameter.__init__(self,
|
ptree.types.GroupParameter.__init__(self,
|
||||||
|
@ -2,6 +2,7 @@ from pyqtgraph.Qt import QtGui, QtCore
|
|||||||
import pyqtgraph.parametertree as ptree
|
import pyqtgraph.parametertree as ptree
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pyqtgraph.pgcollections import OrderedDict
|
from pyqtgraph.pgcollections import OrderedDict
|
||||||
|
import pyqtgraph as pg
|
||||||
|
|
||||||
__all__ = ['DataFilterWidget']
|
__all__ = ['DataFilterWidget']
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ class DataFilterWidget(ptree.ParameterTree):
|
|||||||
|
|
||||||
self.setFields = self.params.setFields
|
self.setFields = self.params.setFields
|
||||||
self.filterData = self.params.filterData
|
self.filterData = self.params.filterData
|
||||||
|
self.describe = self.params.describe
|
||||||
|
|
||||||
def filterChanged(self):
|
def filterChanged(self):
|
||||||
self.sigFilterChanged.emit(self)
|
self.sigFilterChanged.emit(self)
|
||||||
@ -70,18 +72,28 @@ class DataFilterParameter(ptree.types.GroupParameter):
|
|||||||
for fp in self:
|
for fp in self:
|
||||||
if fp.value() is False:
|
if fp.value() is False:
|
||||||
continue
|
continue
|
||||||
mask &= fp.generateMask(data)
|
mask &= fp.generateMask(data, mask.copy())
|
||||||
#key, mn, mx = fp.fieldName, fp['Min'], fp['Max']
|
#key, mn, mx = fp.fieldName, fp['Min'], fp['Max']
|
||||||
|
|
||||||
#vals = data[key]
|
#vals = data[key]
|
||||||
#mask &= (vals >= mn)
|
#mask &= (vals >= mn)
|
||||||
#mask &= (vals < mx) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
|
#mask &= (vals < mx) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
|
||||||
return mask
|
return mask
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
"""Return a list of strings describing the currently enabled filters."""
|
||||||
|
desc = []
|
||||||
|
for fp in self:
|
||||||
|
if fp.value() is False:
|
||||||
|
continue
|
||||||
|
desc.append(fp.describe())
|
||||||
|
return desc
|
||||||
|
|
||||||
class RangeFilterItem(ptree.types.SimpleParameter):
|
class RangeFilterItem(ptree.types.SimpleParameter):
|
||||||
def __init__(self, name, opts):
|
def __init__(self, name, opts):
|
||||||
self.fieldName = name
|
self.fieldName = name
|
||||||
units = opts.get('units', '')
|
units = opts.get('units', '')
|
||||||
|
self.units = units
|
||||||
ptree.types.SimpleParameter.__init__(self,
|
ptree.types.SimpleParameter.__init__(self,
|
||||||
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
|
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
|
||||||
children=[
|
children=[
|
||||||
@ -90,19 +102,24 @@ class RangeFilterItem(ptree.types.SimpleParameter):
|
|||||||
dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),
|
dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),
|
||||||
])
|
])
|
||||||
|
|
||||||
def generateMask(self, data):
|
def generateMask(self, data, mask):
|
||||||
vals = data[self.fieldName]
|
vals = data[self.fieldName][mask]
|
||||||
return (vals >= self['Min']) & (vals < self['Max']) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
|
mask[mask] = (vals >= self['Min']) & (vals < self['Max']) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
|
||||||
|
return mask
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
return "%s < %s < %s" % (pg.siFormat(self['Min'], suffix=self.units), self.fieldName, pg.siFormat(self['Max'], suffix=self.units))
|
||||||
|
|
||||||
class EnumFilterItem(ptree.types.SimpleParameter):
|
class EnumFilterItem(ptree.types.SimpleParameter):
|
||||||
def __init__(self, name, opts):
|
def __init__(self, name, opts):
|
||||||
self.fieldName = name
|
self.fieldName = name
|
||||||
vals = opts.get('values', [])
|
vals = opts.get('values', [])
|
||||||
childs = []
|
childs = []
|
||||||
for v in vals:
|
if isinstance(vals, list):
|
||||||
ch = ptree.Parameter.create(name=str(v), type='bool', value=True)
|
vals = OrderedDict([(v,str(v)) for v in vals])
|
||||||
ch.maskValue = v
|
for val,vname in vals.items():
|
||||||
|
ch = ptree.Parameter.create(name=vname, type='bool', value=True)
|
||||||
|
ch.maskValue = val
|
||||||
childs.append(ch)
|
childs.append(ch)
|
||||||
ch = ptree.Parameter.create(name='(other)', type='bool', value=True)
|
ch = ptree.Parameter.create(name='(other)', type='bool', value=True)
|
||||||
ch.maskValue = '__other__'
|
ch.maskValue = '__other__'
|
||||||
@ -112,10 +129,10 @@ class EnumFilterItem(ptree.types.SimpleParameter):
|
|||||||
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
|
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
|
||||||
children=childs)
|
children=childs)
|
||||||
|
|
||||||
def generateMask(self, data):
|
def generateMask(self, data, startMask):
|
||||||
vals = data[self.fieldName]
|
vals = data[self.fieldName][startMask]
|
||||||
mask = np.ones(len(data), dtype=bool)
|
mask = np.ones(len(vals), dtype=bool)
|
||||||
otherMask = np.ones(len(data), dtype=bool)
|
otherMask = np.ones(len(vals), dtype=bool)
|
||||||
for c in self:
|
for c in self:
|
||||||
key = c.maskValue
|
key = c.maskValue
|
||||||
if key == '__other__':
|
if key == '__other__':
|
||||||
@ -125,4 +142,9 @@ class EnumFilterItem(ptree.types.SimpleParameter):
|
|||||||
otherMask &= m
|
otherMask &= m
|
||||||
if c.value() is False:
|
if c.value() is False:
|
||||||
mask &= m
|
mask &= m
|
||||||
return mask
|
startMask[startMask] = mask
|
||||||
|
return startMask
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
vals = [ch.name() for ch in self if ch.value() is True]
|
||||||
|
return "%s: %s" % (self.fieldName, ', '.join(vals))
|
@ -6,6 +6,7 @@ import pyqtgraph.parametertree as ptree
|
|||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pyqtgraph.pgcollections import OrderedDict
|
from pyqtgraph.pgcollections import OrderedDict
|
||||||
|
import pyqtgraph as pg
|
||||||
|
|
||||||
__all__ = ['ScatterPlotWidget']
|
__all__ = ['ScatterPlotWidget']
|
||||||
|
|
||||||
@ -47,6 +48,12 @@ class ScatterPlotWidget(QtGui.QSplitter):
|
|||||||
self.ctrlPanel.addWidget(self.ptree)
|
self.ctrlPanel.addWidget(self.ptree)
|
||||||
self.addWidget(self.plot)
|
self.addWidget(self.plot)
|
||||||
|
|
||||||
|
bg = pg.mkColor(pg.getConfigOption('background'))
|
||||||
|
bg.setAlpha(150)
|
||||||
|
self.filterText = pg.TextItem(border=pg.getConfigOption('foreground'), color=bg)
|
||||||
|
self.filterText.setPos(60,20)
|
||||||
|
self.filterText.setParentItem(self.plot.plotItem)
|
||||||
|
|
||||||
self.data = None
|
self.data = None
|
||||||
self.mouseOverField = None
|
self.mouseOverField = None
|
||||||
self.scatterPlot = None
|
self.scatterPlot = None
|
||||||
@ -97,6 +104,13 @@ class ScatterPlotWidget(QtGui.QSplitter):
|
|||||||
def filterChanged(self, f):
|
def filterChanged(self, f):
|
||||||
self.filtered = None
|
self.filtered = None
|
||||||
self.updatePlot()
|
self.updatePlot()
|
||||||
|
desc = self.filter.describe()
|
||||||
|
if len(desc) == 0:
|
||||||
|
self.filterText.setVisible(False)
|
||||||
|
else:
|
||||||
|
self.filterText.setText('\n'.join(desc))
|
||||||
|
self.filterText.setVisible(True)
|
||||||
|
|
||||||
|
|
||||||
def updatePlot(self):
|
def updatePlot(self):
|
||||||
self.plot.clear()
|
self.plot.clear()
|
||||||
@ -125,69 +139,69 @@ class ScatterPlotWidget(QtGui.QSplitter):
|
|||||||
self.plot.setLabels(left=('N', ''), bottom=(sel[0], units[0]), title='')
|
self.plot.setLabels(left=('N', ''), bottom=(sel[0], units[0]), title='')
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return
|
return
|
||||||
x = data[sel[0]]
|
#x = data[sel[0]]
|
||||||
#if x.dtype.kind == 'f':
|
#y = None
|
||||||
#mask = ~np.isnan(x)
|
xy = [data[sel[0]], None]
|
||||||
#else:
|
|
||||||
#mask = np.ones(len(x), dtype=bool)
|
|
||||||
#x = x[mask]
|
|
||||||
#style['symbolBrush'] = colors[mask]
|
|
||||||
y = None
|
|
||||||
elif len(sel) == 2:
|
elif len(sel) == 2:
|
||||||
self.plot.setLabels(left=(sel[1],units[1]), bottom=(sel[0],units[0]))
|
self.plot.setLabels(left=(sel[1],units[1]), bottom=(sel[0],units[0]))
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
xydata = []
|
xy = [data[sel[0]], data[sel[1]]]
|
||||||
for ax in [0,1]:
|
#xydata = []
|
||||||
d = data[sel[ax]]
|
#for ax in [0,1]:
|
||||||
## scatter catecorical values just a bit so they show up better in the scatter plot.
|
#d = data[sel[ax]]
|
||||||
#if sel[ax] in ['MorphologyBSMean', 'MorphologyTDMean', 'FIType']:
|
### scatter catecorical values just a bit so they show up better in the scatter plot.
|
||||||
#d += np.random.normal(size=len(cells), scale=0.1)
|
##if sel[ax] in ['MorphologyBSMean', 'MorphologyTDMean', 'FIType']:
|
||||||
xydata.append(d)
|
##d += np.random.normal(size=len(cells), scale=0.1)
|
||||||
x,y = xydata
|
|
||||||
#mask = np.ones(len(x), dtype=bool)
|
#xydata.append(d)
|
||||||
#if x.dtype.kind == 'f':
|
#x,y = xydata
|
||||||
#mask |= ~np.isnan(x)
|
|
||||||
#if y.dtype.kind == 'f':
|
|
||||||
#mask |= ~np.isnan(y)
|
|
||||||
#x = x[mask]
|
|
||||||
#y = y[mask]
|
|
||||||
#style['symbolBrush'] = colors[mask]
|
|
||||||
|
|
||||||
## convert enum-type fields to float, set axis labels
|
## convert enum-type fields to float, set axis labels
|
||||||
xy = [x,y]
|
enum = [False, False]
|
||||||
for i in [0,1]:
|
for i in [0,1]:
|
||||||
axis = self.plot.getAxis(['bottom', 'left'][i])
|
axis = self.plot.getAxis(['bottom', 'left'][i])
|
||||||
if xy[i] is not None and xy[i].dtype.kind in ('S', 'O'):
|
if xy[i] is not None and (self.fields[sel[i]].get('mode', None) == 'enum' or xy[i].dtype.kind in ('S', 'O')):
|
||||||
vals = self.fields[sel[i]].get('values', list(set(xy[i])))
|
vals = self.fields[sel[i]].get('values', list(set(xy[i])))
|
||||||
xy[i] = np.array([vals.index(x) if x in vals else len(vals) for x in xy[i]], dtype=float)
|
xy[i] = np.array([vals.index(x) if x in vals else len(vals) for x in xy[i]], dtype=float)
|
||||||
axis.setTicks([list(enumerate(vals))])
|
axis.setTicks([list(enumerate(vals))])
|
||||||
|
enum[i] = True
|
||||||
else:
|
else:
|
||||||
axis.setTicks(None) # reset to automatic ticking
|
axis.setTicks(None) # reset to automatic ticking
|
||||||
x,y = xy
|
|
||||||
|
|
||||||
## mask out any nan values
|
## mask out any nan values
|
||||||
mask = np.ones(len(x), dtype=bool)
|
mask = np.ones(len(xy[0]), dtype=bool)
|
||||||
if x.dtype.kind == 'f':
|
if xy[0].dtype.kind == 'f':
|
||||||
mask &= ~np.isnan(x)
|
mask &= ~np.isnan(xy[0])
|
||||||
if y is not None and y.dtype.kind == 'f':
|
if xy[1] is not None and xy[1].dtype.kind == 'f':
|
||||||
mask &= ~np.isnan(y)
|
mask &= ~np.isnan(xy[1])
|
||||||
x = x[mask]
|
|
||||||
|
xy[0] = xy[0][mask]
|
||||||
style['symbolBrush'] = colors[mask]
|
style['symbolBrush'] = colors[mask]
|
||||||
|
|
||||||
## Scatter y-values for a histogram-like appearance
|
## Scatter y-values for a histogram-like appearance
|
||||||
if y is None:
|
if xy[1] is None:
|
||||||
y = fn.pseudoScatter(x)
|
## column scatter plot
|
||||||
|
xy[1] = fn.pseudoScatter(xy[0])
|
||||||
else:
|
else:
|
||||||
y = y[mask]
|
## beeswarm plots
|
||||||
|
xy[1] = xy[1][mask]
|
||||||
|
for ax in [0,1]:
|
||||||
|
if not enum[ax]:
|
||||||
|
continue
|
||||||
|
for i in range(int(xy[ax].max())+1):
|
||||||
|
keymask = xy[ax] == i
|
||||||
|
scatter = pg.pseudoScatter(xy[1-ax][keymask], bidir=True)
|
||||||
|
scatter *= 0.2 / np.abs(scatter).max()
|
||||||
|
xy[ax][keymask] += scatter
|
||||||
|
|
||||||
if self.scatterPlot is not None:
|
if self.scatterPlot is not None:
|
||||||
try:
|
try:
|
||||||
self.scatterPlot.sigPointsClicked.disconnect(self.plotClicked)
|
self.scatterPlot.sigPointsClicked.disconnect(self.plotClicked)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.scatterPlot = self.plot.plot(x, y, data=data[mask], **style)
|
self.scatterPlot = self.plot.plot(xy[0], xy[1], data=data[mask], **style)
|
||||||
self.scatterPlot.sigPointsClicked.connect(self.plotClicked)
|
self.scatterPlot.sigPointsClicked.connect(self.plotClicked)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user