From 7016d1c6c36a4c9a0052f5d2f4c6d2485124e651 Mon Sep 17 00:00:00 2001 From: zhujun98 Date: Sun, 28 Jun 2020 21:23:06 +0200 Subject: [PATCH 1/2] Fix arrayToPath Use the correct format for streaming QByteArray to QPainterPath. --- pyqtgraph/functions.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 75318bbc..2147fb86 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1471,12 +1471,14 @@ def arrayToQPath(x, y, connect='all'): ## Speed this up using >> operator ## Format is: - ## numVerts(i4) 0(i4) - ## x(f8) y(f8) 0(i4) <-- 0 means this vertex does not connect - ## x(f8) y(f8) 1(i4) <-- 1 means this vertex connects to the previous vertex + ## numVerts(i4) + ## 0(i4) x(f8) y(f8) <-- 0 means this vertex does not connect + ## 1(i4) x(f8) y(f8) <-- 1 means this vertex connects to the previous vertex ## ... - ## 0(i4) + ## cStart(i4) fillRule(i4) ## + ## see: https://github.com/qt/qtbase/blob/dev/src/gui/painting/qpainterpath.cpp + ## All values are big endian--pack using struct.pack('>d') or struct.pack('>i') path = QtGui.QPainterPath() @@ -1484,12 +1486,12 @@ def arrayToQPath(x, y, connect='all'): #profiler = debug.Profiler() n = x.shape[0] # create empty array, pad with extra space on either end - arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')]) + arr = np.empty(n+2, dtype=[('c', '>i4'), ('x', '>f8'), ('y', '>f8')]) # write first two integers #profiler('allocate empty') byteview = arr.view(dtype=np.ubyte) - byteview[:12] = 0 - byteview.data[12:20] = struct.pack('>ii', n, 0) + byteview[:16] = 0 + byteview.data[16:20] = struct.pack('>i', n) #profiler('pack header') # Fill array with vertex values arr[1:-1]['x'] = x @@ -1508,17 +1510,18 @@ def arrayToQPath(x, y, connect='all'): else: raise Exception('connect argument must be "all", "pairs", "finite", or array') + arr[1]['c'] = 0 # the first vertex has no previous vertex to connect + #profiler('fill array') - # write last 0 - lastInd = 20*(n+1) - byteview.data[lastInd:lastInd+4] = struct.pack('>i', 0) + byteview.data[-20:-16] = struct.pack('>i', 0) # cStart + byteview.data[-16:-12] = struct.pack('>i', 0) # fillRule (Qt.OddEvenFill) #profiler('footer') # create datastream object and stream into path ## Avoiding this method because QByteArray(str) leaks memory in PySide #buf = QtCore.QByteArray(arr.data[12:lastInd+4]) # I think one unnecessary copy happens here - path.strn = byteview.data[12:lastInd+4] # make sure data doesn't run away + path.strn = byteview.data[16:-12] # make sure data doesn't run away try: buf = QtCore.QByteArray.fromRawData(path.strn) except TypeError: From b61c7c1e39dbaf81071b70ce696811ff76dfd582 Mon Sep 17 00:00:00 2001 From: zhujun98 Date: Mon, 29 Jun 2020 23:17:47 +0200 Subject: [PATCH 2/2] Fix the cases with connect being 'pairs' and 'finite' --- pyqtgraph/functions.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 2147fb86..98cf301e 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1501,10 +1501,14 @@ def arrayToQPath(x, y, connect='all'): if eq(connect, 'all'): arr[1:-1]['c'] = 1 elif eq(connect, 'pairs'): - arr[1:-1]['c'][::2] = 1 - arr[1:-1]['c'][1::2] = 0 + arr[1:-1]['c'][::2] = 0 + arr[1:-1]['c'][1::2] = 1 # connect every 2nd point to every 1st one elif eq(connect, 'finite'): - arr[1:-1]['c'] = np.isfinite(x) & np.isfinite(y) + # Let's call a point with either x or y being nan is an invalid point. + # A point will anyway not connect to an invalid point regardless of the + # 'c' value of the invalid point. Therefore, we should set 'c' to 0 for + # the next point of an invalid point. + arr[2:]['c'] = np.isfinite(x) & np.isfinite(y) elif isinstance(connect, np.ndarray): arr[1:-1]['c'] = connect else: