Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3200ff9

Browse filesBrowse files
committed
GSOD: LineStyle class
1 parent 69ecf1c commit 3200ff9
Copy full SHA for 3200ff9

File tree

6 files changed

+337
-274
lines changed
Filter options

6 files changed

+337
-274
lines changed

‎examples/lines_bars_and_markers/linestyles.py

Copy file name to clipboardExpand all lines: examples/lines_bars_and_markers/linestyles.py
+23-69Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,74 +3,28 @@
33
Linestyles
44
==========
55
6-
Simple linestyles can be defined using the strings "solid", "dotted", "dashed"
7-
or "dashdot". More refined control can be achieved by providing a dash tuple
8-
``(offset, (on_off_seq))``. For example, ``(0, (3, 10, 1, 15))`` means
9-
(3pt line, 10pt space, 1pt line, 15pt space) with no offset. See also
10-
`.Line2D.set_linestyle`.
11-
12-
*Note*: The dash style can also be configured via `.Line2D.set_dashes`
13-
as shown in :doc:`/gallery/lines_bars_and_markers/line_demo_dash_control`
14-
and passing a list of dash sequences using the keyword *dashes* to the
15-
cycler in :doc:`property_cycle </tutorials/intermediate/color_cycle>`.
6+
The Matplotlib `~mpl._enums.LineStyle` specifies the dash pattern used to draw
7+
a given line. The simplest line styles can be accessed by name using the
8+
strings "solid", "dotted", "dashed" and "dashdot" (or their short names, "-",
9+
":", "--", and "-.", respectively).
10+
11+
The exact spacing of the dashes used can be controlled using the
12+
'lines.*_pattern' family of rc parameters. For example,
13+
:rc:`lines.dashdot_pattern` controls the exact spacing of dashed used whenever
14+
the '-.' `~mpl._enums.LineStyle` is specified.
15+
16+
For more information about how to create custom `~mpl._enums.LineStyle`
17+
specifications, see `the LineStyle docs <mpl._enums.LineStyle>`.
18+
19+
*Note*: For historical reasons, one can also specify the dash pattern for a
20+
particular line using `.Line2D.set_dashes` as shown in
21+
:doc:`/gallery/lines_bars_and_markers/line_demo_dash_control` (or by passing a
22+
list of dash sequences using the keyword *dashes* to the cycler in
23+
:doc:`property_cycle </tutorials/intermediate/color_cycle>`). This interface is
24+
strictly less expressive, and we recommend using LineStyle (or the keyword
25+
*linestyle* to the :doc:`property cycler
26+
</tutorials/intermediate/color_cycle>`).
1627
"""
17-
import numpy as np
18-
import matplotlib.pyplot as plt
19-
20-
linestyle_str = [
21-
('solid', 'solid'), # Same as (0, ()) or '-'
22-
('dotted', 'dotted'), # Same as (0, (1, 1)) or '.'
23-
('dashed', 'dashed'), # Same as '--'
24-
('dashdot', 'dashdot')] # Same as '-.'
25-
26-
linestyle_tuple = [
27-
('loosely dotted', (0, (1, 10))),
28-
('dotted', (0, (1, 1))),
29-
('densely dotted', (0, (1, 1))),
30-
31-
('loosely dashed', (0, (5, 10))),
32-
('dashed', (0, (5, 5))),
33-
('densely dashed', (0, (5, 1))),
34-
35-
('loosely dashdotted', (0, (3, 10, 1, 10))),
36-
('dashdotted', (0, (3, 5, 1, 5))),
37-
('densely dashdotted', (0, (3, 1, 1, 1))),
38-
39-
('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))),
40-
('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))),
41-
('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]
42-
43-
44-
def plot_linestyles(ax, linestyles, title):
45-
X, Y = np.linspace(0, 100, 10), np.zeros(10)
46-
yticklabels = []
47-
48-
for i, (name, linestyle) in enumerate(linestyles):
49-
ax.plot(X, Y+i, linestyle=linestyle, linewidth=1.5, color='black')
50-
yticklabels.append(name)
51-
52-
ax.set_title(title)
53-
ax.set(ylim=(-0.5, len(linestyles)-0.5),
54-
yticks=np.arange(len(linestyles)),
55-
yticklabels=yticklabels)
56-
ax.tick_params(left=False, bottom=False, labelbottom=False)
57-
ax.spines[:].set_visible(False)
58-
59-
# For each line style, add a text annotation with a small offset from
60-
# the reference point (0 in Axes coords, y tick value in Data coords).
61-
for i, (name, linestyle) in enumerate(linestyles):
62-
ax.annotate(repr(linestyle),
63-
xy=(0.0, i), xycoords=ax.get_yaxis_transform(),
64-
xytext=(-6, -12), textcoords='offset points',
65-
color="blue", fontsize=8, ha="right", family="monospace")
66-
67-
68-
ax0, ax1 = (plt.figure(figsize=(10, 8))
69-
.add_gridspec(2, 1, height_ratios=[1, 3])
70-
.subplots())
71-
72-
plot_linestyles(ax0, linestyle_str[::-1], title='Named linestyles')
73-
plot_linestyles(ax1, linestyle_tuple[::-1], title='Parametrized linestyles')
28+
from matplotlib._enums import LineStyle
7429

75-
plt.tight_layout()
76-
plt.show()
30+
LineStyle.demo()

‎lib/matplotlib/_enums.py

Copy file name to clipboardExpand all lines: lib/matplotlib/_enums.py
+262-3Lines changed: 262 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"""
1212

1313
from enum import Enum, auto
14-
from matplotlib import cbook, docstring
14+
from numbers import Number
15+
16+
from matplotlib import _api, docstring
1517

1618

1719
class _AutoStringNameEnum(Enum):
@@ -28,12 +30,12 @@ def _deprecate_case_insensitive_join_cap(s):
2830
s_low = s.lower()
2931
if s != s_low:
3032
if s_low in ['miter', 'round', 'bevel']:
31-
cbook.warn_deprecated(
33+
_api.warn_deprecated(
3234
"3.3", message="Case-insensitive capstyles are deprecated "
3335
"since %(since)s and support for them will be removed "
3436
"%(removal)s; please pass them in lowercase.")
3537
elif s_low in ['butt', 'round', 'projecting']:
36-
cbook.warn_deprecated(
38+
_api.warn_deprecated(
3739
"3.3", message="Case-insensitive joinstyles are deprecated "
3840
"since %(since)s and support for them will be removed "
3941
"%(removal)s; please pass them in lowercase.")
@@ -206,3 +208,260 @@ def demo():
206208

207209
docstring.interpd.update({'JoinStyle': JoinStyle.input_description,
208210
'CapStyle': CapStyle.input_description})
211+
212+
213+
#: Maps short codes for line style to their full name used by backends.
214+
_ls_mapper = {'': 'None', ' ': 'None', 'none': 'None',
215+
'-': 'solid', '--': 'dashed', '-.': 'dashdot', ':': 'dotted'}
216+
_deprecated_lineStyles = {
217+
'-': '_draw_solid',
218+
'--': '_draw_dashed',
219+
'-.': '_draw_dash_dot',
220+
':': '_draw_dotted',
221+
'None': '_draw_nothing',
222+
' ': '_draw_nothing',
223+
'': '_draw_nothing',
224+
}
225+
226+
227+
class NamedLineStyle(str, _AutoStringNameEnum):
228+
"""
229+
Describe if the line is solid or dashed, and the dash pattern, if any.
230+
231+
All lines in Matplotlib are considered either solid or "dashed". Some
232+
common dashing patterns are built-in, and are sufficient for a majority of
233+
uses:
234+
235+
=============================== =================
236+
Linestyle Description
237+
=============================== =================
238+
``'-'`` or ``'solid'`` solid line
239+
``'--'`` or ``'dashed'`` dashed line
240+
``'-.'`` or ``'dashdot'`` dash-dotted line
241+
``':'`` or ``'dotted'`` dotted line
242+
``'None'`` or ``' '`` or ``''`` draw nothing
243+
=============================== =================
244+
245+
However, for more fine-grained control, one can directly specify the
246+
dashing pattern by specifying::
247+
248+
(offset, onoffseq)
249+
250+
where ``onoffseq`` is an even length tuple specifying the lengths of each
251+
subsequent dash and space, and ``offset`` controls at which point in this
252+
pattern the start of the line will begin (to allow you to e.g. prevent
253+
corners from happening to land in between dashes).
254+
255+
For example, (5, 2, 1, 2) describes a sequence of 5 point and 1 point
256+
dashes separated by 2 point spaces.
257+
258+
Setting ``onoffseq`` to ``None`` results in a solid *LineStyle*.
259+
260+
The default dashing patterns described in the table above are themselves
261+
all described in this notation, and can therefore be customized by editing
262+
the appropriate ``lines.*_pattern`` *rc* parameter, as described in
263+
:doc:`/tutorials/introductory/customizing`.
264+
265+
.. plot::
266+
:alt: Demo of possible LineStyle's.
267+
268+
from matplotlib._types import LineStyle
269+
LineStyle.demo()
270+
271+
.. note::
272+
273+
In addition to directly taking a ``linestyle`` argument,
274+
`~.lines.Line2D` exposes a ``~.lines.Line2D.set_dashes`` method that
275+
can be used to create a new *LineStyle* by providing just the
276+
``onoffseq``, but does not let you customize the offset. This method is
277+
called when using the keyword *dashes* to the cycler , as shown in
278+
:doc:`property_cycle </tutorials/intermediate/color_cycle>`.
279+
"""
280+
solid = auto()
281+
dashed = auto()
282+
dotted = auto()
283+
dashdot = auto()
284+
none = auto()
285+
custom = auto()
286+
287+
class LineStyle(str):
288+
289+
def __init__(self, ls, scale=1):
290+
"""
291+
Parameters
292+
----------
293+
ls : str or dash tuple
294+
A description of the dashing pattern of the line. Allowed string
295+
inputs are {'-', 'solid', '--', 'dashed', '-.', 'dashdot', ':',
296+
'dotted', '', ' ', 'None', 'none'}. Alternatively, the dash tuple
297+
(``offset``, ``onoffseq``) can be specified directly in points.
298+
scale : float
299+
Uniformly scale the internal dash sequence length by a constant
300+
factor.
301+
"""
302+
303+
self._linestyle_spec = ls
304+
if isinstance(ls, str):
305+
if ls in [' ', '', 'None']:
306+
ls = 'none'
307+
if ls in _ls_mapper:
308+
ls = _ls_mapper[ls]
309+
Enum.__init__(self)
310+
offset, onoffseq = None, None
311+
else:
312+
try:
313+
offset, onoffseq = ls
314+
except ValueError: # not enough/too many values to unpack
315+
raise ValueError('LineStyle should be a string or a 2-tuple, '
316+
'instead received: ' + str(ls))
317+
if offset is None:
318+
_api.warn_deprecated(
319+
"3.3", message="Passing the dash offset as None is deprecated "
320+
"since %(since)s and support for it will be removed "
321+
"%(removal)s; pass it as zero instead.")
322+
offset = 0
323+
324+
if onoffseq is not None:
325+
# normalize offset to be positive and shorter than the dash cycle
326+
dsum = sum(onoffseq)
327+
if dsum:
328+
offset %= dsum
329+
if len(onoffseq) % 2 != 0:
330+
raise ValueError('LineStyle onoffseq must be of even length.')
331+
if not all(isinstance(elem, Number) for elem in onoffseq):
332+
raise ValueError('LineStyle onoffseq must be list of floats.')
333+
self._us_offset = offset
334+
self._us_onoffseq = onoffseq
335+
336+
def __hash__(self):
337+
if self == LineStyle.custom:
338+
return (self._us_offset, tuple(self._us_onoffseq)).__hash__()
339+
return _AutoStringNameEnum.__hash__(self)
340+
341+
342+
def get_dashes(self, lw=1):
343+
"""
344+
Get the (scaled) dash sequence for this `.LineStyle`.
345+
"""
346+
# defer lookup until draw time
347+
if self._us_offset is None or self._us_onoffseq is None:
348+
self._us_offset, self._us_onoffseq = \
349+
LineStyle._get_dash_pattern(self.name)
350+
# normalize offset to be positive and shorter than the dash cycle
351+
dsum = sum(self._us_onoffseq)
352+
self._us_offset %= dsum
353+
return self._scale_dashes(self._us_offset, self._us_onoffseq, lw)
354+
355+
@staticmethod
356+
def _scale_dashes(offset, dashes, lw):
357+
from . import rcParams
358+
if not rcParams['lines.scale_dashes']:
359+
return offset, dashes
360+
scaled_offset = offset * lw
361+
scaled_dashes = ([x * lw if x is not None else None for x in dashes]
362+
if dashes is not None else None)
363+
return scaled_offset, scaled_dashes
364+
365+
@staticmethod
366+
def _get_dash_pattern(style):
367+
"""Convert linestyle string to explicit dash pattern."""
368+
# import must be local for validator code to live here
369+
from . import rcParams
370+
# un-dashed styles
371+
if style in ['solid', 'None']:
372+
offset = 0
373+
dashes = None
374+
# dashed styles
375+
elif style in ['dashed', 'dashdot', 'dotted']:
376+
offset = 0
377+
dashes = tuple(rcParams['lines.{}_pattern'.format(style)])
378+
return offset, dashes
379+
380+
@staticmethod
381+
def from_dashes(seq):
382+
"""
383+
Create a `.LineStyle` from a dash sequence (i.e. the ``onoffseq``).
384+
385+
The dash sequence is a sequence of floats of even length describing
386+
the length of dashes and spaces in points.
387+
388+
Parameters
389+
----------
390+
seq : sequence of floats (on/off ink in points) or (None, None)
391+
If *seq* is empty or ``(None, None)``, the `.LineStyle` will be
392+
solid.
393+
"""
394+
if seq == (None, None) or len(seq) == 0:
395+
return LineStyle('-')
396+
else:
397+
return LineStyle((0, seq))
398+
399+
@staticmethod
400+
def demo():
401+
import numpy as np
402+
import matplotlib.pyplot as plt
403+
404+
linestyle_str = [
405+
('solid', 'solid'), # Same as (0, ()) or '-'
406+
('dotted', 'dotted'), # Same as (0, (1, 1)) or '.'
407+
('dashed', 'dashed'), # Same as '--'
408+
('dashdot', 'dashdot')] # Same as '-.'
409+
410+
linestyle_tuple = [
411+
('loosely dotted', (0, (1, 10))),
412+
('dotted', (0, (1, 1))),
413+
('densely dotted', (0, (1, 1))),
414+
415+
('loosely dashed', (0, (5, 10))),
416+
('dashed', (0, (5, 5))),
417+
('densely dashed', (0, (5, 1))),
418+
419+
('loosely dashdotted', (0, (3, 10, 1, 10))),
420+
('dashdotted', (0, (3, 5, 1, 5))),
421+
('densely dashdotted', (0, (3, 1, 1, 1))),
422+
423+
('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))),
424+
('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))),
425+
('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]
426+
427+
def plot_linestyles(ax, linestyles, title):
428+
X, Y = np.linspace(0, 100, 10), np.zeros(10)
429+
yticklabels = []
430+
431+
for i, (name, linestyle) in enumerate(linestyles):
432+
ax.plot(X, Y+i, linestyle=linestyle, linewidth=1.5,
433+
color='black')
434+
yticklabels.append(name)
435+
436+
ax.set_title(title)
437+
ax.set(ylim=(-0.5, len(linestyles)-0.5),
438+
yticks=np.arange(len(linestyles)),
439+
yticklabels=yticklabels)
440+
ax.tick_params(left=False, bottom=False, labelbottom=False)
441+
for spine in ax.spines.values():
442+
spine.set_visible(False)
443+
444+
# For each line style, add a text annotation with a small offset
445+
# from the reference point (0 in Axes coords, y tick value in Data
446+
# coords).
447+
for i, (name, linestyle) in enumerate(linestyles):
448+
ax.annotate(repr(linestyle),
449+
xy=(0.0, i), xycoords=ax.get_yaxis_transform(),
450+
xytext=(-6, -12), textcoords='offset points',
451+
color="blue", fontsize=8, ha="right",
452+
family="monospace")
453+
454+
ax0, ax1 = (plt.figure(figsize=(10, 8))
455+
.add_gridspec(2, 1, height_ratios=[1, 3])
456+
.subplots())
457+
458+
plot_linestyles(ax0, linestyle_str[::-1], title='Named linestyles')
459+
plot_linestyles(ax1, linestyle_tuple[::-1],
460+
title='Parametrized linestyles')
461+
462+
plt.tight_layout()
463+
plt.show()
464+
465+
466+
LineStyle._ls_mapper = _ls_mapper
467+
LineStyle._deprecated_lineStyles = _deprecated_lineStyles

‎lib/matplotlib/cbook/__init__.py

Copy file name to clipboardExpand all lines: lib/matplotlib/cbook/__init__.py
-6Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,12 +1258,6 @@ def _compute_conf_interval(data, med, iqr, bootstrap):
12581258
return bxpstats
12591259

12601260

1261-
#: Maps short codes for line style to their full name used by backends.
1262-
ls_mapper = {'-': 'solid', '--': 'dashed', '-.': 'dashdot', ':': 'dotted'}
1263-
#: Maps full names for line styles used by backends to their short codes.
1264-
ls_mapper_r = {v: k for k, v in ls_mapper.items()}
1265-
1266-
12671261
def contiguous_regions(mask):
12681262
"""
12691263
Return a list of (ind0, ind1) such that ``mask[ind0:ind1].all()`` is

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.