dynamic range limiting in PlotDataItem (#1140)
* dynamic range limiting in PlotDataItem * revised version of cynamic range limiting * replaced == with is operator * removed unicode +- character, converted to ascii * code/docstring cleanup * clean state with changes * silenced numpy all-NaN warnings * reverted PlotWidget.py to original * reverted PlotWidget.py to original * reverted PlotWidget.py to original * rewrapped/reformated setDynamicRangeLimits docstring Co-authored-by: Ogi Moore <ognyan.moore@gmail.com>
This commit is contained in:
parent
39f9c6a6aa
commit
65e90faec5
@ -6,6 +6,7 @@ try:
|
|||||||
except:
|
except:
|
||||||
HAVE_OPENGL = False
|
HAVE_OPENGL = False
|
||||||
|
|
||||||
|
import warnings
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .GraphicsObject import GraphicsObject
|
from .GraphicsObject import GraphicsObject
|
||||||
from .. import functions as fn
|
from .. import functions as fn
|
||||||
@ -148,7 +149,10 @@ class PlotCurveItem(GraphicsObject):
|
|||||||
if frac >= 1.0:
|
if frac >= 1.0:
|
||||||
# include complete data range
|
# include complete data range
|
||||||
# first try faster nanmin/max function, then cut out infs if needed.
|
# first try faster nanmin/max function, then cut out infs if needed.
|
||||||
b = (np.nanmin(d), np.nanmax(d))
|
with warnings.catch_warnings():
|
||||||
|
# All-NaN data is acceptable; Explicit numpy warning is not needed.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
b = (np.nanmin(d), np.nanmax(d))
|
||||||
if any(np.isinf(b)):
|
if any(np.isinf(b)):
|
||||||
mask = np.isfinite(d)
|
mask = np.isfinite(d)
|
||||||
d = d[mask]
|
d = d[mask]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import warnings
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .. import metaarray as metaarray
|
from .. import metaarray as metaarray
|
||||||
from ..Qt import QtCore
|
from ..Qt import QtCore
|
||||||
@ -13,33 +14,33 @@ from .. import getConfigOption
|
|||||||
class PlotDataItem(GraphicsObject):
|
class PlotDataItem(GraphicsObject):
|
||||||
"""
|
"""
|
||||||
**Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
|
**Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
|
||||||
|
|
||||||
GraphicsItem for displaying plot curves, scatter plots, or both.
|
GraphicsItem for displaying plot curves, scatter plots, or both.
|
||||||
While it is possible to use :class:`PlotCurveItem <pyqtgraph.PlotCurveItem>` or
|
While it is possible to use :class:`PlotCurveItem <pyqtgraph.PlotCurveItem>` or
|
||||||
:class:`ScatterPlotItem <pyqtgraph.ScatterPlotItem>` individually, this class
|
:class:`ScatterPlotItem <pyqtgraph.ScatterPlotItem>` individually, this class
|
||||||
provides a unified interface to both. Instances of :class:`PlotDataItem` are
|
provides a unified interface to both. Instances of :class:`PlotDataItem` are
|
||||||
usually created by plot() methods such as :func:`pyqtgraph.plot` and
|
usually created by plot() methods such as :func:`pyqtgraph.plot` and
|
||||||
:func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`.
|
:func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`.
|
||||||
|
|
||||||
============================== ==============================================
|
============================== ==============================================
|
||||||
**Signals:**
|
**Signals:**
|
||||||
sigPlotChanged(self) Emitted when the data in this item is updated.
|
sigPlotChanged(self) Emitted when the data in this item is updated.
|
||||||
sigClicked(self) Emitted when the item is clicked.
|
sigClicked(self) Emitted when the item is clicked.
|
||||||
sigPointsClicked(self, points) Emitted when a plot point is clicked
|
sigPointsClicked(self, points) Emitted when a plot point is clicked
|
||||||
Sends the list of points under the mouse.
|
Sends the list of points under the mouse.
|
||||||
============================== ==============================================
|
============================== ==============================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sigPlotChanged = QtCore.Signal(object)
|
sigPlotChanged = QtCore.Signal(object)
|
||||||
sigClicked = QtCore.Signal(object)
|
sigClicked = QtCore.Signal(object)
|
||||||
sigPointsClicked = QtCore.Signal(object, object)
|
sigPointsClicked = QtCore.Signal(object, object)
|
||||||
|
|
||||||
def __init__(self, *args, **kargs):
|
def __init__(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
There are many different ways to create a PlotDataItem:
|
There are many different ways to create a PlotDataItem:
|
||||||
|
|
||||||
**Data initialization arguments:** (x,y data only)
|
**Data initialization arguments:** (x,y data only)
|
||||||
|
|
||||||
=================================== ======================================
|
=================================== ======================================
|
||||||
PlotDataItem(xValues, yValues) x and y values may be any sequence
|
PlotDataItem(xValues, yValues) x and y values may be any sequence
|
||||||
(including ndarray) of real numbers
|
(including ndarray) of real numbers
|
||||||
@ -49,8 +50,9 @@ class PlotDataItem(GraphicsObject):
|
|||||||
PlotDataItem(ndarray(Nx2)) numpy array with shape (N, 2) where
|
PlotDataItem(ndarray(Nx2)) numpy array with shape (N, 2) where
|
||||||
``x=data[:,0]`` and ``y=data[:,1]``
|
``x=data[:,0]`` and ``y=data[:,1]``
|
||||||
=================================== ======================================
|
=================================== ======================================
|
||||||
|
|
||||||
**Data initialization arguments:** (x,y data AND may include spot style)
|
**Data initialization arguments:** (x,y data AND may include spot style)
|
||||||
|
|
||||||
|
|
||||||
============================ =========================================
|
============================ =========================================
|
||||||
PlotDataItem(recarray) numpy array with ``dtype=[('x', float),
|
PlotDataItem(recarray) numpy array with ``dtype=[('x', float),
|
||||||
@ -73,6 +75,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
shadowPen Pen for secondary line to draw behind the primary line. disabled by default.
|
shadowPen Pen for secondary line to draw behind the primary line. disabled by default.
|
||||||
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
||||||
fillLevel Fill the area between the curve and fillLevel
|
fillLevel Fill the area between the curve and fillLevel
|
||||||
|
|
||||||
fillOutline (bool) If True, an outline surrounding the *fillLevel* area is drawn.
|
fillOutline (bool) If True, an outline surrounding the *fillLevel* area is drawn.
|
||||||
fillBrush Fill to use when fillLevel is specified.
|
fillBrush Fill to use when fillLevel is specified.
|
||||||
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
||||||
@ -88,53 +91,56 @@ class PlotDataItem(GraphicsObject):
|
|||||||
step mode is not enabled.
|
step mode is not enabled.
|
||||||
Passing True is a deprecated equivalent to "center".
|
Passing True is a deprecated equivalent to "center".
|
||||||
(added in version 0.9.9)
|
(added in version 0.9.9)
|
||||||
|
|
||||||
============ ==============================================================================
|
============ ==============================================================================
|
||||||
|
|
||||||
**Point style keyword arguments:** (see :func:`ScatterPlotItem.setData() <pyqtgraph.ScatterPlotItem.setData>` for more information)
|
**Point style keyword arguments:** (see :func:`ScatterPlotItem.setData() <pyqtgraph.ScatterPlotItem.setData>` for more information)
|
||||||
|
|
||||||
============ =====================================================
|
============ =====================================================
|
||||||
symbol Symbol to use for drawing points OR list of symbols,
|
symbol Symbol to use for drawing points OR list of symbols,
|
||||||
one per point. Default is no symbol.
|
one per point. Default is no symbol.
|
||||||
Options are o, s, t, d, +, or any QPainterPath
|
Options are o, s, t, d, +, or any QPainterPath
|
||||||
symbolPen Outline pen for drawing points OR list of pens, one
|
symbolPen Outline pen for drawing points OR list of pens, one
|
||||||
per point. May be any single argument accepted by
|
per point. May be any single argument accepted by
|
||||||
:func:`mkPen() <pyqtgraph.mkPen>`
|
:func:`mkPen() <pyqtgraph.mkPen>`
|
||||||
symbolBrush Brush for filling points OR list of brushes, one per
|
symbolBrush Brush for filling points OR list of brushes, one per
|
||||||
point. May be any single argument accepted by
|
point. May be any single argument accepted by
|
||||||
:func:`mkBrush() <pyqtgraph.mkBrush>`
|
:func:`mkBrush() <pyqtgraph.mkBrush>`
|
||||||
symbolSize Diameter of symbols OR list of diameters.
|
symbolSize Diameter of symbols OR list of diameters.
|
||||||
pxMode (bool) If True, then symbolSize is specified in
|
pxMode (bool) If True, then symbolSize is specified in
|
||||||
pixels. If False, then symbolSize is
|
pixels. If False, then symbolSize is
|
||||||
specified in data coordinates.
|
specified in data coordinates.
|
||||||
============ =====================================================
|
============ =====================================================
|
||||||
|
|
||||||
**Optimization keyword arguments:**
|
**Optimization keyword arguments:**
|
||||||
|
|
||||||
================ =====================================================================
|
================= =====================================================================
|
||||||
antialias (bool) By default, antialiasing is disabled to improve performance.
|
antialias (bool) By default, antialiasing is disabled to improve performance.
|
||||||
Note that in some cases (in particluar, when pxMode=True), points
|
Note that in some cases (in particluar, when pxMode=True), points
|
||||||
will be rendered antialiased even if this is set to False.
|
will be rendered antialiased even if this is set to False.
|
||||||
decimate deprecated.
|
decimate deprecated.
|
||||||
downsample (int) Reduce the number of samples displayed by this value
|
downsample (int) Reduce the number of samples displayed by this value
|
||||||
downsampleMethod 'subsample': Downsample by taking the first of N samples.
|
downsampleMethod 'subsample': Downsample by taking the first of N samples.
|
||||||
This method is fastest and least accurate.
|
This method is fastest and least accurate.
|
||||||
'mean': Downsample by taking the mean of N samples.
|
'mean': Downsample by taking the mean of N samples.
|
||||||
'peak': Downsample by drawing a saw wave that follows the min
|
'peak': Downsample by drawing a saw wave that follows the min
|
||||||
and max of the original data. This method produces the best
|
and max of the original data. This method produces the best
|
||||||
visual representation of the data but is slower.
|
visual representation of the data but is slower.
|
||||||
autoDownsample (bool) If True, resample the data before plotting to avoid plotting
|
autoDownsample (bool) If True, resample the data before plotting to avoid plotting
|
||||||
multiple line segments per pixel. This can improve performance when
|
multiple line segments per pixel. This can improve performance when
|
||||||
viewing very high-density data, but increases the initial overhead
|
viewing very high-density data, but increases the initial overhead
|
||||||
and memory usage.
|
and memory usage.
|
||||||
clipToView (bool) If True, only plot data that is visible within the X range of
|
clipToView (bool) If True, only plot data that is visible within the X range of
|
||||||
the containing ViewBox. This can improve performance when plotting
|
the containing ViewBox. This can improve performance when plotting
|
||||||
very large data sets where only a fraction of the data is visible
|
very large data sets where only a fraction of the data is visible
|
||||||
at any time.
|
at any time.
|
||||||
identical *deprecated*
|
dynamicRangeLimit (float or None) Limit off-screen positions of data points at large
|
||||||
================ =====================================================================
|
magnification to avoids display errors. Disabled if None.
|
||||||
|
identical *deprecated*
|
||||||
|
================= =====================================================================
|
||||||
|
|
||||||
**Meta-info keyword arguments:**
|
**Meta-info keyword arguments:**
|
||||||
|
|
||||||
========== ================================================
|
========== ================================================
|
||||||
name name of dataset. This would appear in a legend
|
name name of dataset. This would appear in a legend
|
||||||
========== ================================================
|
========== ================================================
|
||||||
@ -152,57 +158,58 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.scatter = ScatterPlotItem()
|
self.scatter = ScatterPlotItem()
|
||||||
self.curve.setParentItem(self)
|
self.curve.setParentItem(self)
|
||||||
self.scatter.setParentItem(self)
|
self.scatter.setParentItem(self)
|
||||||
|
|
||||||
self.curve.sigClicked.connect(self.curveClicked)
|
self.curve.sigClicked.connect(self.curveClicked)
|
||||||
self.scatter.sigClicked.connect(self.scatterClicked)
|
self.scatter.sigClicked.connect(self.scatterClicked)
|
||||||
|
|
||||||
|
self._dataRect = None
|
||||||
#self.clear()
|
#self.clear()
|
||||||
self.opts = {
|
self.opts = {
|
||||||
'connect': 'all',
|
'connect': 'all',
|
||||||
|
|
||||||
'fftMode': False,
|
'fftMode': False,
|
||||||
'logMode': [False, False],
|
'logMode': [False, False],
|
||||||
'derivativeMode': False,
|
'derivativeMode': False,
|
||||||
'phasemapMode': False,
|
'phasemapMode': False,
|
||||||
'alphaHint': 1.0,
|
'alphaHint': 1.0,
|
||||||
'alphaMode': False,
|
'alphaMode': False,
|
||||||
|
|
||||||
'pen': (200,200,200),
|
'pen': (200,200,200),
|
||||||
'shadowPen': None,
|
'shadowPen': None,
|
||||||
'fillLevel': None,
|
'fillLevel': None,
|
||||||
'fillOutline': False,
|
'fillOutline': False,
|
||||||
'fillBrush': None,
|
'fillBrush': None,
|
||||||
'stepMode': None,
|
'stepMode': None,
|
||||||
|
|
||||||
'symbol': None,
|
'symbol': None,
|
||||||
'symbolSize': 10,
|
'symbolSize': 10,
|
||||||
'symbolPen': (200,200,200),
|
'symbolPen': (200,200,200),
|
||||||
'symbolBrush': (50, 50, 150),
|
'symbolBrush': (50, 50, 150),
|
||||||
'pxMode': True,
|
'pxMode': True,
|
||||||
|
|
||||||
'antialias': getConfigOption('antialias'),
|
'antialias': getConfigOption('antialias'),
|
||||||
'pointMode': None,
|
'pointMode': None,
|
||||||
|
|
||||||
'downsample': 1,
|
'downsample': 1,
|
||||||
'autoDownsample': False,
|
'autoDownsample': False,
|
||||||
'downsampleMethod': 'peak',
|
'downsampleMethod': 'peak',
|
||||||
'autoDownsampleFactor': 5., # draw ~5 samples per pixel
|
'autoDownsampleFactor': 5., # draw ~5 samples per pixel
|
||||||
'clipToView': False,
|
'clipToView': False,
|
||||||
|
'dynamicRangeLimit': 1e6,
|
||||||
|
|
||||||
'data': None,
|
'data': None,
|
||||||
}
|
}
|
||||||
self.setData(*args, **kargs)
|
self.setData(*args, **kargs)
|
||||||
|
|
||||||
def implements(self, interface=None):
|
def implements(self, interface=None):
|
||||||
ints = ['plotData']
|
ints = ['plotData']
|
||||||
if interface is None:
|
if interface is None:
|
||||||
return ints
|
return ints
|
||||||
return interface in ints
|
return interface in ints
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.opts.get('name', None)
|
return self.opts.get('name', None)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
return QtCore.QRectF() ## let child items handle this
|
return QtCore.QRectF() ## let child items handle this
|
||||||
|
|
||||||
@ -213,7 +220,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.opts['alphaMode'] = auto
|
self.opts['alphaMode'] = auto
|
||||||
self.setOpacity(alpha)
|
self.setOpacity(alpha)
|
||||||
#self.update()
|
#self.update()
|
||||||
|
|
||||||
def setFftMode(self, mode):
|
def setFftMode(self, mode):
|
||||||
if self.opts['fftMode'] == mode:
|
if self.opts['fftMode'] == mode:
|
||||||
return
|
return
|
||||||
@ -222,7 +229,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.xClean = self.yClean = None
|
self.xClean = self.yClean = None
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
self.informViewBoundsChanged()
|
self.informViewBoundsChanged()
|
||||||
|
|
||||||
def setLogMode(self, xMode, yMode):
|
def setLogMode(self, xMode, yMode):
|
||||||
if self.opts['logMode'] == [xMode, yMode]:
|
if self.opts['logMode'] == [xMode, yMode]:
|
||||||
return
|
return
|
||||||
@ -232,6 +239,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.updateItems()
|
self.updateItems()
|
||||||
self.informViewBoundsChanged()
|
self.informViewBoundsChanged()
|
||||||
|
|
||||||
|
|
||||||
def setDerivativeMode(self, mode):
|
def setDerivativeMode(self, mode):
|
||||||
if self.opts['derivativeMode'] == mode:
|
if self.opts['derivativeMode'] == mode:
|
||||||
return
|
return
|
||||||
@ -255,7 +263,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
return
|
return
|
||||||
self.opts['pointMode'] = mode
|
self.opts['pointMode'] = mode
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setPen(self, *args, **kargs):
|
def setPen(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Sets the pen used to draw lines between points.
|
| Sets the pen used to draw lines between points.
|
||||||
@ -268,11 +276,11 @@ class PlotDataItem(GraphicsObject):
|
|||||||
#c.setPen(pen)
|
#c.setPen(pen)
|
||||||
#self.update()
|
#self.update()
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def setShadowPen(self, *args, **kargs):
|
def setShadowPen(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Sets the shadow pen used to draw lines between points (this is for enhancing contrast or
|
| Sets the shadow pen used to draw lines between points (this is for enhancing contrast or
|
||||||
emphacizing data).
|
emphacizing data).
|
||||||
| This line is drawn behind the primary pen (see :func:`setPen() <pyqtgraph.PlotDataItem.setPen>`)
|
| This line is drawn behind the primary pen (see :func:`setPen() <pyqtgraph.PlotDataItem.setPen>`)
|
||||||
and should generally be assigned greater width than the primary pen.
|
and should generally be assigned greater width than the primary pen.
|
||||||
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
||||||
@ -283,17 +291,17 @@ class PlotDataItem(GraphicsObject):
|
|||||||
#c.setPen(pen)
|
#c.setPen(pen)
|
||||||
#self.update()
|
#self.update()
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def setFillBrush(self, *args, **kargs):
|
def setFillBrush(self, *args, **kargs):
|
||||||
brush = fn.mkBrush(*args, **kargs)
|
brush = fn.mkBrush(*args, **kargs)
|
||||||
if self.opts['fillBrush'] == brush:
|
if self.opts['fillBrush'] == brush:
|
||||||
return
|
return
|
||||||
self.opts['fillBrush'] = brush
|
self.opts['fillBrush'] = brush
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def setBrush(self, *args, **kargs):
|
def setBrush(self, *args, **kargs):
|
||||||
return self.setFillBrush(*args, **kargs)
|
return self.setFillBrush(*args, **kargs)
|
||||||
|
|
||||||
def setFillLevel(self, level):
|
def setFillLevel(self, level):
|
||||||
if self.opts['fillLevel'] == level:
|
if self.opts['fillLevel'] == level:
|
||||||
return
|
return
|
||||||
@ -306,7 +314,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.opts['symbol'] = symbol
|
self.opts['symbol'] = symbol
|
||||||
#self.scatter.setSymbol(symbol)
|
#self.scatter.setSymbol(symbol)
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def setSymbolPen(self, *args, **kargs):
|
def setSymbolPen(self, *args, **kargs):
|
||||||
pen = fn.mkPen(*args, **kargs)
|
pen = fn.mkPen(*args, **kargs)
|
||||||
if self.opts['symbolPen'] == pen:
|
if self.opts['symbolPen'] == pen:
|
||||||
@ -314,9 +322,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.opts['symbolPen'] = pen
|
self.opts['symbolPen'] = pen
|
||||||
#self.scatter.setSymbolPen(pen)
|
#self.scatter.setSymbolPen(pen)
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setSymbolBrush(self, *args, **kargs):
|
def setSymbolBrush(self, *args, **kargs):
|
||||||
brush = fn.mkBrush(*args, **kargs)
|
brush = fn.mkBrush(*args, **kargs)
|
||||||
if self.opts['symbolBrush'] == brush:
|
if self.opts['symbolBrush'] == brush:
|
||||||
@ -324,8 +330,8 @@ class PlotDataItem(GraphicsObject):
|
|||||||
self.opts['symbolBrush'] = brush
|
self.opts['symbolBrush'] = brush
|
||||||
#self.scatter.setSymbolBrush(brush)
|
#self.scatter.setSymbolBrush(brush)
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
|
|
||||||
def setSymbolSize(self, size):
|
def setSymbolSize(self, size):
|
||||||
if self.opts['symbolSize'] == size:
|
if self.opts['symbolSize'] == size:
|
||||||
return
|
return
|
||||||
@ -336,8 +342,8 @@ class PlotDataItem(GraphicsObject):
|
|||||||
def setDownsampling(self, ds=None, auto=None, method=None):
|
def setDownsampling(self, ds=None, auto=None, method=None):
|
||||||
"""
|
"""
|
||||||
Set the downsampling mode of this item. Downsampling reduces the number
|
Set the downsampling mode of this item. Downsampling reduces the number
|
||||||
of samples drawn to increase performance.
|
of samples drawn to increase performance.
|
||||||
|
|
||||||
============== =================================================================
|
============== =================================================================
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
ds (int) Reduce visible plot samples by this factor. To disable,
|
ds (int) Reduce visible plot samples by this factor. To disable,
|
||||||
@ -356,28 +362,46 @@ class PlotDataItem(GraphicsObject):
|
|||||||
if self.opts['downsample'] != ds:
|
if self.opts['downsample'] != ds:
|
||||||
changed = True
|
changed = True
|
||||||
self.opts['downsample'] = ds
|
self.opts['downsample'] = ds
|
||||||
|
|
||||||
if auto is not None and self.opts['autoDownsample'] != auto:
|
if auto is not None and self.opts['autoDownsample'] != auto:
|
||||||
self.opts['autoDownsample'] = auto
|
self.opts['autoDownsample'] = auto
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if method is not None:
|
if method is not None:
|
||||||
if self.opts['downsampleMethod'] != method:
|
if self.opts['downsampleMethod'] != method:
|
||||||
changed = True
|
changed = True
|
||||||
self.opts['downsampleMethod'] = method
|
self.opts['downsampleMethod'] = method
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
self.xDisp = self.yDisp = None
|
self.xDisp = self.yDisp = None
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def setClipToView(self, clip):
|
def setClipToView(self, clip):
|
||||||
if self.opts['clipToView'] == clip:
|
if self.opts['clipToView'] == clip:
|
||||||
return
|
return
|
||||||
self.opts['clipToView'] = clip
|
self.opts['clipToView'] = clip
|
||||||
self.xDisp = self.yDisp = None
|
self.xDisp = self.yDisp = None
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
|
def setDynamicRangeLimit(self, limit):
|
||||||
|
"""
|
||||||
|
Limit the off-screen positions of data points at large magnification
|
||||||
|
This avoids errors with plots not displaying because their visibility is incorrectly determined. The default setting repositions far-off points to be within +-1E+06 times the viewport height.
|
||||||
|
|
||||||
|
=============== ================================================================
|
||||||
|
**Arguments:**
|
||||||
|
limit (float or None) Maximum allowed vertical distance of plotted
|
||||||
|
points in units of viewport height.
|
||||||
|
'None' disables the check for a minimal increase in performance.
|
||||||
|
Default is 1E+06.
|
||||||
|
=============== ================================================================
|
||||||
|
"""
|
||||||
|
if limit == self.opts['dynamicRangeLimit']:
|
||||||
|
return # avoid update if there is no change
|
||||||
|
self.opts['dynamicRangeLimit'] = limit # can be None
|
||||||
|
self.xDisp = self.yDisp = None
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
def setData(self, *args, **kargs):
|
def setData(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
Clear any data displayed by this item and display new data.
|
Clear any data displayed by this item and display new data.
|
||||||
@ -421,7 +445,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
x = data.xvals(0).view(np.ndarray)
|
x = data.xvals(0).view(np.ndarray)
|
||||||
else:
|
else:
|
||||||
raise Exception('Invalid data type %s' % type(data))
|
raise Exception('Invalid data type %s' % type(data))
|
||||||
|
|
||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
seq = ('listOfValues', 'MetaArray', 'empty')
|
seq = ('listOfValues', 'MetaArray', 'empty')
|
||||||
dtyp = dataType(args[0]), dataType(args[1])
|
dtyp = dataType(args[0]), dataType(args[1])
|
||||||
@ -443,45 +467,45 @@ class PlotDataItem(GraphicsObject):
|
|||||||
y = np.array(args[1])
|
y = np.array(args[1])
|
||||||
else:
|
else:
|
||||||
y = args[1].view(np.ndarray)
|
y = args[1].view(np.ndarray)
|
||||||
|
|
||||||
if 'x' in kargs:
|
if 'x' in kargs:
|
||||||
x = kargs['x']
|
x = kargs['x']
|
||||||
if 'y' in kargs:
|
if 'y' in kargs:
|
||||||
y = kargs['y']
|
y = kargs['y']
|
||||||
|
|
||||||
profiler('interpret data')
|
profiler('interpret data')
|
||||||
## pull in all style arguments.
|
## pull in all style arguments.
|
||||||
## Use self.opts to fill in anything not present in kargs.
|
## Use self.opts to fill in anything not present in kargs.
|
||||||
|
|
||||||
if 'name' in kargs:
|
if 'name' in kargs:
|
||||||
self.opts['name'] = kargs['name']
|
self.opts['name'] = kargs['name']
|
||||||
if 'connect' in kargs:
|
if 'connect' in kargs:
|
||||||
self.opts['connect'] = kargs['connect']
|
self.opts['connect'] = kargs['connect']
|
||||||
|
|
||||||
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
||||||
|
|
||||||
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs):
|
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs):
|
||||||
kargs['symbol'] = 'o'
|
kargs['symbol'] = 'o'
|
||||||
|
|
||||||
if 'brush' in kargs:
|
if 'brush' in kargs:
|
||||||
kargs['fillBrush'] = kargs['brush']
|
kargs['fillBrush'] = kargs['brush']
|
||||||
|
|
||||||
for k in list(self.opts.keys()):
|
for k in list(self.opts.keys()):
|
||||||
if k in kargs:
|
if k in kargs:
|
||||||
self.opts[k] = kargs[k]
|
self.opts[k] = kargs[k]
|
||||||
|
|
||||||
#curveArgs = {}
|
#curveArgs = {}
|
||||||
#for k in ['pen', 'shadowPen', 'fillLevel', 'brush']:
|
#for k in ['pen', 'shadowPen', 'fillLevel', 'brush']:
|
||||||
#if k in kargs:
|
#if k in kargs:
|
||||||
#self.opts[k] = kargs[k]
|
#self.opts[k] = kargs[k]
|
||||||
#curveArgs[k] = self.opts[k]
|
#curveArgs[k] = self.opts[k]
|
||||||
|
|
||||||
#scatterArgs = {}
|
#scatterArgs = {}
|
||||||
#for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol')]:
|
#for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol')]:
|
||||||
#if k in kargs:
|
#if k in kargs:
|
||||||
#self.opts[k] = kargs[k]
|
#self.opts[k] = kargs[k]
|
||||||
#scatterArgs[v] = self.opts[k]
|
#scatterArgs[v] = self.opts[k]
|
||||||
|
|
||||||
|
|
||||||
if y is None:
|
if y is None:
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
@ -489,51 +513,53 @@ class PlotDataItem(GraphicsObject):
|
|||||||
return
|
return
|
||||||
if y is not None and x is None:
|
if y is not None and x is None:
|
||||||
x = np.arange(len(y))
|
x = np.arange(len(y))
|
||||||
|
|
||||||
if not isinstance(x, np.ndarray):
|
if not isinstance(x, np.ndarray):
|
||||||
x = np.array(x)
|
x = np.array(x)
|
||||||
if not isinstance(y, np.ndarray):
|
if not isinstance(y, np.ndarray):
|
||||||
y = np.array(y)
|
y = np.array(y)
|
||||||
|
|
||||||
self.xData = x.view(np.ndarray) ## one last check to make sure there are no MetaArrays getting by
|
self.xData = x.view(np.ndarray) ## one last check to make sure there are no MetaArrays getting by
|
||||||
self.yData = y.view(np.ndarray)
|
self.yData = y.view(np.ndarray)
|
||||||
|
self._dataRect = None
|
||||||
self.xClean = self.yClean = None
|
self.xClean = self.yClean = None
|
||||||
self.xDisp = None
|
self.xDisp = None
|
||||||
self.yDisp = None
|
self.yDisp = None
|
||||||
profiler('set data')
|
profiler('set data')
|
||||||
|
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
profiler('update items')
|
profiler('update items')
|
||||||
|
|
||||||
self.informViewBoundsChanged()
|
self.informViewBoundsChanged()
|
||||||
#view = self.getViewBox()
|
#view = self.getViewBox()
|
||||||
#if view is not None:
|
#if view is not None:
|
||||||
#view.itemBoundsChanged(self) ## inform view so it can update its range if it wants
|
#view.itemBoundsChanged(self) ## inform view so it can update its range if it wants
|
||||||
|
|
||||||
self.sigPlotChanged.emit(self)
|
self.sigPlotChanged.emit(self)
|
||||||
profiler('emit')
|
profiler('emit')
|
||||||
|
|
||||||
def updateItems(self):
|
def updateItems(self):
|
||||||
|
|
||||||
curveArgs = {}
|
curveArgs = {}
|
||||||
for k,v in [('pen','pen'), ('shadowPen','shadowPen'), ('fillLevel','fillLevel'), ('fillOutline', 'fillOutline'), ('fillBrush', 'brush'), ('antialias', 'antialias'), ('connect', 'connect'), ('stepMode', 'stepMode')]:
|
for k,v in [('pen','pen'), ('shadowPen','shadowPen'), ('fillLevel','fillLevel'), ('fillOutline', 'fillOutline'), ('fillBrush', 'brush'), ('antialias', 'antialias'), ('connect', 'connect'), ('stepMode', 'stepMode')]:
|
||||||
curveArgs[v] = self.opts[k]
|
curveArgs[v] = self.opts[k]
|
||||||
|
|
||||||
scatterArgs = {}
|
scatterArgs = {}
|
||||||
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol'), ('symbolSize', 'size'), ('data', 'data'), ('pxMode', 'pxMode'), ('antialias', 'antialias')]:
|
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol'), ('symbolSize', 'size'), ('data', 'data'), ('pxMode', 'pxMode'), ('antialias', 'antialias')]:
|
||||||
if k in self.opts:
|
if k in self.opts:
|
||||||
scatterArgs[v] = self.opts[k]
|
scatterArgs[v] = self.opts[k]
|
||||||
|
|
||||||
x,y = self.getData()
|
x,y = self.getData()
|
||||||
#scatterArgs['mask'] = self.dataMask
|
#scatterArgs['mask'] = self.dataMask
|
||||||
|
|
||||||
if curveArgs['pen'] is not None or (curveArgs['brush'] is not None and curveArgs['fillLevel'] is not None):
|
if curveArgs['pen'] is not None or (curveArgs['brush'] is not None and curveArgs['fillLevel'] is not None):
|
||||||
self.curve.setData(x=x, y=y, **curveArgs)
|
self.curve.setData(x=x, y=y, **curveArgs)
|
||||||
self.curve.show()
|
self.curve.show()
|
||||||
else:
|
else:
|
||||||
self.curve.hide()
|
self.curve.hide()
|
||||||
|
|
||||||
if scatterArgs['symbol'] is not None:
|
if scatterArgs['symbol'] is not None:
|
||||||
|
|
||||||
## check against `True` too for backwards compatibility
|
## check against `True` too for backwards compatibility
|
||||||
if self.opts.get('stepMode', False) in ("center", True):
|
if self.opts.get('stepMode', False) in ("center", True):
|
||||||
x = 0.5 * (x[:-1] + x[1:])
|
x = 0.5 * (x[:-1] + x[1:])
|
||||||
@ -546,17 +572,18 @@ class PlotDataItem(GraphicsObject):
|
|||||||
def getData(self):
|
def getData(self):
|
||||||
if self.xData is None:
|
if self.xData is None:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
if self.xDisp is None:
|
if self.xDisp is None:
|
||||||
x = self.xData
|
x = self.xData
|
||||||
y = self.yData
|
y = self.yData
|
||||||
|
|
||||||
if self.opts['fftMode']:
|
if self.opts['fftMode']:
|
||||||
x,y = self._fourierTransform(x, y)
|
x,y = self._fourierTransform(x, y)
|
||||||
# Ignore the first bin for fft data if we have a logx scale
|
# Ignore the first bin for fft data if we have a logx scale
|
||||||
if self.opts['logMode'][0]:
|
if self.opts['logMode'][0]:
|
||||||
x=x[1:]
|
x=x[1:]
|
||||||
y=y[1:]
|
y=y[1:]
|
||||||
|
|
||||||
if self.opts['derivativeMode']: # plot dV/dt
|
if self.opts['derivativeMode']: # plot dV/dt
|
||||||
y = np.diff(self.yData)/np.diff(self.xData)
|
y = np.diff(self.yData)/np.diff(self.xData)
|
||||||
x = x[:-1]
|
x = x[:-1]
|
||||||
@ -569,11 +596,11 @@ class PlotDataItem(GraphicsObject):
|
|||||||
x = np.log10(x)
|
x = np.log10(x)
|
||||||
if self.opts['logMode'][1]:
|
if self.opts['logMode'][1]:
|
||||||
y = np.log10(y)
|
y = np.log10(y)
|
||||||
|
|
||||||
ds = self.opts['downsample']
|
ds = self.opts['downsample']
|
||||||
if not isinstance(ds, int):
|
if not isinstance(ds, int):
|
||||||
ds = 1
|
ds = 1
|
||||||
|
|
||||||
if self.opts['autoDownsample']:
|
if self.opts['autoDownsample']:
|
||||||
# this option presumes that x-values have uniform spacing
|
# this option presumes that x-values have uniform spacing
|
||||||
range = self.viewRect()
|
range = self.viewRect()
|
||||||
@ -586,7 +613,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
if width != 0.0:
|
if width != 0.0:
|
||||||
ds = int(max(1, int((x1-x0) / (width*self.opts['autoDownsampleFactor']))))
|
ds = int(max(1, int((x1-x0) / (width*self.opts['autoDownsampleFactor']))))
|
||||||
## downsampling is expensive; delay until after clipping.
|
## downsampling is expensive; delay until after clipping.
|
||||||
|
|
||||||
if self.opts['clipToView']:
|
if self.opts['clipToView']:
|
||||||
view = self.getViewBox()
|
view = self.getViewBox()
|
||||||
if view is None or not view.autoRangeEnabled()[0]:
|
if view is None or not view.autoRangeEnabled()[0]:
|
||||||
@ -598,8 +625,8 @@ class PlotDataItem(GraphicsObject):
|
|||||||
dx = float(x[-1]-x[0]) / (len(x)-1)
|
dx = float(x[-1]-x[0]) / (len(x)-1)
|
||||||
x0 = np.clip(int((range.left()-x[0])/dx) - 1*ds, 0, len(x)-1)
|
x0 = np.clip(int((range.left()-x[0])/dx) - 1*ds, 0, len(x)-1)
|
||||||
x1 = np.clip(int((range.right()-x[0])/dx) + 2*ds, 0, len(x)-1)
|
x1 = np.clip(int((range.right()-x[0])/dx) + 2*ds, 0, len(x)-1)
|
||||||
|
|
||||||
# if data has been clipped too strongly (in case of non-uniform
|
# if data has been clipped too strongly (in case of non-uniform
|
||||||
# spacing of x-values), refine the clipping region as required
|
# spacing of x-values), refine the clipping region as required
|
||||||
# worst case performance: O(log(n))
|
# worst case performance: O(log(n))
|
||||||
# best case performance: O(1)
|
# best case performance: O(1)
|
||||||
@ -609,10 +636,9 @@ class PlotDataItem(GraphicsObject):
|
|||||||
if x[x1] < range.right():
|
if x[x1] < range.right():
|
||||||
x1 = np.searchsorted(x, range.right()) + 2*ds
|
x1 = np.searchsorted(x, range.right()) + 2*ds
|
||||||
x1 = np.clip(x1, a_min=0, a_max=len(x))
|
x1 = np.clip(x1, a_min=0, a_max=len(x))
|
||||||
|
|
||||||
x = x[x0:x1]
|
x = x[x0:x1]
|
||||||
y = y[x0:x1]
|
y = y[x0:x1]
|
||||||
|
|
||||||
if ds > 1:
|
if ds > 1:
|
||||||
if self.opts['downsampleMethod'] == 'subsample':
|
if self.opts['downsampleMethod'] == 'subsample':
|
||||||
x = x[::ds]
|
x = x[::ds]
|
||||||
@ -631,12 +657,48 @@ class PlotDataItem(GraphicsObject):
|
|||||||
y1[:,0] = y2.max(axis=1)
|
y1[:,0] = y2.max(axis=1)
|
||||||
y1[:,1] = y2.min(axis=1)
|
y1[:,1] = y2.min(axis=1)
|
||||||
y = y1.reshape(n*2)
|
y = y1.reshape(n*2)
|
||||||
|
|
||||||
|
if self.opts['dynamicRangeLimit'] is not None:
|
||||||
|
view_range = self.viewRect()
|
||||||
|
if view_range is not None:
|
||||||
|
data_range = self.dataRect()
|
||||||
|
if data_range is not None:
|
||||||
|
view_height = view_range.height()
|
||||||
|
lim = self.opts['dynamicRangeLimit']
|
||||||
|
if data_range.height() > lim * view_height:
|
||||||
|
min_val = view_range.top() - lim * view_height
|
||||||
|
max_val = view_range.bottom() + lim * view_height
|
||||||
|
y = np.clip(y, a_min=min_val, a_max=max_val)
|
||||||
|
|
||||||
self.xDisp = x
|
self.xDisp = x
|
||||||
self.yDisp = y
|
self.yDisp = y
|
||||||
return self.xDisp, self.yDisp
|
return self.xDisp, self.yDisp
|
||||||
|
|
||||||
|
def dataRect(self):
|
||||||
|
"""
|
||||||
|
Returns a bounding rectangle (as QRectF) for the full set of data.
|
||||||
|
Will return None if there is no data or if all values (x or y) are NaN.
|
||||||
|
"""
|
||||||
|
if self._dataRect is not None:
|
||||||
|
return self._dataRect
|
||||||
|
if self.xData is None or self.yData is None:
|
||||||
|
return None
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# All-NaN data is handled by returning None; Explicit numpy warning is not needed.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
ymin = np.nanmin(self.yData)
|
||||||
|
if np.isnan( ymin ):
|
||||||
|
return None # most likely case for all-NaN data
|
||||||
|
xmin = np.nanmin(self.xData)
|
||||||
|
if np.isnan( xmin ):
|
||||||
|
return None # less likely case for all-NaN data
|
||||||
|
ymax = np.nanmax(self.yData)
|
||||||
|
xmax = np.nanmax(self.xData)
|
||||||
|
self._dataRect = QtCore.QRectF(
|
||||||
|
QtCore.QPointF(xmin,ymin),
|
||||||
|
QtCore.QPointF(xmax,ymax) )
|
||||||
|
return self._dataRect
|
||||||
|
|
||||||
def dataBounds(self, ax, frac=1.0, orthoRange=None):
|
def dataBounds(self, ax, frac=1.0, orthoRange=None):
|
||||||
"""
|
"""
|
||||||
Returns the range occupied by the data (along a specific axis) in this item.
|
Returns the range occupied by the data (along a specific axis) in this item.
|
||||||
@ -645,18 +707,18 @@ class PlotDataItem(GraphicsObject):
|
|||||||
=============== =============================================================
|
=============== =============================================================
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
ax (0 or 1) the axis for which to return this item's data range
|
ax (0 or 1) the axis for which to return this item's data range
|
||||||
frac (float 0.0-1.0) Specifies what fraction of the total data
|
frac (float 0.0-1.0) Specifies what fraction of the total data
|
||||||
range to return. By default, the entire range is returned.
|
range to return. By default, the entire range is returned.
|
||||||
This allows the ViewBox to ignore large spikes in the data
|
This allows the ViewBox to ignore large spikes in the data
|
||||||
when auto-scaling.
|
when auto-scaling.
|
||||||
orthoRange ([min,max] or None) Specifies that only the data within the
|
orthoRange ([min,max] or None) Specifies that only the data within the
|
||||||
given range (orthogonal to *ax*) should me measured when
|
given range (orthogonal to *ax*) should me measured when
|
||||||
returning the data range. (For example, a ViewBox might ask
|
returning the data range. (For example, a ViewBox might ask
|
||||||
what is the y-range of all data with x-values between min
|
what is the y-range of all data with x-values between min
|
||||||
and max)
|
and max)
|
||||||
=============== =============================================================
|
=============== =============================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
range = [None, None]
|
range = [None, None]
|
||||||
if self.curve.isVisible():
|
if self.curve.isVisible():
|
||||||
range = self.curve.dataBounds(ax, frac, orthoRange)
|
range = self.curve.dataBounds(ax, frac, orthoRange)
|
||||||
@ -667,7 +729,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
r2[1] if range[1] is None else (range[1] if r2[1] is None else min(r2[1], range[1]))
|
r2[1] if range[1] is None else (range[1] if r2[1] is None else min(r2[1], range[1]))
|
||||||
]
|
]
|
||||||
return range
|
return range
|
||||||
|
|
||||||
def pixelPadding(self):
|
def pixelPadding(self):
|
||||||
"""
|
"""
|
||||||
Return the size in pixels that this item may draw beyond the values returned by dataBounds().
|
Return the size in pixels that this item may draw beyond the values returned by dataBounds().
|
||||||
@ -679,7 +741,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
elif self.scatter.isVisible():
|
elif self.scatter.isVisible():
|
||||||
pad = max(pad, self.scatter.pixelPadding())
|
pad = max(pad, self.scatter.pixelPadding())
|
||||||
return pad
|
return pad
|
||||||
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
#for i in self.curves+self.scatters:
|
#for i in self.curves+self.scatters:
|
||||||
@ -693,25 +755,29 @@ class PlotDataItem(GraphicsObject):
|
|||||||
#self.yClean = None
|
#self.yClean = None
|
||||||
self.xDisp = None
|
self.xDisp = None
|
||||||
self.yDisp = None
|
self.yDisp = None
|
||||||
|
self._dataRect = None
|
||||||
self.curve.clear()
|
self.curve.clear()
|
||||||
self.scatter.clear()
|
self.scatter.clear()
|
||||||
|
|
||||||
def appendData(self, *args, **kargs):
|
def appendData(self, *args, **kargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def curveClicked(self):
|
def curveClicked(self):
|
||||||
self.sigClicked.emit(self)
|
self.sigClicked.emit(self)
|
||||||
|
|
||||||
def scatterClicked(self, plt, points):
|
def scatterClicked(self, plt, points):
|
||||||
self.sigClicked.emit(self)
|
self.sigClicked.emit(self)
|
||||||
self.sigPointsClicked.emit(self, points)
|
self.sigPointsClicked.emit(self, points)
|
||||||
|
|
||||||
def viewRangeChanged(self):
|
def viewRangeChanged(self):
|
||||||
# view range has changed; re-plot if needed
|
# view range has changed; re-plot if needed
|
||||||
if self.opts['clipToView'] or self.opts['autoDownsample']:
|
if( self.opts['clipToView']
|
||||||
|
or self.opts['autoDownsample']
|
||||||
|
or self.opts['dynamicRangeLimit'] is not None
|
||||||
|
):
|
||||||
self.xDisp = self.yDisp = None
|
self.xDisp = self.yDisp = None
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def _fourierTransform(self, x, y):
|
def _fourierTransform(self, x, y):
|
||||||
## Perform fourier transform. If x values are not sampled uniformly,
|
## Perform fourier transform. If x values are not sampled uniformly,
|
||||||
## then use np.interp to resample before taking fft.
|
## then use np.interp to resample before taking fft.
|
||||||
@ -727,7 +793,7 @@ class PlotDataItem(GraphicsObject):
|
|||||||
x = np.fft.rfftfreq(n, d)
|
x = np.fft.rfftfreq(n, d)
|
||||||
y = np.abs(f)
|
y = np.abs(f)
|
||||||
return x, y
|
return x, y
|
||||||
|
|
||||||
def dataType(obj):
|
def dataType(obj):
|
||||||
if hasattr(obj, '__len__') and len(obj) == 0:
|
if hasattr(obj, '__len__') and len(obj) == 0:
|
||||||
return 'empty'
|
return 'empty'
|
||||||
@ -735,7 +801,7 @@ def dataType(obj):
|
|||||||
return 'dictOfLists'
|
return 'dictOfLists'
|
||||||
elif isSequence(obj):
|
elif isSequence(obj):
|
||||||
first = obj[0]
|
first = obj[0]
|
||||||
|
|
||||||
if (hasattr(obj, 'implements') and obj.implements('MetaArray')):
|
if (hasattr(obj, 'implements') and obj.implements('MetaArray')):
|
||||||
return 'MetaArray'
|
return 'MetaArray'
|
||||||
elif isinstance(obj, np.ndarray):
|
elif isinstance(obj, np.ndarray):
|
||||||
@ -752,13 +818,13 @@ def dataType(obj):
|
|||||||
return 'listOfDicts'
|
return 'listOfDicts'
|
||||||
else:
|
else:
|
||||||
return 'listOfValues'
|
return 'listOfValues'
|
||||||
|
|
||||||
|
|
||||||
def isSequence(obj):
|
def isSequence(obj):
|
||||||
return hasattr(obj, '__iter__') or isinstance(obj, np.ndarray) or (hasattr(obj, 'implements') and obj.implements('MetaArray'))
|
return hasattr(obj, '__iter__') or isinstance(obj, np.ndarray) or (hasattr(obj, 'implements') and obj.implements('MetaArray'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#class TableData:
|
#class TableData:
|
||||||
#"""
|
#"""
|
||||||
#Class for presenting multiple forms of tabular data through a consistent interface.
|
#Class for presenting multiple forms of tabular data through a consistent interface.
|
||||||
@ -768,7 +834,7 @@ def isSequence(obj):
|
|||||||
#- dict-of-lists
|
#- dict-of-lists
|
||||||
#- dict (single record)
|
#- dict (single record)
|
||||||
#Note: if all the values in this record are lists, it will be interpreted as multiple records
|
#Note: if all the values in this record are lists, it will be interpreted as multiple records
|
||||||
|
|
||||||
#Data can be accessed and modified by column, by row, or by value
|
#Data can be accessed and modified by column, by row, or by value
|
||||||
#data[columnName]
|
#data[columnName]
|
||||||
#data[rowId]
|
#data[rowId]
|
||||||
@ -776,7 +842,7 @@ def isSequence(obj):
|
|||||||
#data[columnName] = [value, value, ...]
|
#data[columnName] = [value, value, ...]
|
||||||
#data[rowId] = {columnName: value, ...}
|
#data[rowId] = {columnName: value, ...}
|
||||||
#"""
|
#"""
|
||||||
|
|
||||||
#def __init__(self, data):
|
#def __init__(self, data):
|
||||||
#self.data = data
|
#self.data = data
|
||||||
#if isinstance(data, np.ndarray):
|
#if isinstance(data, np.ndarray):
|
||||||
@ -797,13 +863,13 @@ def isSequence(obj):
|
|||||||
#self.mode = data.mode
|
#self.mode = data.mode
|
||||||
#else:
|
#else:
|
||||||
#raise TypeError(type(data))
|
#raise TypeError(type(data))
|
||||||
|
|
||||||
#for fn in ['__getitem__', '__setitem__']:
|
#for fn in ['__getitem__', '__setitem__']:
|
||||||
#setattr(self, fn, getattr(self, '_TableData'+fn+self.mode))
|
#setattr(self, fn, getattr(self, '_TableData'+fn+self.mode))
|
||||||
|
|
||||||
#def originalData(self):
|
#def originalData(self):
|
||||||
#return self.data
|
#return self.data
|
||||||
|
|
||||||
#def toArray(self):
|
#def toArray(self):
|
||||||
#if self.mode == 'array':
|
#if self.mode == 'array':
|
||||||
#return self.data
|
#return self.data
|
||||||
@ -818,13 +884,13 @@ def isSequence(obj):
|
|||||||
#for i in xrange(1, len(self)):
|
#for i in xrange(1, len(self)):
|
||||||
#arr[i] = tuple(self[i].values())
|
#arr[i] = tuple(self[i].values())
|
||||||
#return arr
|
#return arr
|
||||||
|
|
||||||
#def __getitem__array(self, arg):
|
#def __getitem__array(self, arg):
|
||||||
#if isinstance(arg, tuple):
|
#if isinstance(arg, tuple):
|
||||||
#return self.data[arg[0]][arg[1]]
|
#return self.data[arg[0]][arg[1]]
|
||||||
#else:
|
#else:
|
||||||
#return self.data[arg]
|
#return self.data[arg]
|
||||||
|
|
||||||
#def __getitem__list(self, arg):
|
#def __getitem__list(self, arg):
|
||||||
#if isinstance(arg, basestring):
|
#if isinstance(arg, basestring):
|
||||||
#return [d.get(arg, None) for d in self.data]
|
#return [d.get(arg, None) for d in self.data]
|
||||||
@ -835,7 +901,7 @@ def isSequence(obj):
|
|||||||
#return self.data[arg[0]][arg[1]]
|
#return self.data[arg[0]][arg[1]]
|
||||||
#else:
|
#else:
|
||||||
#raise TypeError(type(arg))
|
#raise TypeError(type(arg))
|
||||||
|
|
||||||
#def __getitem__dict(self, arg):
|
#def __getitem__dict(self, arg):
|
||||||
#if isinstance(arg, basestring):
|
#if isinstance(arg, basestring):
|
||||||
#return self.data[arg]
|
#return self.data[arg]
|
||||||
@ -866,7 +932,7 @@ def isSequence(obj):
|
|||||||
#self.data[arg[0]][arg[1]] = val
|
#self.data[arg[0]][arg[1]] = val
|
||||||
#else:
|
#else:
|
||||||
#raise TypeError(type(arg))
|
#raise TypeError(type(arg))
|
||||||
|
|
||||||
#def __setitem__dict(self, arg, val):
|
#def __setitem__dict(self, arg, val):
|
||||||
#if isinstance(arg, basestring):
|
#if isinstance(arg, basestring):
|
||||||
#if len(val) != len(self.data[arg]):
|
#if len(val) != len(self.data[arg]):
|
||||||
@ -887,7 +953,7 @@ def isSequence(obj):
|
|||||||
#return (args[1], args[0])
|
#return (args[1], args[0])
|
||||||
#else:
|
#else:
|
||||||
#return args
|
#return args
|
||||||
|
|
||||||
#def __iter__(self):
|
#def __iter__(self):
|
||||||
#for i in xrange(len(self)):
|
#for i in xrange(len(self)):
|
||||||
#yield self[i]
|
#yield self[i]
|
||||||
@ -909,6 +975,6 @@ def isSequence(obj):
|
|||||||
#return list(names)
|
#return list(names)
|
||||||
#elif self.mode == 'dict':
|
#elif self.mode == 'dict':
|
||||||
#return self.data.keys()
|
#return self.data.keys()
|
||||||
|
|
||||||
#def keys(self):
|
#def keys(self):
|
||||||
#return self.columnNames()
|
#return self.columnNames()
|
||||||
|
Loading…
Reference in New Issue
Block a user