speed up PlotCurveItem fillLevel#2032
Conversation
|
Nice find on this @pijyoi ...would never have guessed that 50 points along the path to be the "optimum" size to break it up by... I was really expecting the |
|
An artificial example that chokes on master (slow to come up with plot, panning not usable) but works fine on this PR. import pyqtgraph as pg
import numpy as np
def gaussian(x, mu, sig):
return np.exp(-np.power(x - mu, 2.) / (2 * np.power(sig, 2.)))
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()
item = pg.PlotCurveItem()
item.setPen(None)
pw.addItem(item)
x = np.linspace(-3, 3, 10001)
mid = (x[:-1] + x[1:])/2
y = gaussian(mid, 0, 1)
y[::2] *= -1
item.setData(x, y, stepMode="center", fillLevel=0, brush='b')
pg.exec() |
Whenever I'm curious about this sort of thing, I typically use github search; only "example" I found with filllevel not being 0 or None was this guy: What this looks like GUI wise, I'm not sure |
|
@goodboy, you have mentioned before on Slack that you use |
|
For import pyqtgraph as pg
import numpy as np
pg.mkQApp()
glw = pg.GraphicsLayoutWidget(show=True)
x = np.arange(12)
y = np.array([10, 9, 8, 7, 6, 5, 6, 7, 8, 9, 10])
PCI = pg.PlotCurveItem
glw.addPlot().addItem(PCI(x, y, stepMode="center", fillLevel=None))
glw.addPlot().addItem(PCI(x, y, stepMode="center", fillLevel=0))
glw.addPlot().addItem(PCI(x, y, stepMode="center", fillLevel=0, brush='b'))
glw.addPlot().addItem(PCI(x, y, stepMode="center", fillLevel=0, brush='b', fillOutline=True))
pg.exec() |
|
I think that further supports 'fillLevel is not None' as the main toggle switch for handling the area under the curve. (Rather than the brush setting.) Having "fill" in the name really helps associate it with that, even if it isn't 100% applicable for some less common applications. It might be good to add a reference to |
I can't really tell if that isn't documenting accidental behavior and making it official...
|
|
Alternative approach: Assume that a future PR might
Then the Turning on the fill would then again require only one argument (since baseline defaults to 0.0, which is the desirable value 90% of the time). If we go in that direction, then the toggle for fill/no fill can be a check for |
|
I think
Emulated options:
|
|
That sounds like a good strategy. I suspect that we would still want an adjustable baseline value even in enclosed mode, unless I misunderstand that mode. As above, Note that this only affects the first-stage handler that adds the lines. For the second stage, the suggested assignments seem like they should work. I believe the numeric Once we know what everything does, we can define and document some aliases :) |
|
I am not confident that it can be easily done without breaking existing behavior (intended or otherwise) after all.
|
|
Add support for fillOutline.
import pyqtgraph as pg
import numpy as np
pg.mkQApp()
glw = pg.GraphicsLayoutWidget(show=True)
x = np.arange(12)
y = np.array([10, 9, 8, 7, 6, 5, 6, 7, 8, 9, 10])
pen = pg.mkPen('w', width=1)
spen = pg.mkPen((0,80,0), width=10)
PCI = pg.PlotCurveItem
glw.addPlot().addItem(PCI(x, y, pen=pen, shadowPen=spen, stepMode="center", fillLevel=None))
glw.addPlot().addItem(PCI(x, y, pen=pen, shadowPen=spen, stepMode="center", fillLevel=0))
glw.addPlot().addItem(PCI(x, y, pen=pen, shadowPen=spen, stepMode="center", fillLevel=0, brush='b'))
glw.addPlot().addItem(PCI(x, y, pen=pen, shadowPen=spen, stepMode="center", fillLevel=0, brush='b', fillOutline=True))
pg.exec() |
|
@pijyoi this is remarkable, also this might be the first time I've seen shadowPen in actual use too. I'll try and set some time aside today to break this. The example you give is pretty good, and I'm wondering if we should incorporate it into one of the examples. EDIT: just a note on the example, we should probably not merge red and green in such a way, they can be difficult to distinguish for individuals that are colorblind. |
Thanks, I have modified it. The example is less an example of how to use and more a check that this PR is maintaining prior behavior (intended or otherwise). |
That's completely fair. My broader point is I don't think the naming of these options is not clear what the result should look like, I think this is a use-case where just a blurb in the docs in insufficient, and where a picture speaks a thousand words... Added benefit is that they can be checks when making changes (perhaps we should add this to one images in the test suite? |
|
I agree that having plots in the documentation would help in illustrating their use. The issue I have is that there is an inconsistency between stepMode and non-stepMode as shown in the script below. So it might be a good idea to hold off the documentation till we have a more consistent api as discussed in #2032 (comment) Interestingly (for me), the bottom-row, 2nd-col plot shows that import pyqtgraph as pg
import numpy as np
pg.mkQApp()
glw = pg.GraphicsLayoutWidget(show=True)
x = np.arange(12)
y = np.array([10, 9, 8, 7, 6, 5, 6, 7, 8, 9, 10])
xc = (x[1:] + x[:-1])/2
pen = pg.mkPen('w', width=1)
spen = pg.mkPen((0,80,0), width=10)
PCI = pg.PlotCurveItem
common = dict(pen=pen, shadowPen=spen, stepMode="center")
glw.addPlot().addItem(PCI(x, y, **common, fillLevel=None))
glw.addPlot().addItem(PCI(x, y, **common, fillLevel=0))
glw.addPlot().addItem(PCI(x, y, **common, fillLevel=0, brush='b'))
glw.addPlot().addItem(PCI(x, y, **common, fillLevel=0, brush='b', fillOutline=True))
glw.nextRow()
common = dict(pen=pen, shadowPen=spen)
glw.addPlot().addItem(PCI(xc, y, **common, fillLevel=None))
glw.addPlot().addItem(PCI(xc, y, **common, fillLevel=0))
glw.addPlot().addItem(PCI(xc, y, **common, fillLevel=0, brush='b'))
glw.addPlot().addItem(PCI(xc, y, **common, fillLevel=0, brush='b', fillOutline=True))
pg.exec() |
|
It seems strangely unsatisfying that the top and bottom version of the third plot (filled, no outline) come out differently in terms of the vertical edge lines. @pijyoi, can your code give us direct access to switch if these lines are plotted not? An example use might be a display for digital I/O signals? A vertical line at start/end might then falsely imply a transition. (I admit that that is not a great example, and I'd just use the current version anyway.) There would be a nice sense of symmetry if the lines could also be manually turned on for the non-stepped plot... I can't currently come up with a reasonable example for that, but my impression from From an API perspective, I still think the edge line switching could be done through the existing But this is all nit-picking. If you don't see a way to handle this in an easy AND satisfying way, then I am good with the functionality as you have it now. Documentation-wise, I think it might be acceptable to merge the functionality first, since the code seems to provide the same output with the same parameter settings in all typical use cases. I might be able to help with a documentation path after that, but I'd prefer not to add any confusion to the code updates before then. @j9ac9k , what do you think is the best merging procedure? |
|
So the thing is that PlotCurveItem is suitable for plotting series data (where the x-ordinate increases monotonically), but it can also be used for plotting arbitrary (x, y) and in particular can be used for drawing closed shapes. From this point of view, Even if the current API may seem non-orthogonal, there are users using
So this would also be redundant for the same reason.
It's not easy because the existing behavior is like this:
So Definitely not satisfying... As for adding more API: https://xkcd.com/927/ |
|
(Idle API discussion) (Main PR discussion)
Then our long-term strategy might indeed be to find a convenient way to move the entire step mode system out of Once we figure something out that is good enough to justify the "one more standard" syndrome, we would slowly move to deprecate having this stepmode functionality hard-coded into PlotCurveItem. In that case, are there still open issues to be solved? |
|
FWIW we ended up implementing a version of the I'm very interested in seeing if these improvements can be swapped in. |
I found an in-library usage that assumes the above: |
I need some kind of chrome extension for obscuring xkcd urls as I knew which one this was from the number alone.
I suspect this is probably what we would want (independent of the HistogramLUTItem; but having that be in practice elsewhere certainly helps). |



To see it in action, run
examples/PlotSpeedTest.pyand checkfill. Compared to master, enablingfillwill no longer be catastrophically slow.What's not supported (i.e. falls back to old existing code):
fillOutline = Truethis option seems only useful for histograms?CLARIFICATION: filling using the brush will take the fast path, it's drawing the outline using the pen when fillOutline=True that is unchanged.o
i.e. if the pen is a thick pen, it does not take the thick pen fast path implemented in experimental line drawing mode for thick lines #2011Help needed for testing it out.
Implementation details:
fillPathhas catastrophic slowdown when the path gets complex. i.e. the slowdown does not appear to be proportional to length. So this PR breaks up the filling into smaller chunks.