Fixed click signal propagation for PlotDataItem
This commit is contained in:
parent
59ad54c55e
commit
33bc81a121
@ -12,17 +12,50 @@ __all__ = ['PlotCurveItem']
|
||||
class PlotCurveItem(GraphicsObject):
|
||||
|
||||
|
||||
"""Class representing a single plot curve. Provides:
|
||||
"""
|
||||
Class representing a single plot curve. Instances of this class are created
|
||||
automatically as part of PlotDataItem; these rarely need to be instantiated
|
||||
directly.
|
||||
|
||||
Features:
|
||||
|
||||
- Fast data update
|
||||
- FFT display mode
|
||||
- shadow pen
|
||||
- mouse interaction
|
||||
- FFT display mode (accessed via PlotItem context menu)
|
||||
- Fill under curve
|
||||
- Mouse interaction
|
||||
|
||||
==================== ===============================================
|
||||
**Signals:**
|
||||
sigPlotChanged(self) Emitted when the data being plotted has changed
|
||||
sigClicked(self) Emitted when the curve is clicked
|
||||
==================== ===============================================
|
||||
"""
|
||||
|
||||
sigPlotChanged = QtCore.Signal(object)
|
||||
sigClicked = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, y=None, x=None, fillLevel=None, copy=False, pen=None, shadowPen=None, brush=None, parent=None, clickable=False):
|
||||
"""
|
||||
============== =======================================================
|
||||
**Arguments:**
|
||||
x, y (numpy arrays) Data to show
|
||||
pen Pen to use when drawing. Any single argument accepted by
|
||||
:func:`mkPen <pyqtgraph.mkPen>` is allowed.
|
||||
shadowPen Pen for drawing behind the primary pen. Usually this
|
||||
is used to emphasize the curve by providing a
|
||||
high-contrast border. Any single argument accepted by
|
||||
:func:`mkPen <pyqtgraph.mkPen>` is allowed.
|
||||
fillLevel (float or None) Fill the area 'under' the curve to
|
||||
*fillLevel*
|
||||
brush QBrush to use when filling. Any single argument accepted
|
||||
by :func:`mkBrush <pyqtgraph.mkBrush>` is allowed.
|
||||
clickable If True, the item will emit sigClicked when it is
|
||||
clicked on.
|
||||
============== =======================================================
|
||||
|
||||
|
||||
|
||||
"""
|
||||
GraphicsObject.__init__(self, parent)
|
||||
self.clear()
|
||||
self.path = None
|
||||
@ -62,6 +95,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
return interface in ints
|
||||
|
||||
def setClickable(self, s):
|
||||
"""Sets whether the item responds to mouse clicks."""
|
||||
self.clickable = s
|
||||
|
||||
|
||||
@ -127,18 +161,25 @@ class PlotCurveItem(GraphicsObject):
|
||||
#return self.metaData
|
||||
|
||||
def setPen(self, *args, **kargs):
|
||||
"""Set the pen used to draw the curve."""
|
||||
self.opts['pen'] = fn.mkPen(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
def setShadowPen(self, *args, **kargs):
|
||||
"""Set the shadow pen used to draw behind tyhe primary pen.
|
||||
This pen must have a larger width than the primary
|
||||
pen to be visible.
|
||||
"""
|
||||
self.opts['shadowPen'] = fn.mkPen(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
def setBrush(self, *args, **kargs):
|
||||
"""Set the brush used when filling the area under the curve"""
|
||||
self.opts['brush'] = fn.mkBrush(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
def setFillLevel(self, level):
|
||||
"""Set the level filled to when filling under the curve"""
|
||||
self.opts['fillLevel'] = level
|
||||
self.fillPath = None
|
||||
self.update()
|
||||
@ -177,7 +218,9 @@ class PlotCurveItem(GraphicsObject):
|
||||
#self.update()
|
||||
|
||||
def setData(self, *args, **kargs):
|
||||
"""Same as updateData()"""
|
||||
"""
|
||||
Accepts most of the same arguments as __init__.
|
||||
"""
|
||||
self.updateData(*args, **kargs)
|
||||
|
||||
def updateData(self, *args, **kargs):
|
||||
|
@ -24,15 +24,18 @@ class PlotDataItem(GraphicsObject):
|
||||
usually created by plot() methods such as :func:`pyqtgraph.plot` and
|
||||
:func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`.
|
||||
|
||||
===================== ==============================================
|
||||
============================== ==============================================
|
||||
**Signals:**
|
||||
sigPlotChanged(self) Emitted when the data in this item is updated.
|
||||
sigClicked(self) Emitted when the item is clicked.
|
||||
===================== ==============================================
|
||||
sigPointsClicked(self, points) Emitted when a plot point is clicked
|
||||
Sends the list of points under the mouse.
|
||||
============================== ==============================================
|
||||
"""
|
||||
|
||||
sigPlotChanged = QtCore.Signal(object)
|
||||
sigClicked = QtCore.Signal(object)
|
||||
sigPointsClicked = QtCore.Signal(object, object)
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
"""
|
||||
@ -109,6 +112,10 @@ class PlotDataItem(GraphicsObject):
|
||||
self.curve.setParentItem(self)
|
||||
self.scatter.setParentItem(self)
|
||||
|
||||
self.curve.sigClicked.connect(self.curveClicked)
|
||||
self.scatter.sigClicked.connect(self.scatterClicked)
|
||||
|
||||
|
||||
#self.clear()
|
||||
self.opts = {
|
||||
'fftMode': False,
|
||||
@ -127,6 +134,8 @@ class PlotDataItem(GraphicsObject):
|
||||
'symbolPen': (200,200,200),
|
||||
'symbolBrush': (50, 50, 150),
|
||||
'identical': False,
|
||||
|
||||
'data': None,
|
||||
}
|
||||
self.setData(*args, **kargs)
|
||||
|
||||
@ -150,8 +159,8 @@ class PlotDataItem(GraphicsObject):
|
||||
self.xDisp = self.yDisp = None
|
||||
self.updateItems()
|
||||
|
||||
def setLogMode(self, mode):
|
||||
self.opts['logMode'] = mode
|
||||
def setLogMode(self, xMode, yMode):
|
||||
self.opts['logMode'] = (xMode, yMode)
|
||||
self.xDisp = self.yDisp = None
|
||||
self.updateItems()
|
||||
|
||||
@ -244,7 +253,7 @@ class PlotDataItem(GraphicsObject):
|
||||
data = args[0]
|
||||
dt = dataType(data)
|
||||
if dt == 'empty':
|
||||
return
|
||||
pass
|
||||
elif dt == 'listOfValues':
|
||||
y = np.array(data)
|
||||
elif dt == 'Nx2array':
|
||||
@ -260,6 +269,8 @@ class PlotDataItem(GraphicsObject):
|
||||
x = np.array([d.get('x',None) for d in data])
|
||||
if 'y' in data[0]:
|
||||
y = np.array([d.get('y',None) for d in data])
|
||||
for k in ['data', 'symbolSize', 'symbolPen', 'symbolBrush', 'symbolShape']:
|
||||
kargs[k] = [d.get(k, None) for d in data]
|
||||
elif dt == 'MetaArray':
|
||||
y = data.view(np.ndarray)
|
||||
x = data.xvals(0).view(np.ndarray)
|
||||
@ -349,7 +360,8 @@ class PlotDataItem(GraphicsObject):
|
||||
curveArgs[v] = self.opts[k]
|
||||
|
||||
scatterArgs = {}
|
||||
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol'), ('symbolSize', 'size')]:
|
||||
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol'), ('symbolSize', 'size'), ('data', 'data')]:
|
||||
if k in self.opts:
|
||||
scatterArgs[v] = self.opts[k]
|
||||
|
||||
x,y = self.getData()
|
||||
@ -398,6 +410,11 @@ class PlotDataItem(GraphicsObject):
|
||||
x = np.log10(x)
|
||||
if self.opts['logMode'][1]:
|
||||
y = np.log10(y)
|
||||
if any(self.opts['logMode']): ## re-check for NANs after log
|
||||
nanMask = np.isinf(x) | np.isinf(y) | np.isnan(x) | np.isnan(y)
|
||||
if any(nanMask):
|
||||
x = x[~nanMask]
|
||||
y = y[~nanMask]
|
||||
self.xDisp = x
|
||||
self.yDisp = y
|
||||
#print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
||||
@ -438,6 +455,13 @@ class PlotDataItem(GraphicsObject):
|
||||
def appendData(self, *args, **kargs):
|
||||
pass
|
||||
|
||||
def curveClicked(self):
|
||||
self.sigClicked.emit(self)
|
||||
|
||||
def scatterClicked(self, plt, points):
|
||||
self.sigClicked.emit(self)
|
||||
self.sigPointsClicked.emit(self, points)
|
||||
|
||||
|
||||
def dataType(obj):
|
||||
if hasattr(obj, '__len__') and len(obj) == 0:
|
||||
|
@ -7,7 +7,23 @@ import scipy.stats
|
||||
|
||||
__all__ = ['ScatterPlotItem', 'SpotItem']
|
||||
class ScatterPlotItem(GraphicsObject):
|
||||
"""
|
||||
Displays a set of x/y points. Instances of this class are created
|
||||
automatically as part of PlotDataItem; these rarely need to be instantiated
|
||||
directly.
|
||||
|
||||
The size, shape, pen, and fill brush may be set for each point individually
|
||||
or for all points.
|
||||
|
||||
|
||||
======================== ===============================================
|
||||
**Signals:**
|
||||
sigPlotChanged(self) Emitted when the data being plotted has changed
|
||||
sigClicked(self, points) Emitted when the curve is clicked. Sends a list
|
||||
of all the points under the mouse pointer.
|
||||
======================== ===============================================
|
||||
|
||||
"""
|
||||
#sigPointClicked = QtCore.Signal(object, object)
|
||||
sigClicked = QtCore.Signal(object, object) ## self, points
|
||||
sigPlotChanged = QtCore.Signal(object)
|
||||
@ -37,35 +53,37 @@ class ScatterPlotItem(GraphicsObject):
|
||||
|
||||
def setData(self, *args, **kargs):
|
||||
"""
|
||||
Ordered Arguments:
|
||||
If there is only one unnamed argument, it will be interpreted like the 'spots' argument.
|
||||
**Ordered Arguments:**
|
||||
|
||||
If there are two unnamed arguments, they will be interpreted as sequences of x and y values.
|
||||
* If there is only one unnamed argument, it will be interpreted like the 'spots' argument.
|
||||
* If there are two unnamed arguments, they will be interpreted as sequences of x and y values.
|
||||
|
||||
Keyword Arguments:
|
||||
*spots*: Optional list of dicts. Each dict specifies parameters for a single spot:
|
||||
====================== =================================================
|
||||
**Keyword Arguments:**
|
||||
*spots* Optional list of dicts. Each dict specifies parameters for a single spot:
|
||||
{'pos': (x,y), 'size', 'pen', 'brush', 'symbol'}. This is just an alternate method
|
||||
of passing in data for the corresponding arguments.
|
||||
*x*,*y*: 1D arrays of x,y values.
|
||||
*pos*: 2D structure of x,y pairs (such as Nx2 array or list of tuples)
|
||||
*pxMode*: If True, spots are always the same size regardless of scaling, and size is given in px.
|
||||
*x*,*y* 1D arrays of x,y values.
|
||||
*pos* 2D structure of x,y pairs (such as Nx2 array or list of tuples)
|
||||
*pxMode* If True, spots are always the same size regardless of scaling, and size is given in px.
|
||||
Otherwise, size is in scene coordinates and the spots scale with the view.
|
||||
Default is True
|
||||
*identical*: If True, all spots are forced to look identical.
|
||||
*identical* If True, all spots are forced to look identical.
|
||||
This can result in performance enhancement.
|
||||
Default is False
|
||||
*symbol* can be one (or a list) of:
|
||||
'o' circle (default)
|
||||
's' square
|
||||
't' triangle
|
||||
'd' diamond
|
||||
'+' plus
|
||||
|
||||
*pen*: The pen (or list of pens) to use for drawing spot outlines.
|
||||
*brush*: The brush (or list of brushes) to use for filling spots.
|
||||
*size*: The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
|
||||
* 'o' circle (default)
|
||||
* 's' square
|
||||
* 't' triangle
|
||||
* 'd' diamond
|
||||
* '+' plus
|
||||
*pen* The pen (or list of pens) to use for drawing spot outlines.
|
||||
*brush* The brush (or list of brushes) to use for filling spots.
|
||||
*size* The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
|
||||
it is in the item's local coordinate system.
|
||||
*data*: a list of python objects used to uniquely identify each spot.
|
||||
*data* a list of python objects used to uniquely identify each spot.
|
||||
====================== =================================================
|
||||
"""
|
||||
|
||||
self.clear()
|
||||
@ -149,6 +167,9 @@ class ScatterPlotItem(GraphicsObject):
|
||||
setMethod = getattr(self, 'set' + k[0].upper() + k[1:])
|
||||
setMethod(kargs[k])
|
||||
|
||||
if 'data' in kargs:
|
||||
self.setPointData(kargs['data'])
|
||||
|
||||
self.updateSpots()
|
||||
|
||||
|
||||
@ -183,7 +204,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
#self.data[k].append(v)
|
||||
|
||||
def setPoints(self, *args, **kargs):
|
||||
"""Deprecated; use setData"""
|
||||
##Deprecated; use setData
|
||||
return self.setData(*args, **kargs)
|
||||
|
||||
#def setPoints(self, spots=None, x=None, y=None, data=None):
|
||||
@ -259,6 +280,16 @@ class ScatterPlotItem(GraphicsObject):
|
||||
self.opts['size'] = size
|
||||
self.updateSpots()
|
||||
|
||||
def setPointData(self, data):
|
||||
if isinstance(data, np.ndarray) or isinstance(data, list):
|
||||
if self.data is None:
|
||||
raise Exception("Must set xy data before setting meta data.")
|
||||
if len(data) != len(self.data):
|
||||
raise Exception("Length of meta data does not match number of points (%d != %d)" % (len(data), len(self.data)))
|
||||
self.data['data'] = data
|
||||
self.updateSpots()
|
||||
|
||||
|
||||
def setIdentical(self, ident):
|
||||
self.opts['identical'] = ident
|
||||
self.updateSpots()
|
||||
@ -354,6 +385,12 @@ class ScatterPlotItem(GraphicsObject):
|
||||
symbol = self.data['symbol'].copy()
|
||||
symbol[symbol==''] = self.opts['symbol']
|
||||
|
||||
data = self.data['data'].copy()
|
||||
if 'data' in self.opts:
|
||||
data[data==None] = self.opts['data']
|
||||
|
||||
|
||||
|
||||
for i in xrange(len(self.data)):
|
||||
s = self.data[i]
|
||||
pos = Point(s['x'], s['y'])
|
||||
@ -373,7 +410,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
#ymn = min(ymn, pos[1]-psize)
|
||||
#ymx = max(ymx, pos[1]+psize)
|
||||
|
||||
item = self.mkSpot(pos, size[i], self.opts['pxMode'], brush[i], pen[i], s['data'], symbol=symbol[i], index=len(self.spots))
|
||||
item = self.mkSpot(pos, size[i], self.opts['pxMode'], brush[i], pen[i], data[i], symbol=symbol[i], index=len(self.spots))
|
||||
self.spots.append(item)
|
||||
self.data[i]['spot'] = item
|
||||
#if self.optimize:
|
||||
|
Loading…
Reference in New Issue
Block a user