diff --git a/doc/api/next_api_changes/deprecations.rst b/doc/api/next_api_changes/deprecations.rst index 63ad76a5762a..987024602da6 100644 --- a/doc/api/next_api_changes/deprecations.rst +++ b/doc/api/next_api_changes/deprecations.rst @@ -264,3 +264,12 @@ method consistent across all artists; thus, additional parameters to ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This method is deprecated. `.DraggableBase.on_motion` now handles both the blitting and the non-blitting cases. + +Passing the dash offset as None +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Fine control of dash patterns can be achieved by passing an ``(offset, +(on-length, off-length, on-length, off-length, ...))`` pair as the linestyle +property of `.Line2D` and `.LineCollection`. Previously, certain APIs would +accept ``offset = None`` as a synonym for ``offset = 0``, but this was never +universally implemented, e.g. for vector output. Support for ``offset = None`` +is deprecated, set the offset to 0 instead. diff --git a/examples/text_labels_and_annotations/legend_demo.py b/examples/text_labels_and_annotations/legend_demo.py index 20fd9325993b..f90297b87c8d 100644 --- a/examples/text_labels_and_annotations/legend_demo.py +++ b/examples/text_labels_and_annotations/legend_demo.py @@ -153,7 +153,7 @@ def create_artists(self, legend, orig_handle, lw = orig_handle.get_linewidths()[i] except IndexError: lw = orig_handle.get_linewidths()[0] - if dashes[0] is not None: + if dashes[1] is not None: legline.set_dashes(dashes[1]) legline.set_color(color) legline.set_transform(trans) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index d77d2aaeaf36..b17da96dbee9 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -711,7 +711,7 @@ def __init__(self): self._capstyle = 'butt' self._cliprect = None self._clippath = None - self._dashes = None, None + self._dashes = 0, None self._joinstyle = 'round' self._linestyle = 'solid' self._linewidth = 1 diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 94b1ce1ed91b..ea86b3f5a85f 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -115,9 +115,9 @@ def __init__(self, cm.ScalarMappable.__init__(self, norm, cmap) # list of un-scaled dash patterns # this is needed scaling the dash pattern by linewidth - self._us_linestyles = [(None, None)] + self._us_linestyles = [(0, None)] # list of dash patterns - self._linestyles = [(None, None)] + self._linestyles = [(0, None)] # list of unbroadcast/scaled linewidths self._us_lw = [0] self._linewidths = [0] @@ -333,7 +333,7 @@ def draw(self, renderer): if (len(paths) == 1 and len(trans) <= 1 and len(facecolors) == 1 and len(edgecolors) == 1 and len(self._linewidths) == 1 and - self._linestyles == [(None, None)] and + all(ls[1] is None for ls in self._linestyles) and len(self._antialiaseds) == 1 and len(self._urls) == 1 and self.get_hatch() is None): if len(trans): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index fd6f103a2c0d..3b2b71807313 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -38,7 +38,8 @@ def _get_dash_pattern(style): style = ls_mapper.get(style, style) # un-dashed styles if style in ['solid', 'None']: - offset, dashes = None, None + offset = 0 + dashes = None # dashed styles elif style in ['dashed', 'dashdot', 'dotted']: offset = 0 @@ -46,11 +47,17 @@ def _get_dash_pattern(style): # elif isinstance(style, tuple): offset, dashes = style + if offset is None: + cbook.warn_deprecated( + "3.3", message="Passing the dash offset as None is deprecated " + "since %(since)s and support for it will be removed " + "%(removal)s; pass it as zero instead.") + offset = 0 else: raise ValueError('Unrecognized linestyle: %s' % str(style)) # normalize offset to be positive and shorter than the dash cycle - if dashes is not None and offset is not None: + if dashes is not None: dsum = sum(dashes) if dsum: offset %= dsum @@ -61,14 +68,9 @@ def _get_dash_pattern(style): def _scale_dashes(offset, dashes, lw): if not rcParams['lines.scale_dashes']: return offset, dashes - - scaled_offset = scaled_dashes = None - if offset is not None: - scaled_offset = offset * lw - if dashes is not None: - scaled_dashes = [x * lw if x is not None else None - for x in dashes] - + scaled_offset = offset * lw + scaled_dashes = ([x * lw if x is not None else None for x in dashes] + if dashes is not None else None) return scaled_offset, scaled_dashes diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 9eefcccdcbc7..d9b3b01571d3 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -579,12 +579,18 @@ def _is_iterable_not_string_like(x): and _is_iterable_not_string_like(ls[1]) and len(ls[1]) % 2 == 0 and all(isinstance(elem, Number) for elem in ls[1])): + if ls[0] is None: + cbook.warn_deprecated( + "3.3", message="Passing the dash offset as None is deprecated " + "since %(since)s and support for it will be removed " + "%(removal)s; pass it as zero instead.") + ls = (0, ls[1]) return ls # For backcompat: (on, off, on, off, ...); the offset is implicitly None. if (_is_iterable_not_string_like(ls) and len(ls) % 2 == 0 and all(isinstance(elem, Number) for elem in ls)): - return (None, ls) + return (0, ls) raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 75cdbf0ce7c5..59aabf0fae98 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -77,7 +77,7 @@ def test__EventCollection__get_props(): # check that the default lineoffset matches the input lineoffset assert props['lineoffset'] == coll.get_lineoffset() # check that the default linestyle matches the input linestyle - assert coll.get_linestyle() == [(None, None)] + assert coll.get_linestyle() == [(0, None)] # check that the default color matches the input color for color in [coll.get_color(), *coll.get_colors()]: np.testing.assert_array_equal(color, props['color']) @@ -501,11 +501,11 @@ def test_lslw_bcast(): col.set_linestyles(['-', '-']) col.set_linewidths([1, 2, 3]) - assert col.get_linestyles() == [(None, None)] * 6 + assert col.get_linestyles() == [(0, None)] * 6 assert col.get_linewidths() == [1, 2, 3] * 2 col.set_linestyles(['-', '-', '-']) - assert col.get_linestyles() == [(None, None)] * 3 + assert col.get_linestyles() == [(0, None)] * 3 assert (col.get_linewidths() == [1, 2, 3]).all() diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index d68e2a94f693..35a17ffc9c41 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -370,10 +370,9 @@ def generate_validator_testcases(valid): ('', ''), (' ', ' '), ('None', 'none'), ('none', 'none'), ('DoTtEd', 'dotted'), # case-insensitive - ('1, 3', (None, (1, 3))), - ([1.23, 456], (None, [1.23, 456.0])), - ([1, 2, 3, 4], (None, [1.0, 2.0, 3.0, 4.0])), - ((None, [1, 2]), (None, [1, 2])), + ('1, 3', (0, (1, 3))), + ([1.23, 456], (0, [1.23, 456.0])), + ([1, 2, 3, 4], (0, [1.0, 2.0, 3.0, 4.0])), ((0, [1, 2]), (0, [1, 2])), ((-1, [1, 2]), (-1, [1, 2])), ), diff --git a/src/py_converters.cpp b/src/py_converters.cpp index 03fdb5845bef..b5e88dd0b2b5 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -237,6 +237,14 @@ int convert_dashes(PyObject *dashobj, void *dashesp) if (PyErr_Occurred()) { return 0; } + } else { + if (PyErr_WarnEx(PyExc_FutureWarning, + "Passing the dash offset as None is deprecated since " + "Matplotlib 3.3 and will be removed in Matplotlib 3.5; " + "pass it as zero instead.", + 1)) { + return 0; + } } if (dashes_seq == Py_None) {