arrayToQPath can handle empty paths (#1920)
* Fixes #1888 * Improve test coverage of arrayToQPath * Use early exit to solve empty path instead of slice manipulation * address codeql qualms: Unused import, uneven tuple
This commit is contained in:
parent
d0961cc320
commit
e18d1fb1f2
@ -1925,6 +1925,8 @@ def arrayToQPath(x, y, connect='all', finiteCheck=True):
|
|||||||
|
|
||||||
path = QtGui.QPainterPath()
|
path = QtGui.QPainterPath()
|
||||||
n = x.shape[0]
|
n = x.shape[0]
|
||||||
|
if n == 0:
|
||||||
|
return path
|
||||||
|
|
||||||
connect_array = None
|
connect_array = None
|
||||||
if isinstance(connect, np.ndarray):
|
if isinstance(connect, np.ndarray):
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from contextlib import suppress
|
|
||||||
from pyqtgraph.functions import arrayToQPath, eq
|
from pyqtgraph.functions import arrayToQPath, eq
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -264,20 +263,44 @@ def test_CIELab_reconversion():
|
|||||||
|
|
||||||
MoveToElement = pg.QtGui.QPainterPath.ElementType.MoveToElement
|
MoveToElement = pg.QtGui.QPainterPath.ElementType.MoveToElement
|
||||||
LineToElement = pg.QtGui.QPainterPath.ElementType.LineToElement
|
LineToElement = pg.QtGui.QPainterPath.ElementType.LineToElement
|
||||||
|
_dtypes = []
|
||||||
|
for bits in 32, 64:
|
||||||
|
for base in 'int', 'float', 'uint':
|
||||||
|
_dtypes.append(f'{base}{bits}')
|
||||||
|
_dtypes.extend(['uint8', 'uint16'])
|
||||||
|
|
||||||
|
def _handle_underflow(dtype, *elements):
|
||||||
|
"""Wrapper around path description which converts underflow into proper points"""
|
||||||
|
out = []
|
||||||
|
for el in elements:
|
||||||
|
newElement = [el[0]]
|
||||||
|
for ii in range(1, 3):
|
||||||
|
coord = el[ii]
|
||||||
|
if coord < 0:
|
||||||
|
coord = np.array(coord, dtype=dtype)
|
||||||
|
newElement.append(float(coord))
|
||||||
|
out.append(tuple(newElement))
|
||||||
|
return out
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"xs, ys, connect, expected", [
|
"xs, ys, connect, expected", [
|
||||||
|
*(
|
||||||
(
|
(
|
||||||
np.arange(6), np.arange(0, -6, step=-1), 'all', (
|
np.arange(6, dtype=dtype), np.arange(0, -6, step=-1, dtype=dtype), 'all',
|
||||||
|
_handle_underflow(dtype,
|
||||||
(MoveToElement, 0.0, 0.0),
|
(MoveToElement, 0.0, 0.0),
|
||||||
(LineToElement, 1.0, -1.0),
|
(LineToElement, 1.0, -1.0),
|
||||||
(LineToElement, 2.0, -2.0),
|
(LineToElement, 2.0, -2.0),
|
||||||
(LineToElement, 3.0, -3.0),
|
(LineToElement, 3.0, -3.0),
|
||||||
(LineToElement, 4.0, -4.0),
|
(LineToElement, 4.0, -4.0),
|
||||||
(LineToElement, 5.0, -5.0),
|
(LineToElement, 5.0, -5.0)
|
||||||
)
|
)
|
||||||
|
) for dtype in _dtypes
|
||||||
),
|
),
|
||||||
|
*(
|
||||||
(
|
(
|
||||||
np.arange(6), np.arange(0, -6, step=-1), 'pairs', (
|
np.arange(6, dtype=dtype), np.arange(0, -6, step=-1, dtype=dtype), 'pairs',
|
||||||
|
_handle_underflow(dtype,
|
||||||
(MoveToElement, 0.0, 0.0),
|
(MoveToElement, 0.0, 0.0),
|
||||||
(LineToElement, 1.0, -1.0),
|
(LineToElement, 1.0, -1.0),
|
||||||
(MoveToElement, 2.0, -2.0),
|
(MoveToElement, 2.0, -2.0),
|
||||||
@ -285,16 +308,21 @@ LineToElement = pg.QtGui.QPainterPath.ElementType.LineToElement
|
|||||||
(MoveToElement, 4.0, -4.0),
|
(MoveToElement, 4.0, -4.0),
|
||||||
(LineToElement, 5.0, -5.0),
|
(LineToElement, 5.0, -5.0),
|
||||||
)
|
)
|
||||||
|
) for dtype in _dtypes
|
||||||
),
|
),
|
||||||
|
*(
|
||||||
(
|
(
|
||||||
np.arange(5), np.arange(0, -5, step=-1), 'pairs', (
|
np.arange(5, dtype=dtype), np.arange(0, -5, step=-1, dtype=dtype), 'pairs',
|
||||||
|
_handle_underflow(dtype,
|
||||||
(MoveToElement, 0.0, 0.0),
|
(MoveToElement, 0.0, 0.0),
|
||||||
(LineToElement, 1.0, -1.0),
|
(LineToElement, 1.0, -1.0),
|
||||||
(MoveToElement, 2.0, -2.0),
|
(MoveToElement, 2.0, -2.0),
|
||||||
(LineToElement, 3.0, -3.0),
|
(LineToElement, 3.0, -3.0),
|
||||||
(MoveToElement, 4.0, -4.0)
|
(MoveToElement, 4.0, -4.0)
|
||||||
)
|
)
|
||||||
|
) for dtype in _dtypes
|
||||||
),
|
),
|
||||||
|
# NaN types don't coerce to integers, don't test for all types since that doesn't make sense
|
||||||
(
|
(
|
||||||
np.arange(5), np.array([0, -1, np.NaN, -3, -4]), 'finite', (
|
np.arange(5), np.array([0, -1, np.NaN, -3, -4]), 'finite', (
|
||||||
(MoveToElement, 0.0, 0.0),
|
(MoveToElement, 0.0, 0.0),
|
||||||
@ -313,24 +341,33 @@ LineToElement = pg.QtGui.QPainterPath.ElementType.LineToElement
|
|||||||
(LineToElement, 4.0, -4.0)
|
(LineToElement, 4.0, -4.0)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
*(
|
||||||
(
|
(
|
||||||
np.arange(5), np.arange(0, -5, step=-1), np.array([0, 1, 0, 1, 0]), (
|
np.arange(5, dtype=dtype), np.arange(0, -5, step=-1, dtype=dtype), np.array([0, 1, 0, 1, 0]),
|
||||||
|
_handle_underflow(dtype,
|
||||||
(MoveToElement, 0.0, 0.0),
|
(MoveToElement, 0.0, 0.0),
|
||||||
(MoveToElement, 1.0, -1.0),
|
(MoveToElement, 1.0, -1.0),
|
||||||
(LineToElement, 2.0, -2.0),
|
(LineToElement, 2.0, -2.0),
|
||||||
(MoveToElement, 3.0, -3.0),
|
(MoveToElement, 3.0, -3.0),
|
||||||
(LineToElement, 4.0, -4.0)
|
(LineToElement, 4.0, -4.0)
|
||||||
)
|
)
|
||||||
)
|
) for dtype in _dtypes
|
||||||
|
),
|
||||||
|
# Empty path with all types of connection
|
||||||
|
*(
|
||||||
|
(
|
||||||
|
np.arange(0), np.arange(0, dtype=dtype), conn, ()
|
||||||
|
) for conn in ['all', 'pairs', 'finite', np.array([])] for dtype in _dtypes
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_arrayToQPath(xs, ys, connect, expected):
|
def test_arrayToQPath(xs, ys, connect, expected):
|
||||||
path = arrayToQPath(xs, ys, connect=connect)
|
path = arrayToQPath(xs, ys, connect=connect)
|
||||||
|
element = None
|
||||||
for i in range(path.elementCount()):
|
for i in range(path.elementCount()):
|
||||||
with suppress(NameError):
|
|
||||||
# nan elements add two line-segments, for simplicity of test config
|
# nan elements add two line-segments, for simplicity of test config
|
||||||
# we can ignore the second segment
|
# we can ignore the second segment
|
||||||
if (eq(element.x, np.nan) or eq(element.y, np.nan)):
|
if element is not None and (eq(element.x, np.nan) or eq(element.y, np.nan)):
|
||||||
continue
|
continue
|
||||||
element = path.elementAt(i)
|
element = path.elementAt(i)
|
||||||
assert eq(expected[i], (element.type, element.x, element.y))
|
assert eq(expected[i], (element.type, element.x, element.y))
|
||||||
|
Loading…
Reference in New Issue
Block a user