diff --git a/examples/histogram.py b/examples/histogram.py index 85fbe3f0..53d4fdad 100644 --- a/examples/histogram.py +++ b/examples/histogram.py @@ -20,9 +20,9 @@ vals = np.hstack([np.random.normal(size=500), np.random.normal(size=260, loc=4)] ## compute standard histogram y,x = np.histogram(vals, bins=np.linspace(-3, 8, 40)) -## Using stepMode=True causes the plot to draw two lines for each sample. +## Using stepMode="center" causes the plot to draw two lines for each sample. ## notice that len(x) == len(y)+1 -plt1.plot(x, y, stepMode=True, fillLevel=0, fillOutline=True, brush=(0,0,255,150)) +plt1.plot(x, y, stepMode="center", fillLevel=0, fillOutline=True, brush=(0,0,255,150)) ## Now draw all points as a nicely-spaced scatter plot y = pg.pseudoScatter(vals, spacing=0.15) diff --git a/pyqtgraph/exporters/tests/test_csv.py b/pyqtgraph/exporters/tests/test_csv.py index d6da033b..9cffc64d 100644 --- a/pyqtgraph/exporters/tests/test_csv.py +++ b/pyqtgraph/exporters/tests/test_csv.py @@ -28,7 +28,7 @@ def test_CSVExporter(): y3 = [1,5,2,3,4,6,1,2,4,2,3,5,3] x3 = pg.np.linspace(0, 1.0, len(y3)+1) - plt.plot(x=x3, y=y3, stepMode=True) + plt.plot(x=x3, y=y3, stepMode="center") ex = pg.exporters.CSVExporter(plt.plotItem) ex.export(fileName=tempfilename) diff --git a/pyqtgraph/graphicsItems/PlotCurveItem.py b/pyqtgraph/graphicsItems/PlotCurveItem.py index 0796f52c..f38934bd 100644 --- a/pyqtgraph/graphicsItems/PlotCurveItem.py +++ b/pyqtgraph/graphicsItems/PlotCurveItem.py @@ -62,7 +62,7 @@ class PlotCurveItem(GraphicsObject): 'fillLevel': None, 'fillOutline': False, 'brush': None, - 'stepMode': False, + 'stepMode': None, 'name': None, 'antialias': getConfigOption('antialias'), 'connect': 'all', @@ -315,9 +315,17 @@ class PlotCurveItem(GraphicsObject): by :func:`mkBrush ` is allowed. antialias (bool) Whether to use antialiasing when drawing. This is disabled by default because it decreases performance. - stepMode If True, two orthogonal lines are drawn for each sample - as steps. This is commonly used when drawing histograms. - Note that in this case, len(x) == len(y) + 1 + stepMode (str or None) If "center", a step is drawn using the x + values as boundaries and the given y values are + associated to the mid-points between the boundaries of + each step. This is commonly used when drawing + histograms. Note that in this case, len(x) == len(y) + 1 + If "left" or "right", the step is drawn assuming that + the y value is associated to the left or right boundary, + respectively. In this case len(x) == len(y) + If not passed or an empty string or None is passed, the + step mode is not enabled. + Passing True is a deprecated equivalent to "center". connect Argument specifying how vertexes should be connected by line segments. Default is "all", indicating full connection. "pairs" causes only even-numbered segments @@ -379,7 +387,10 @@ class PlotCurveItem(GraphicsObject): if 'stepMode' in kargs: self.opts['stepMode'] = kargs['stepMode'] - if self.opts['stepMode'] is True: + if self.opts['stepMode'] in ("center", True): ## check against True for backwards compatibility + if self.opts['stepMode'] is True: + import warnings + warnings.warn('stepMode=True is deprecated, use stepMode="center" instead', DeprecationWarning, stacklevel=3) if len(self.xData) != len(self.yData)+1: ## allow difference of 1 for step mode plots raise Exception("len(X) must be len(Y)+1 since stepMode=True (got %s and %s)" % (self.xData.shape, self.yData.shape)) else: @@ -413,10 +424,22 @@ class PlotCurveItem(GraphicsObject): profiler('emit') def generatePath(self, x, y): - if self.opts['stepMode']: + stepMode = self.opts['stepMode'] + if stepMode: ## each value in the x/y arrays generates 2 points. - x2 = np.empty((len(x),2), dtype=x.dtype) - x2[:] = x[:,np.newaxis] + if stepMode == "right": + x2 = np.empty((len(x) + 1, 2), dtype=x.dtype) + x2[:-1] = x[:, np.newaxis] + x2[-1] = x2[-2] + elif stepMode == "left": + x2 = np.empty((len(x) + 1, 2), dtype=x.dtype) + x2[1:] = x[:, np.newaxis] + x2[0] = x2[1] + elif stepMode in ("center", True): ## support True for back-compat + x2 = np.empty((len(x),2), dtype=x.dtype) + x2[:] = x[:, np.newaxis] + else: + raise ValueError("Unsupported stepMode %s" % stepMode) if self.opts['fillLevel'] is None: x = x2.reshape(x2.size)[1:-1] y2 = np.empty((len(y),2), dtype=y.dtype) diff --git a/pyqtgraph/graphicsItems/PlotDataItem.py b/pyqtgraph/graphicsItems/PlotDataItem.py index 4bc5a6d1..449f50a8 100644 --- a/pyqtgraph/graphicsItems/PlotDataItem.py +++ b/pyqtgraph/graphicsItems/PlotDataItem.py @@ -76,9 +76,17 @@ class PlotDataItem(GraphicsObject): fillOutline (bool) If True, an outline surrounding the *fillLevel* area is drawn. fillBrush Fill to use when fillLevel is specified. May be any single argument accepted by :func:`mkBrush() ` - stepMode If True, two orthogonal lines are drawn for each sample - as steps. This is commonly used when drawing histograms. - Note that in this case, ``len(x) == len(y) + 1`` + stepMode (str or None) If "center", a step is drawn using the x + values as boundaries and the given y values are + associated to the mid-points between the boundaries of + each step. This is commonly used when drawing + histograms. Note that in this case, len(x) == len(y) + 1 + If "left" or "right", the step is drawn assuming that + the y value is associated to the left or right boundary, + respectively. In this case len(x) == len(y) + If not passed or an empty string or None is passed, the + step mode is not enabled. + Passing True is a deprecated equivalent to "center". (added in version 0.9.9) ============ ============================================================================== @@ -376,6 +384,12 @@ class PlotDataItem(GraphicsObject): See :func:`__init__() ` for details; it accepts the same arguments. """ #self.clear() + if kargs.get("stepMode", None) is True: + import warnings + warnings.warn( + 'stepMode=True is deprecated, use stepMode="center" instead', + DeprecationWarning, stacklevel=3 + ) profiler = debug.Profiler() y = None x = None @@ -520,8 +534,8 @@ class PlotDataItem(GraphicsObject): self.curve.hide() if scatterArgs['symbol'] is not None: - - if self.opts.get('stepMode', False) is True: + ## check against `True` too for backwards compatibility + if self.opts.get('stepMode', False) in ("center", True): x = 0.5 * (x[:-1] + x[1:]) self.scatter.setData(x=x, y=y, **scatterArgs) self.scatter.show() diff --git a/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py b/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py index 894afc74..926b6098 100644 --- a/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py +++ b/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py @@ -61,7 +61,7 @@ def test_clear(): def test_clear_in_step_mode(): w = pg.PlotWidget() - c = pg.PlotDataItem([1,4,2,3], [5,7,6], stepMode=True) + c = pg.PlotDataItem([1,4,2,3], [5,7,6], stepMode="center") w.addItem(c) c.clear()