From 7860d641ab6395206402cef437de17de5787da7c Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Sat, 22 May 2021 21:43:03 -0700 Subject: [PATCH] Perform finiteCheck on PlotCurveItem.setData The check for NaN values in arrayToQPath is expensive, this change attempts to do the check one time, and if no NaN values are present, an optional flag is passed along telling arrayToQPath that the check can be skipped --- pyqtgraph/functions.py | 4 ++-- pyqtgraph/graphicsItems/PlotCurveItem.py | 19 ++++++++++++---- pyqtgraph/graphicsItems/PlotDataItem.py | 29 +++++++++++++++++++++--- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index db77ea51..8a250df3 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1678,7 +1678,7 @@ def downsample(data, n, axis=0, xvals='subsample'): return MetaArray(d2, info=info) -def arrayToQPath(x, y, connect='all'): +def arrayToQPath(x, y, connect='all', finiteCheck=True): """Convert an array of x,y coordinats to QPainterPath as efficiently as possible. The *connect* argument may be 'all', indicating that each point should be connected to the next; 'pairs', indicating that each pair of points @@ -1735,7 +1735,7 @@ def arrayToQPath(x, y, connect='all'): # Qt version 5.12.3; these must now be manually cleaned out. isfinite = None qtver = [int(x) for x in QtVersion.split('.')] - if qtver >= [5, 12, 3]: + if qtver >= [5, 12, 3] and finiteCheck: isfinite = np.isfinite(x) & np.isfinite(y) if not np.all(isfinite): # credit: Divakar https://stackoverflow.com/a/41191127/643629 diff --git a/pyqtgraph/graphicsItems/PlotCurveItem.py b/pyqtgraph/graphicsItems/PlotCurveItem.py index 06f9bdee..150b755a 100644 --- a/pyqtgraph/graphicsItems/PlotCurveItem.py +++ b/pyqtgraph/graphicsItems/PlotCurveItem.py @@ -65,6 +65,7 @@ class PlotCurveItem(GraphicsObject): 'connect': 'all', 'mouseWidth': 8, # width of shape responding to mouse click 'compositionMode': None, + 'skipFiniteCheck': True } if 'pen' not in kargs: self.opts['pen'] = fn.mkPen('w') @@ -336,6 +337,11 @@ class PlotCurveItem(GraphicsObject): connectivity, specify an array of boolean values. compositionMode See :func:`setCompositionMode `. + skipFiniteCheck Optimization parameter that can speed up plot time by + telling the painter to not check and compensate for NaN + values. If set to True, and NaN values exist, the data + may not be displayed or your plot will take a + significant performance hit. Defaults to False. =============== ======================================================== If non-keyword arguments are used, they will be interpreted as @@ -373,6 +379,7 @@ class PlotCurveItem(GraphicsObject): if data.dtype.kind == 'c': raise Exception("Can not plot complex data types.") + profiler("data checks") #self.setCacheMode(QtGui.QGraphicsItem.NoCache) ## Disabling and re-enabling the cache works around a bug in Qt 4.6 causing the cached results to display incorrectly @@ -421,6 +428,8 @@ class PlotCurveItem(GraphicsObject): if 'antialias' in kargs: self.opts['antialias'] = kargs['antialias'] + self.opts['skipFiniteCheck'] = kargs.get('skipFiniteCheck', False) + profiler('set') self.update() profiler('update') @@ -458,10 +467,12 @@ class PlotCurveItem(GraphicsObject): y[0] = self.opts['fillLevel'] y[-1] = self.opts['fillLevel'] - path = fn.arrayToQPath(x, y, connect=self.opts['connect']) - - return path - + return fn.arrayToQPath( + x, + y, + connect=self.opts['connect'], + finiteCheck=not self.opts['skipFiniteCheck'] + ) def getPath(self): if self.path is None: diff --git a/pyqtgraph/graphicsItems/PlotDataItem.py b/pyqtgraph/graphicsItems/PlotDataItem.py index c36f4ed1..eb883c22 100644 --- a/pyqtgraph/graphicsItems/PlotDataItem.py +++ b/pyqtgraph/graphicsItems/PlotDataItem.py @@ -140,6 +140,11 @@ class PlotDataItem(GraphicsObject): at any time. dynamicRangeLimit (float or None) Limit off-screen positions of data points at large magnification to avoids display errors. Disabled if None. + skipFiniteCheck (bool) Optimization parameter that can speed up plot time by + telling the painter to not check and compensate for NaN + values. If set to True, and NaN values exist, the data + may not be displayed or your plot will take a + significant performance hit. Defaults to False. identical *deprecated* ================= ===================================================================== @@ -210,7 +215,7 @@ class PlotDataItem(GraphicsObject): 'clipToView': False, 'dynamicRangeLimit': 1e6, 'dynamicRangeHyst': 3.0, - + 'skipFiniteCheck': False, 'data': None, } self.setCurveClickable(kargs.get('clickable', False)) @@ -605,11 +610,29 @@ class PlotDataItem(GraphicsObject): scatterArgs = {} if styleUpdate: # repeat style arguments only when changed - 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'), + ('skipFiniteCheck', 'skipFiniteCheck') + ]: if k in self.opts: curveArgs[v] = self.opts[k] - 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: scatterArgs[v] = self.opts[k]