From 8e13d3ba691b95df5e469875614c32ea8b86eb95 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Wed, 2 Dec 2020 14:40:29 -0800 Subject: [PATCH 1/8] BUGFIX: naked plot directive naively "responsive" --- lib/matplotlib/sphinxext/__init__.py | 3 +++ lib/matplotlib/sphinxext/plot_directive.py | 19 +++++++++++++++---- .../sphinxext/static/plot_directive.css | 13 +++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 lib/matplotlib/sphinxext/static/plot_directive.css diff --git a/lib/matplotlib/sphinxext/__init__.py b/lib/matplotlib/sphinxext/__init__.py index e69de29bb2d1..e994f9258771 100644 --- a/lib/matplotlib/sphinxext/__init__.py +++ b/lib/matplotlib/sphinxext/__init__.py @@ -0,0 +1,3 @@ +from pathlib import Path + +_static_path = Path(__file__).resolve().parent / Path('static') diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 2f29dc00b3c3..7de243f2ead6 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -156,7 +156,7 @@ import matplotlib from matplotlib.backend_bases import FigureManagerBase import matplotlib.pyplot as plt -from matplotlib import _pylab_helpers, cbook +from matplotlib import _pylab_helpers, cbook, sphinxext matplotlib.use("agg") align = cbook.deprecated( @@ -254,6 +254,13 @@ def run(self): raise self.error(str(e)) +def _copy_css_file(app, exc): + if exc is None and app.builder.format == 'html': + src = sphinxext._static_path / Path('plot_directive.css') + dst = app.outdir / Path('_static') + shutil.copy(src, dst) + + def setup(app): setup.app = app setup.config = app.config @@ -269,9 +276,9 @@ def setup(app): app.add_config_value('plot_apply_rcparams', False, True) app.add_config_value('plot_working_directory', None, True) app.add_config_value('plot_template', None, True) - app.connect('doctree-read', mark_plot_labels) - + app.add_css_file('plot_directive.css') + app.connect('build-finished', _copy_css_file) metadata = {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': matplotlib.__version__} return metadata @@ -521,7 +528,7 @@ def render_figures(code, code_path, output_dir, output_base, context, """ formats = get_plot_formats(config) - # -- Try to determine if all images already exist + # Try to determine if all images already exist code_pieces = split_code_at_show(code) @@ -624,6 +631,10 @@ def run(arguments, content, options, state_machine, state, lineno): default_fmt = formats[0][0] options.setdefault('include-source', config.plot_include_source) + if 'class' in options: + options['class'] = options['class'] + ' plot-directive' + else: + options.setdefault('class', 'plot-directive') keep_context = 'context' in options context_opt = None if not keep_context else options['context'] diff --git a/lib/matplotlib/sphinxext/static/plot_directive.css b/lib/matplotlib/sphinxext/static/plot_directive.css new file mode 100644 index 000000000000..dcd95d62713c --- /dev/null +++ b/lib/matplotlib/sphinxext/static/plot_directive.css @@ -0,0 +1,13 @@ +/* + * plot_directive.css + * ~~~~~~~~~~~~ + * + * Stylesheet controlling images created using the `plot` directive within + * Sphinx. + * + */ + +img.plot-directive { + border: 0; + max-width: 100%; +} From af7d34b86efc739b4e00f4c7d37aaa08eda3e056 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Fri, 4 Dec 2020 10:34:47 -0800 Subject: [PATCH 2/8] BF: plot_directive template to allow class+caption --- lib/matplotlib/sphinxext/plot_directive.py | 15 +++++++------- lib/matplotlib/tests/test_sphinxext.py | 4 ++++ lib/matplotlib/tests/tinypages/some_plots.rst | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 7de243f2ead6..857915a43bc6 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -344,7 +344,6 @@ def split_code_at_show(text): # Template # ----------------------------------------------------------------------------- - TEMPLATE = """ {{ source_code }} @@ -379,9 +378,8 @@ def split_code_at_show(text): `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ {%- endfor -%} ) - {%- endif -%} - - {{ caption }} + {%- endif %} +{{ caption }} {# appropriate leading whitespace added beforehand #} {% endfor %} .. only:: not html @@ -392,7 +390,7 @@ def split_code_at_show(text): {{ option }} {% endfor %} - {{ caption }} +{{ caption }} {# appropriate leading whitespace added beforehand #} {% endfor %} """ @@ -632,9 +630,12 @@ def run(arguments, content, options, state_machine, state, lineno): options.setdefault('include-source', config.plot_include_source) if 'class' in options: - options['class'] = options['class'] + ' plot-directive' + # classes are parsed into a list of string, and output by simply + # printing the list, abusing the fact that RST guarantees to strip + # non-conforming characters + options['class'] = ['plot-directive'] + options['class'] else: - options.setdefault('class', 'plot-directive') + options.setdefault('class', ['plot-directive']) keep_context = 'context' in options context_opt = None if not keep_context else options['context'] diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index 3ce8d2951d07..ce5ac319975d 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -57,3 +57,7 @@ def plot_file(num): assert b'Plot 17 uses the caption option.' in html_contents # check if figure caption made it into html file assert b'This is the caption for plot 18.' in html_contents + # check if the custom classes made it into the html file + assert b'plot-directive my-class my-other-class' in html_contents + # check that the multi-image caption is applied twice + assert html_contents.count(b'This caption applies to both plots.') == 2 diff --git a/lib/matplotlib/tests/tinypages/some_plots.rst b/lib/matplotlib/tests/tinypages/some_plots.rst index 5a71abc9e761..514552decfee 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/tinypages/some_plots.rst @@ -141,3 +141,23 @@ using the :caption: option: .. plot:: range4.py :caption: This is the caption for plot 18. + +Plot 19 uses shows that the "plot-directive" class is still appended, even if +we request other custom classes: + +.. plot:: range4.py + :class: my-class my-other-class + + Should also have a caption. + +Plot 20 shows that the default template correctly prints the multi-image +scenario: + +.. plot:: + :caption: This caption applies to both plots. + + plt.figure() + plt.plot(range(6)) + + plt.figure() + plt.plot(range(4)) From 919801b86c328aebcdbb032ff79c379aad11849b Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 22 Sep 2020 13:57:56 -0500 Subject: [PATCH 3/8] REORG: JoinStyle and CapStyle classes Centralize docs and validation for JoinStyle and CapStyle in one place. --- doc/api/_types.rst | 9 + doc/api/index.rst | 1 + examples/lines_bars_and_markers/joinstyle.py | 90 ---------- lib/matplotlib/_types.py | 170 +++++++++++++++++++ lib/matplotlib/backend_bases.py | 34 ++-- lib/matplotlib/backends/backend_cairo.py | 21 +-- lib/matplotlib/backends/backend_pdf.py | 20 ++- lib/matplotlib/backends/backend_pgf.py | 13 +- lib/matplotlib/backends/backend_ps.py | 12 +- lib/matplotlib/backends/backend_svg.py | 9 +- lib/matplotlib/backends/backend_wx.py | 13 +- lib/matplotlib/collections.py | 23 +-- lib/matplotlib/lines.py | 56 +++--- lib/matplotlib/markers.py | 33 ++-- lib/matplotlib/patches.py | 25 +-- lib/matplotlib/rcsetup.py | 48 +----- lib/matplotlib/tests/test_collections.py | 13 +- lib/matplotlib/tests/test_patches.py | 7 +- src/py_converters.cpp | 23 ++- 19 files changed, 358 insertions(+), 262 deletions(-) create mode 100644 doc/api/_types.rst delete mode 100644 examples/lines_bars_and_markers/joinstyle.py create mode 100644 lib/matplotlib/_types.py diff --git a/doc/api/_types.rst b/doc/api/_types.rst new file mode 100644 index 000000000000..de1be5cb95b4 --- /dev/null +++ b/doc/api/_types.rst @@ -0,0 +1,9 @@ +********************** +``matplotlib._types`` +********************** + +.. automodule:: matplotlib._types + :members: + :undoc-members: + :show-inheritance: + diff --git a/doc/api/index.rst b/doc/api/index.rst index dba86c35ad0a..10d6d600137d 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -124,6 +124,7 @@ Matplotlib consists of the following submodules: transformations.rst tri_api.rst type1font.rst + _types.rst units_api.rst widgets_api.rst _api_api.rst diff --git a/examples/lines_bars_and_markers/joinstyle.py b/examples/lines_bars_and_markers/joinstyle.py deleted file mode 100644 index dcc47105d12f..000000000000 --- a/examples/lines_bars_and_markers/joinstyle.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -========================== -Join styles and cap styles -========================== - -This example demonstrates the available join styles and cap styles. - -Both are used in `.Line2D` and various ``Collections`` from -`matplotlib.collections` as well as some functions that create these, e.g. -`~matplotlib.pyplot.plot`. - - -Join styles -=========== - -Join styles define how the connection between two line segments is drawn. - -See the respective ``solid_joinstyle``, ``dash_joinstyle`` or ``joinstyle`` -parameters. -""" - -import numpy as np -import matplotlib.pyplot as plt - - -def plot_angle(ax, x, y, angle, style): - phi = np.radians(angle) - xx = [x + .5, x, x + .5*np.cos(phi)] - yy = [y, y, y + .5*np.sin(phi)] - ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) - ax.plot(xx, yy, lw=1, color='black') - ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) - - -fig, ax = plt.subplots(figsize=(8, 6)) -ax.set_title('Join style') - -for x, style in enumerate(['miter', 'round', 'bevel']): - ax.text(x, 5, style) - for y, angle in enumerate([20, 45, 60, 90, 120]): - plot_angle(ax, x, y, angle, style) - if x == 0: - ax.text(-1.3, y, f'{angle} degrees') -ax.text(1, 4.7, '(default)') - -ax.set_xlim(-1.5, 2.75) -ax.set_ylim(-.5, 5.5) -ax.set_axis_off() -plt.show() - - -############################################################################# -# -# Cap styles -# ========== -# -# Cap styles define how the the end of a line is drawn. -# -# See the respective ``solid_capstyle``, ``dash_capstyle`` or ``capstyle`` -# parameters. - -fig, ax = plt.subplots(figsize=(8, 2)) -ax.set_title('Cap style') - -for x, style in enumerate(['butt', 'round', 'projecting']): - ax.text(x+0.25, 1, style, ha='center') - xx = [x, x+0.5] - yy = [0, 0] - ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) - ax.plot(xx, yy, lw=1, color='black') - ax.plot(xx, yy, 'o', color='tab:red', markersize=3) -ax.text(2.25, 0.7, '(default)', ha='center') - -ax.set_ylim(-.5, 1.5) -ax.set_axis_off() - - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_types.py new file mode 100644 index 000000000000..f32cde398d50 --- /dev/null +++ b/lib/matplotlib/_types.py @@ -0,0 +1,170 @@ +""" +Style description information that is shared across unrelated classses. +""" + +from enum import Enum, auto +from matplotlib import cbook + + +class _AutoStringNameEnum(Enum): + """Automate the ``name = 'name'`` part of making a (str, Enum).""" + def _generate_next_value_(name, start, count, last_values): + return name + + +def _deprecate_case_insensitive_join_cap(s): + s_low = s.lower() + if s != s_low: + if s_low in ['miter', 'round', 'bevel']: + cbook.warn_deprecated( + "3.3", message="Case-insensitive capstyles are deprecated " + "since %(since)s and support for them will be removed " + "%(removal)s; please pass them in lowercase.") + elif s_low in ['butt', 'round', 'projecting']: + cbook.warn_deprecated( + "3.3", message="Case-insensitive joinstyles are deprecated " + "since %(since)s and support for them will be removed " + "%(removal)s; please pass them in lowercase.") + # Else, error out at the check_in_list stage. + return s_low + + +class JoinStyle(str, _AutoStringNameEnum): + """ + Define how the connection between two line segments is drawn. + + For a visual impression of each *JoinStyle*, `view these docs online + `, or run `JoinStyle.demo`: + + .. plot:: + :alt: Demo of possible JoinStyle's + + from matplotlib._types import JoinStyle + JoinStyle.demo() + + Lines in Matplotlib are typically defined by a 1D `~.path.Path` and a + finite ``linewidth``, where the underlying 1D `~.path.Path` represents the + center of the stroked line. + + By default, `~.backend_bases.GraphicsContextBase` defines the boundaries of + a stroked line to simply be every point within some radius, + ``linewidth/2``, away from any point of the center line. However, this + results in corners appearing "rounded", which may not be the desired + behavior if you are drawing, for example, a polygon or pointed star. + + Matplotlib provides three options for drawing the corners between adjacent + segments. In short: + + - *miter* is the "arrow-tip" style. Each boundary of the filled-in area + will extend in a straight line parallel to the tangent vector of the + centerline at the point it meets the corner, until they meet in a + sharp point. + - *round* stokes every point within a radius of ``linewidth/2`` of the + center lines. + - *bevel* is the "squared-off" style. It can be thought of as a rounded + corner where the "circular" part of the corner has been cut off. + + .. note:: + + The *miter* option can be controlled further by specifying a "miter + limit", which specifies how long a miter tip can get before it is + automatically "bevel"ed off. Matplotlib does not currently expose a + ``miterlimit`` parameter to the user, and most backends simply use the + upstream default value. For example, the PDF backend assumes the + default value of 10 specified by the PDF standard, while the SVG + backend does not even specify the miter limit, resulting in a default + value of 4 per the SVG specification. + + A more detailed description of the effect of a miter limit can be found + in the `Mozilla Developer Docs + `_ + """ + + miter = auto() + round = auto() + bevel = auto() + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + import numpy as np + import matplotlib.pyplot as plt + + def plot_angle(ax, x, y, angle, style): + phi = np.radians(angle) + xx = [x + .5, x, x + .5*np.cos(phi)] + yy = [y, y, y + .5*np.sin(phi)] + ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) + ax.plot(xx, yy, lw=1, color='black') + ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) + + fig, ax = plt.subplots(figsize=(8, 6)) + ax.set_title('Join style') + for x, style in enumerate(['miter', 'round', 'bevel']): + ax.text(x, 5, style) + for y, angle in enumerate([20, 45, 60, 90, 120]): + plot_angle(ax, x, y, angle, style) + if x == 0: + ax.text(-1.3, y, f'{angle} degrees') + ax.set_xlim(-1.5, 2.75) + ax.set_ylim(-.5, 5.5) + ax.set_axis_off() + fig.show() + + +class CapStyle(str, _AutoStringNameEnum): + r""" + Define how the two endpoints (caps) of an unclosed line are drawn. + + How to draw the start and end points of lines that represent a closed curve + (i.e. that end in a `~.path.Path.CLOSEPOLY`) is controlled by the line's + `JoinStyle`. For all other lines, how the start and end points are drawn is + controlled by the *CapStyle*. + + For a visual impression of each *CapStyle*, `view these docs online + ` or run `CapStyle.demo`: + + .. plot:: + :alt: Demo of possible CapStyle's + + from matplotlib._types import CapStyle + CapStyle.demo() + + Available options: + + - *butt*: the line is squared off at its endpoint. + - *projecting*: the line is squared off as in *butt*, but the filled in + area extends beyond the endpoint a distance of ``linewidth/2``. + - *round*: like *butt*, but a semicircular cap is added to the end of + the line, of radius ``linewidth/2``. + """ + butt = 'butt' + projecting = 'projecting' + round = 'round' + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize=(8, 2)) + ax.set_title('Cap style') + + for x, style in enumerate(['butt', 'round', 'projecting']): + ax.text(x+0.25, 1, style, ha='center') + xx = [x, x+0.5] + yy = [0, 0] + ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) + ax.plot(xx, yy, lw=1, color='black') + ax.plot(xx, yy, 'o', color='tab:red', markersize=3) + ax.text(2.25, 0.7, '(default)', ha='center') + + ax.set_ylim(-.5, 1.5) + ax.set_axis_off() + fig.show() diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 2b89fac08b53..6ea5cfbbba1f 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -50,8 +50,8 @@ from matplotlib.backend_managers import ToolManager from matplotlib.cbook import _setattr_cm from matplotlib.path import Path -from matplotlib.rcsetup import validate_joinstyle, validate_capstyle from matplotlib.transforms import Affine2D +from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -765,11 +765,11 @@ def __init__(self): self._alpha = 1.0 self._forced_alpha = False # if True, _alpha overrides A from RGBA self._antialiased = 1 # use 0, 1 not True, False for extension code - self._capstyle = 'butt' + self._capstyle = CapStyle('butt') self._cliprect = None self._clippath = None self._dashes = 0, None - self._joinstyle = 'round' + self._joinstyle = JoinStyle('round') self._linestyle = 'solid' self._linewidth = 1 self._rgb = (0.0, 0.0, 0.0, 1.0) @@ -820,9 +820,7 @@ def get_antialiased(self): return self._antialiased def get_capstyle(self): - """ - Return the capstyle as a string in ('butt', 'round', 'projecting'). - """ + """Return the `.CapStyle`.""" return self._capstyle def get_clip_rectangle(self): @@ -867,7 +865,7 @@ def get_forced_alpha(self): return self._forced_alpha def get_joinstyle(self): - """Return the line join style as one of ('miter', 'round', 'bevel').""" + """Return the `.JoinStyle`.""" return self._joinstyle def get_linewidth(self): @@ -920,9 +918,14 @@ def set_antialiased(self, b): self._antialiased = int(bool(b)) def set_capstyle(self, cs): - """Set the capstyle to be one of ('butt', 'round', 'projecting').""" - validate_capstyle(cs) - self._capstyle = cs + """ + Set how to draw endpoints of lines. + + Parameters + ---------- + cs : `.CapStyle` or {'butt', 'round', 'projecting'} + """ + self._capstyle = CapStyle(cs) def set_clip_rectangle(self, rectangle): """Set the clip rectangle to a `.Bbox` or None.""" @@ -980,9 +983,14 @@ def set_foreground(self, fg, isRGBA=False): self._rgb = colors.to_rgba(fg) def set_joinstyle(self, js): - """Set the join style to be one of ('miter', 'round', 'bevel').""" - validate_joinstyle(js) - self._joinstyle = js + """ + Set how to draw connections between line segments. + + Parameters + ---------- + js : `.JoinStyle` or {'miter', 'round', 'bevel'}. + """ + self._joinstyle = JoinStyle(js) def set_linewidth(self, w): """Set the linewidth in points.""" diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index ced62715dd6a..98b1fcb577d0 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -32,6 +32,7 @@ from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Affine2D +from matplotlib._types import JoinStyle, CapStyle backend_version = cairo.version @@ -321,15 +322,15 @@ def points_to_pixels(self, points): class GraphicsContextCairo(GraphicsContextBase): _joind = { - 'bevel': cairo.LINE_JOIN_BEVEL, - 'miter': cairo.LINE_JOIN_MITER, - 'round': cairo.LINE_JOIN_ROUND, + JoinStyle.bevel: cairo.LINE_JOIN_BEVEL, + JoinStyle.miter: cairo.LINE_JOIN_MITER, + JoinStyle.round: cairo.LINE_JOIN_ROUND, } _capd = { - 'butt': cairo.LINE_CAP_BUTT, - 'projecting': cairo.LINE_CAP_SQUARE, - 'round': cairo.LINE_CAP_ROUND, + CapStyle.butt: cairo.LINE_CAP_BUTT, + CapStyle.projecting: cairo.LINE_CAP_SQUARE, + CapStyle.round: cairo.LINE_CAP_ROUND, } def __init__(self, renderer): @@ -353,8 +354,8 @@ def set_alpha(self, alpha): # one for False. def set_capstyle(self, cs): - self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs)) - self._capstyle = cs + super().set_capstyle(cs) + self.ctx.set_line_cap(self._capd[self.get_capstyle()]) def set_clip_rectangle(self, rectangle): if not rectangle: @@ -396,8 +397,8 @@ def get_rgb(self): return self.ctx.get_source().get_rgba()[:3] def set_joinstyle(self, js): - self.ctx.set_line_join(_api.check_getitem(self._joind, joinstyle=js)) - self._joinstyle = js + super().set_joinstyle(js) + self.ctx.set_line_join(self._joind[self.get_joinstyle()]) def set_linewidth(self, w): self._linewidth = float(w) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index eafb0048c23e..aa219a7a59da 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -26,22 +26,23 @@ import matplotlib as mpl from matplotlib import _api, _text_layout, cbook from matplotlib._pylab_helpers import Gcf +from matplotlib.afm import AFM from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer +from matplotlib.dates import UTC +import matplotlib.dviread as dviread from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font -from matplotlib.afm import AFM -import matplotlib.type1font as type1font -import matplotlib.dviread as dviread from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING, KERNING_UNFITTED) from matplotlib.mathtext import MathTextParser -from matplotlib.transforms import Affine2D, BboxBase -from matplotlib.path import Path -from matplotlib.dates import UTC from matplotlib import _path +from matplotlib.path import Path +from matplotlib._types import JoinStyle, CapStyle +import matplotlib.type1font as type1font +from matplotlib.transforms import Affine2D, BboxBase from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -746,7 +747,8 @@ def newPage(self, width, height): self.reserveObject('length of content stream')) # Initialize the pdf graphics state to match the default mpl # graphics context: currently only the join style needs to be set - self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) + self.output(GraphicsContextPdf.joinstyles[JoinStyle.round], + Op.setlinejoin) # Clear the list of annotations for the next page self.pageAnnotations = [] @@ -2414,8 +2416,8 @@ def paint(self): """ return Op.paint_path(self.fill(), self.stroke()) - capstyles = {'butt': 0, 'round': 1, 'projecting': 2} - joinstyles = {'miter': 0, 'round': 1, 'bevel': 2} + capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} + joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} def capstyle_cmd(self, style): return [self.capstyles[style], Op.setlinecap] diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index a670788b5360..bc2333dcbeef 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -26,6 +26,7 @@ from matplotlib.path import Path from matplotlib.figure import Figure from matplotlib._pylab_helpers import Gcf +from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -515,15 +516,15 @@ def _print_pgf_clip(self, gc): def _print_pgf_path_styles(self, gc, rgbFace): # cap style - capstyles = {"butt": r"\pgfsetbuttcap", - "round": r"\pgfsetroundcap", - "projecting": r"\pgfsetrectcap"} + capstyles = {CapStyle.butt: r"\pgfsetbuttcap", + CapStyle.round: r"\pgfsetroundcap", + CapStyle.projecting: r"\pgfsetrectcap"} writeln(self.fh, capstyles[gc.get_capstyle()]) # join style - joinstyles = {"miter": r"\pgfsetmiterjoin", - "round": r"\pgfsetroundjoin", - "bevel": r"\pgfsetbeveljoin"} + joinstyles = {JoinStyle.miter: r"\pgfsetmiterjoin", + JoinStyle.round: r"\pgfsetroundjoin", + JoinStyle.bevel: r"\pgfsetbeveljoin"} writeln(self.fh, joinstyles[gc.get_joinstyle()]) # filling diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 11a005ee8bf6..aa249ef0f0b7 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -24,6 +24,7 @@ from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) +from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.cbook import is_writable_file_like, file_requires_unicode from matplotlib.font_manager import get_font from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_NO_SCALE @@ -31,9 +32,9 @@ from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 from matplotlib.path import Path +from matplotlib._types import JoinStyle, CapStyle from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D -from matplotlib.backends.backend_mixed import MixedModeRenderer from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -813,11 +814,16 @@ def _is_transparent(rgb_or_rgba): @_api.deprecated("3.4", alternative="GraphicsContextBase") class GraphicsContextPS(GraphicsContextBase): + + _capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} + def get_capstyle(self): - return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()] + return self._capstyles[super().get_capstyle()] + + _joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} def get_joinstyle(self): - return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()] + return self._joinstyles[super().get_joinstyle()] class _Orientation(Enum): diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 79e2be045abb..8afbf60e3444 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -27,6 +27,7 @@ from matplotlib.path import Path from matplotlib import _path from matplotlib.transforms import Affine2D, Affine2DBase +from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -571,10 +572,10 @@ def _get_style_dict(self, gc, rgbFace): attrib['stroke-opacity'] = short_float_fmt(rgb[3]) if linewidth != 1.0: attrib['stroke-width'] = short_float_fmt(linewidth) - if gc.get_joinstyle() != 'round': - attrib['stroke-linejoin'] = gc.get_joinstyle() - if gc.get_capstyle() != 'butt': - attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()] + if gc.get_joinstyle() != JoinStyle.round: + attrib['stroke-linejoin'] = gc.get_joinstyle().name + if gc.get_capstyle() != CapStyle.butt: + attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle().name] return attrib diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 7a2c9fd48207..7983b6846b5b 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -28,6 +28,7 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D +from matplotlib._types import JoinStyle, CapStyle from matplotlib.widgets import SubplotTool import wx @@ -325,13 +326,13 @@ class GraphicsContextWx(GraphicsContextBase): since wxPython colour management is rather simple, I have not chosen to implement a separate colour manager class. """ - _capd = {'butt': wx.CAP_BUTT, - 'projecting': wx.CAP_PROJECTING, - 'round': wx.CAP_ROUND} + _capd = {CapStyle.butt: wx.CAP_BUTT, + CapStyle.projecting: wx.CAP_PROJECTING, + CapStyle.round: wx.CAP_ROUND} - _joind = {'bevel': wx.JOIN_BEVEL, - 'miter': wx.JOIN_MITER, - 'round': wx.JOIN_ROUND} + _joind = {JoinStyle.bevel: wx.JOIN_BEVEL, + JoinStyle.miter: wx.JOIN_MITER, + JoinStyle.round: wx.JOIN_ROUND} _cache = weakref.WeakKeyDictionary() diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 8aed4182b00f..9957a94f858a 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -16,6 +16,7 @@ import matplotlib as mpl from . import (_api, _path, artist, cbook, cm, colors as mcolors, docstring, hatch as mhatch, lines as mlines, path as mpath, transforms) +from ._types import JoinStyle, CapStyle import warnings @@ -110,14 +111,10 @@ def __init__(self, where *onoffseq* is an even length tuple of on and off ink lengths in points. For examples, see :doc:`/gallery/lines_bars_and_markers/linestyles`. - capstyle : str, default: :rc:`patch.capstyle` + capstyle : `.CapStyle`-like, default: :rc:`patch.capstyle` Style to use for capping lines for all paths in the collection. - See :doc:`/gallery/lines_bars_and_markers/joinstyle` for - a demonstration of each of the allowed values. - joinstyle : str, default: :rc:`patch.joinstyle` + joinstyle : `.JoinStyle`-like, default: :rc:`patch.joinstyle` Style to use for joining lines for all paths in the collection. - See :doc:`/gallery/lines_bars_and_markers/joinstyle` for - a demonstration of each of the allowed values. antialiaseds : bool or list of bool, default: :rc:`patch.antialiased` Whether each patch in the collection should be drawn with antialiasing. @@ -657,30 +654,28 @@ def set_linestyle(self, ls): def set_capstyle(self, cs): """ - Set the capstyle for the collection (for all its elements). + Set the `.CapStyle` for the collection (for all its elements). Parameters ---------- - cs : {'butt', 'round', 'projecting'} + cs : `.CapStyle` or {'butt', 'round', 'projecting'} The capstyle. """ - mpl.rcsetup.validate_capstyle(cs) - self._capstyle = cs + self._capstyle = CapStyle(cs) def get_capstyle(self): return self._capstyle def set_joinstyle(self, js): """ - Set the joinstyle for the collection (for all its elements). + Set the `.JoinStyle` for the collection (for all its elements). Parameters ---------- - js : {'miter', 'round', 'bevel'} + js : `.JoinStyle` or {'miter', 'round', 'bevel'} The joinstyle. """ - mpl.rcsetup.validate_joinstyle(js) - self._joinstyle = js + self._joinstyle = JoinStyle(js) def get_joinstyle(self): return self._joinstyle diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index aac40606e015..1a548278a9a0 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -18,6 +18,7 @@ from .path import Path from .transforms import ( Affine2D, Bbox, BboxTransformFrom, BboxTransformTo, TransformedPath) +from ._types import JoinStyle, CapStyle # Imported here for backward compatibility, even though they don't # really belong. @@ -1311,35 +1312,33 @@ def update_from(self, other): def set_dash_joinstyle(self, s): """ - Set the join style for dashed lines. + How to join segments of the line if it `~Line2D.is dashed`. Parameters ---------- - s : {'miter', 'round', 'bevel'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.JoinStyle` or {'miter', 'round', 'bevel'} """ - mpl.rcsetup.validate_joinstyle(s) - if self._dashjoinstyle != s: + js = JoinStyle(s) + if self._dashjoinstyle != js: self.stale = True - self._dashjoinstyle = s + self._dashjoinstyle = js def set_solid_joinstyle(self, s): """ - Set the join style for solid lines. + How to join segments if the line is solid (not `~Line2D.is_dashed`). Parameters ---------- - s : {'miter', 'round', 'bevel'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.JoinStyle` or {'miter', 'round', 'bevel'} """ - mpl.rcsetup.validate_joinstyle(s) - if self._solidjoinstyle != s: + js = JoinStyle(s) + if self._solidjoinstyle != js: self.stale = True - self._solidjoinstyle = s + self._solidjoinstyle = js def get_dash_joinstyle(self): """ - Return the join style for dashed lines. + Return the `.JoinStyle` for dashed lines. See also `~.Line2D.set_dash_joinstyle`. """ @@ -1347,7 +1346,7 @@ def get_dash_joinstyle(self): def get_solid_joinstyle(self): """ - Return the join style for solid lines. + Return the `.JoinStyle` for solid lines. See also `~.Line2D.set_solid_joinstyle`. """ @@ -1355,35 +1354,33 @@ def get_solid_joinstyle(self): def set_dash_capstyle(self, s): """ - Set the cap style for dashed lines. + How to draw the end caps if the line is `~Line2D.is_dashed`. Parameters ---------- - s : {'butt', 'round', 'projecting'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.CapStyle` or {'butt', 'round', 'projecting'} """ - mpl.rcsetup.validate_capstyle(s) - if self._dashcapstyle != s: + cs = CapStyle(s) + if self._dashcapstyle != cs: self.stale = True - self._dashcapstyle = s + self._dashcapstyle = cs def set_solid_capstyle(self, s): """ - Set the cap style for solid lines. + How to draw the end caps if the line is solid (not `~Line2D.is_dashed`) Parameters ---------- - s : {'butt', 'round', 'projecting'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.CapStyle` or {'butt', 'round', 'projecting'} """ - mpl.rcsetup.validate_capstyle(s) - if self._solidcapstyle != s: + cs = CapStyle(s) + if self._solidcapstyle != cs: self.stale = True - self._solidcapstyle = s + self._solidcapstyle = cs def get_dash_capstyle(self): """ - Return the cap style for dashed lines. + Return the `.CapStyle` for dashed lines. See also `~.Line2D.set_dash_capstyle`. """ @@ -1391,7 +1388,7 @@ def get_dash_capstyle(self): def get_solid_capstyle(self): """ - Return the cap style for solid lines. + Return the `.CapStyle` for solid lines. See also `~.Line2D.set_solid_capstyle`. """ @@ -1399,7 +1396,8 @@ def get_solid_capstyle(self): def is_dashed(self): """ - Return whether line has a dashed linestyle. + Return whether line has a dashed linestyle. A custom linestyle is + assumed to be dashed, we do not inspect the ``onoffseq`` directly. See also `~.Line2D.set_linestyle`. """ diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 403321b38c70..821b8b46430f 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -134,6 +134,7 @@ from . import _api, cbook, rcParams from .path import Path from .transforms import IdentityTransform, Affine2D +from ._types import JoinStyle, CapStyle # special-purpose marker identifiers: (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, @@ -239,8 +240,8 @@ def _recache(self): self._alt_path = None self._alt_transform = None self._snap_threshold = None - self._joinstyle = 'round' - self._capstyle = 'butt' + self._joinstyle = JoinStyle.round + self._capstyle = CapStyle.butt # Initial guess: Assume the marker is filled unless the fillstyle is # set to 'none'. The marker function will override this for unfilled # markers. @@ -380,14 +381,14 @@ def _set_tuple_marker(self): symstyle = marker[1] if symstyle == 0: self._path = Path.unit_regular_polygon(numsides) - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter elif symstyle == 1: self._path = Path.unit_regular_star(numsides) - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel elif symstyle == 2: self._path = Path.unit_regular_asterisk(numsides) self._filled = False - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel else: raise ValueError(f"Unexpected tuple marker: {marker}") self._transform = Affine2D().scale(0.5).rotate_deg(rotation) @@ -497,7 +498,7 @@ def _set_triangle(self, rot, skip): self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_triangle_up(self): return self._set_triangle(0.0, 0) @@ -537,7 +538,7 @@ def _set_square(self): self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_diamond(self): self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) @@ -558,7 +559,7 @@ def _set_diamond(self): rotate = 0. self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_thin_diamond(self): self._set_diamond() @@ -594,7 +595,7 @@ def _set_pentagon(self): self._alt_path = mpath_alt self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_star(self): self._transform = Affine2D().scale(0.5) @@ -625,7 +626,7 @@ def _set_star(self): self._alt_path = mpath_alt self._alt_transform = self._transform - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel def _set_hexagon1(self): self._transform = Affine2D().scale(0.5) @@ -659,7 +660,7 @@ def _set_hexagon1(self): self._alt_path = mpath_alt self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_hexagon2(self): self._transform = Affine2D().scale(0.5).rotate_deg(30) @@ -694,7 +695,7 @@ def _set_hexagon2(self): self._alt_path = mpath_alt self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_octagon(self): self._transform = Affine2D().scale(0.5) @@ -724,7 +725,7 @@ def _set_octagon(self): self._path = self._alt_path = half self._alt_transform = self._transform.frozen().rotate_deg(180.0) - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) @@ -798,7 +799,7 @@ def _set_caretdown(self): self._snap_threshold = 3.0 self._filled = False self._path = self._caret_path - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_caretup(self): self._set_caretdown() @@ -863,7 +864,7 @@ def _set_x(self): def _set_plus_filled(self): self._transform = Affine2D().translate(-0.5, -0.5) self._snap_threshold = 5.0 - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter fs = self.get_fillstyle() if not self._half_fill(): self._path = self._plus_filled_path @@ -895,7 +896,7 @@ def _set_plus_filled(self): def _set_x_filled(self): self._transform = Affine2D().translate(-0.5, -0.5) self._snap_threshold = 5.0 - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter fs = self.get_fillstyle() if not self._half_fill(): self._path = self._x_filled_path diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 103adee18366..e303e0f2a53a 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -16,6 +16,7 @@ get_parallels, inside_circle, make_wedged_bezier2, split_bezier_intersecting_with_closedpath, split_path_inout) from .path import Path +from ._types import JoinStyle, CapStyle @cbook._define_aliases({ @@ -74,9 +75,9 @@ def __init__(self, if linestyle is None: linestyle = "solid" if capstyle is None: - capstyle = 'butt' + capstyle = CapStyle.butt if joinstyle is None: - joinstyle = 'miter' + joinstyle = JoinStyle.miter if antialiased is None: antialiased = mpl.rcParams['patch.antialiased'] @@ -473,14 +474,14 @@ def get_fill(self): def set_capstyle(self, s): """ - Set the capstyle. + Set the `.CapStyle`. Parameters ---------- - s : {'butt', 'round', 'projecting'} + s : `.CapStyle` or {'butt', 'round', 'projecting'} """ - mpl.rcsetup.validate_capstyle(s) - self._capstyle = s + cs = CapStyle(s) + self._capstyle = cs self.stale = True def get_capstyle(self): @@ -489,14 +490,14 @@ def get_capstyle(self): def set_joinstyle(self, s): """ - Set the joinstyle. + Set the `.JoinStyle`. Parameters ---------- - s : {'miter', 'round', 'bevel'} + s : `.JoinStyle` or {'miter', 'round', 'bevel'} """ - mpl.rcsetup.validate_joinstyle(s) - self._joinstyle = s + js = JoinStyle(s) + self._joinstyle = js self.stale = True def get_joinstyle(self): @@ -3970,8 +3971,8 @@ def __init__(self, posA=None, posB=None, path=None, ``joinstyle`` for `FancyArrowPatch` are set to ``"round"``. """ # Traditionally, the cap- and joinstyle for FancyArrowPatch are round - kwargs.setdefault("joinstyle", "round") - kwargs.setdefault("capstyle", "round") + kwargs.setdefault("joinstyle", JoinStyle.round) + kwargs.setdefault("capstyle", CapStyle.round) super().__init__(**kwargs) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index e6c943fdac59..7bb26c72583f 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -22,10 +22,11 @@ import numpy as np -from matplotlib import _api, animation, cbook +from matplotlib import animation, cbook from matplotlib.cbook import ls_mapper -from matplotlib.fontconfig_pattern import parse_fontconfig_pattern from matplotlib.colors import is_color_like +from matplotlib.fontconfig_pattern import parse_fontconfig_pattern +from matplotlib._types import JoinStyle, CapStyle # Don't let the original cycler collide with our validating cycler from cycler import Cycler, cycler as ccycler @@ -571,41 +572,10 @@ def _is_iterable_not_string_like(x): raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") -def _deprecate_case_insensitive_join_cap(s): - s_low = s.lower() - if s != s_low: - if s_low in ['miter', 'round', 'bevel']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive capstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - elif s_low in ['butt', 'round', 'projecting']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive joinstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - # Else, error out at the check_in_list stage. - return s_low - - -def validate_joinstyle(s): - s = _deprecate_case_insensitive_join_cap(s) - _api.check_in_list(['miter', 'round', 'bevel'], joinstyle=s) - return s - - -def validate_capstyle(s): - s = _deprecate_case_insensitive_join_cap(s) - _api.check_in_list(['butt', 'round', 'projecting'], capstyle=s) - return s - - validate_fillstyle = ValidateInStrings( 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none']) -validate_joinstylelist = _listify_validator(validate_joinstyle) -validate_capstylelist = _listify_validator(validate_capstyle) validate_fillstylelist = _listify_validator(validate_fillstyle) @@ -782,8 +752,8 @@ def validate_hatch(s): 'linestyle': _listify_validator(_validate_linestyle), 'facecolor': validate_colorlist, 'edgecolor': validate_colorlist, - 'joinstyle': validate_joinstylelist, - 'capstyle': validate_capstylelist, + 'joinstyle': _listify_validator(JoinStyle), + 'capstyle': _listify_validator(CapStyle), 'fillstyle': validate_fillstylelist, 'markerfacecolor': validate_colorlist, 'markersize': validate_floatlist, @@ -1028,10 +998,10 @@ def _convert_validator_spec(key, conv): "lines.markeredgewidth": validate_float, "lines.markersize": validate_float, # markersize, in points "lines.antialiased": validate_bool, # antialiased (no jaggies) - "lines.dash_joinstyle": validate_joinstyle, - "lines.solid_joinstyle": validate_joinstyle, - "lines.dash_capstyle": validate_capstyle, - "lines.solid_capstyle": validate_capstyle, + "lines.dash_joinstyle": JoinStyle, + "lines.solid_joinstyle": JoinStyle, + "lines.dash_capstyle": CapStyle, + "lines.solid_capstyle": CapStyle, "lines.dashed_pattern": validate_floatlist, "lines.dashdot_pattern": validate_floatlist, "lines.dotted_pattern": validate_floatlist, diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index f125f771f913..db86ad2eee92 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -6,12 +6,13 @@ import pytest import matplotlib as mpl -import matplotlib.pyplot as plt import matplotlib.collections as mcollections -import matplotlib.transforms as mtransforms from matplotlib.collections import (Collection, LineCollection, EventCollection, PolyCollection) +import matplotlib.pyplot as plt +from matplotlib._types import JoinStyle, CapStyle from matplotlib.testing.decorators import image_comparison +import matplotlib.transforms as mtransforms def generate_EventCollection_plot(): @@ -503,17 +504,17 @@ def test_lslw_bcast(): @pytest.mark.style('default') def test_capstyle(): col = mcollections.PathCollection([], capstyle='round') - assert col.get_capstyle() == 'round' + assert col.get_capstyle() == CapStyle.round col.set_capstyle('butt') - assert col.get_capstyle() == 'butt' + assert col.get_capstyle() == CapStyle.butt @pytest.mark.style('default') def test_joinstyle(): col = mcollections.PathCollection([], joinstyle='round') - assert col.get_joinstyle() == 'round' + assert col.get_joinstyle() == JoinStyle.round col.set_joinstyle('miter') - assert col.get_joinstyle() == 'miter' + assert col.get_joinstyle() == JoinStyle.miter @image_comparison(['cap_and_joinstyle.png']) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 2a908098364e..a6001752bfe5 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -6,9 +6,10 @@ import pytest from matplotlib.patches import Patch, Polygon, Rectangle, FancyArrowPatch +import matplotlib.pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.transforms import Bbox -import matplotlib.pyplot as plt +from matplotlib._types import JoinStyle, CapStyle from matplotlib import ( collections as mcollections, colors as mcolors, patches as mpatches, path as mpath, style as mstyle, transforms as mtransforms, rcParams) @@ -620,9 +621,9 @@ def test_default_linestyle(): def test_default_capstyle(): patch = Patch() - assert patch.get_capstyle() == 'butt' + assert patch.get_capstyle() == CapStyle.butt def test_default_joinstyle(): patch = Patch() - assert patch.get_joinstyle() == 'miter' + assert patch.get_joinstyle() == JoinStyle.miter diff --git a/src/py_converters.cpp b/src/py_converters.cpp index ace8332dda76..5fd0f7638ee4 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -130,7 +130,17 @@ int convert_cap(PyObject *capobj, void *capp) int values[] = {agg::butt_cap, agg::round_cap, agg::square_cap}; int result = agg::butt_cap; - if (!convert_string_enum(capobj, "capstyle", names, values, &result)) { + PyObject *name = PyUnicode_FromString("name"); + PyObject *cap_string = PyObject_GetAttr(capobj, name); + if (cap_string == NULL) { + Py_DECREF(name); + PyErr_SetString(PyExc_TypeError, "capstyle must be a CapStyle (Enum) object."); + } + + int success = convert_string_enum(cap_string, "capstyle", names, values, &result); + Py_DECREF(name); + Py_DECREF(cap_string); + if (!success) { return 0; } @@ -143,8 +153,17 @@ int convert_join(PyObject *joinobj, void *joinp) const char *names[] = {"miter", "round", "bevel", NULL}; int values[] = {agg::miter_join_revert, agg::round_join, agg::bevel_join}; int result = agg::miter_join_revert; + PyObject *name = PyUnicode_FromString("name"); + PyObject *join_string = PyObject_GetAttr(joinobj, name); + if (join_string == NULL) { + Py_DECREF(name); + PyErr_SetString(PyExc_TypeError, "joinstyle must be a JoinStyle (Enum) object."); + } - if (!convert_string_enum(joinobj, "joinstyle", names, values, &result)) { + int success = convert_string_enum(join_string, "joinstyle", names, values, &result); + Py_DECREF(name); + Py_DECREF(join_string); + if (!success) { return 0; } From 117adabb71bb90e76ed3ce55a02854789eacfb41 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 20 Oct 2020 10:32:34 -0700 Subject: [PATCH 4/8] REVERT: Do not expose JoinStyle/CapStyle at all --- lib/matplotlib/backend_bases.py | 4 ++-- lib/matplotlib/backends/backend_cairo.py | 21 ++++++++++----------- lib/matplotlib/backends/backend_pdf.py | 20 +++++++++----------- lib/matplotlib/backends/backend_pgf.py | 13 ++++++------- lib/matplotlib/backends/backend_ps.py | 12 +++--------- lib/matplotlib/backends/backend_svg.py | 9 ++++----- lib/matplotlib/backends/backend_wx.py | 13 ++++++------- lib/matplotlib/collections.py | 6 ++---- lib/matplotlib/lines.py | 12 ++++++------ lib/matplotlib/tests/test_collections.py | 13 ++++++------- lib/matplotlib/tests/test_patches.py | 7 +++---- src/py_converters.cpp | 23 ++--------------------- 12 files changed, 59 insertions(+), 94 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 6ea5cfbbba1f..3c69fe685780 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -821,7 +821,7 @@ def get_antialiased(self): def get_capstyle(self): """Return the `.CapStyle`.""" - return self._capstyle + return self._capstyle.name def get_clip_rectangle(self): """ @@ -866,7 +866,7 @@ def get_forced_alpha(self): def get_joinstyle(self): """Return the `.JoinStyle`.""" - return self._joinstyle + return self._joinstyle.name def get_linewidth(self): """Return the line width in points.""" diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index 98b1fcb577d0..ced62715dd6a 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -32,7 +32,6 @@ from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib._types import JoinStyle, CapStyle backend_version = cairo.version @@ -322,15 +321,15 @@ def points_to_pixels(self, points): class GraphicsContextCairo(GraphicsContextBase): _joind = { - JoinStyle.bevel: cairo.LINE_JOIN_BEVEL, - JoinStyle.miter: cairo.LINE_JOIN_MITER, - JoinStyle.round: cairo.LINE_JOIN_ROUND, + 'bevel': cairo.LINE_JOIN_BEVEL, + 'miter': cairo.LINE_JOIN_MITER, + 'round': cairo.LINE_JOIN_ROUND, } _capd = { - CapStyle.butt: cairo.LINE_CAP_BUTT, - CapStyle.projecting: cairo.LINE_CAP_SQUARE, - CapStyle.round: cairo.LINE_CAP_ROUND, + 'butt': cairo.LINE_CAP_BUTT, + 'projecting': cairo.LINE_CAP_SQUARE, + 'round': cairo.LINE_CAP_ROUND, } def __init__(self, renderer): @@ -354,8 +353,8 @@ def set_alpha(self, alpha): # one for False. def set_capstyle(self, cs): - super().set_capstyle(cs) - self.ctx.set_line_cap(self._capd[self.get_capstyle()]) + self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs)) + self._capstyle = cs def set_clip_rectangle(self, rectangle): if not rectangle: @@ -397,8 +396,8 @@ def get_rgb(self): return self.ctx.get_source().get_rgba()[:3] def set_joinstyle(self, js): - super().set_joinstyle(js) - self.ctx.set_line_join(self._joind[self.get_joinstyle()]) + self.ctx.set_line_join(_api.check_getitem(self._joind, joinstyle=js)) + self._joinstyle = js def set_linewidth(self, w): self._linewidth = float(w) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index aa219a7a59da..eafb0048c23e 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -26,23 +26,22 @@ import matplotlib as mpl from matplotlib import _api, _text_layout, cbook from matplotlib._pylab_helpers import Gcf -from matplotlib.afm import AFM from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.dates import UTC -import matplotlib.dviread as dviread from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font +from matplotlib.afm import AFM +import matplotlib.type1font as type1font +import matplotlib.dviread as dviread from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING, KERNING_UNFITTED) from matplotlib.mathtext import MathTextParser -from matplotlib import _path -from matplotlib.path import Path -from matplotlib._types import JoinStyle, CapStyle -import matplotlib.type1font as type1font from matplotlib.transforms import Affine2D, BboxBase +from matplotlib.path import Path +from matplotlib.dates import UTC +from matplotlib import _path from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -747,8 +746,7 @@ def newPage(self, width, height): self.reserveObject('length of content stream')) # Initialize the pdf graphics state to match the default mpl # graphics context: currently only the join style needs to be set - self.output(GraphicsContextPdf.joinstyles[JoinStyle.round], - Op.setlinejoin) + self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) # Clear the list of annotations for the next page self.pageAnnotations = [] @@ -2416,8 +2414,8 @@ def paint(self): """ return Op.paint_path(self.fill(), self.stroke()) - capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} - joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} + capstyles = {'butt': 0, 'round': 1, 'projecting': 2} + joinstyles = {'miter': 0, 'round': 1, 'bevel': 2} def capstyle_cmd(self, style): return [self.capstyles[style], Op.setlinecap] diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index bc2333dcbeef..a670788b5360 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -26,7 +26,6 @@ from matplotlib.path import Path from matplotlib.figure import Figure from matplotlib._pylab_helpers import Gcf -from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -516,15 +515,15 @@ def _print_pgf_clip(self, gc): def _print_pgf_path_styles(self, gc, rgbFace): # cap style - capstyles = {CapStyle.butt: r"\pgfsetbuttcap", - CapStyle.round: r"\pgfsetroundcap", - CapStyle.projecting: r"\pgfsetrectcap"} + capstyles = {"butt": r"\pgfsetbuttcap", + "round": r"\pgfsetroundcap", + "projecting": r"\pgfsetrectcap"} writeln(self.fh, capstyles[gc.get_capstyle()]) # join style - joinstyles = {JoinStyle.miter: r"\pgfsetmiterjoin", - JoinStyle.round: r"\pgfsetroundjoin", - JoinStyle.bevel: r"\pgfsetbeveljoin"} + joinstyles = {"miter": r"\pgfsetmiterjoin", + "round": r"\pgfsetroundjoin", + "bevel": r"\pgfsetbeveljoin"} writeln(self.fh, joinstyles[gc.get_joinstyle()]) # filling diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index aa249ef0f0b7..11a005ee8bf6 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -24,7 +24,6 @@ from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) -from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.cbook import is_writable_file_like, file_requires_unicode from matplotlib.font_manager import get_font from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_NO_SCALE @@ -32,9 +31,9 @@ from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 from matplotlib.path import Path -from matplotlib._types import JoinStyle, CapStyle from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D +from matplotlib.backends.backend_mixed import MixedModeRenderer from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -814,16 +813,11 @@ def _is_transparent(rgb_or_rgba): @_api.deprecated("3.4", alternative="GraphicsContextBase") class GraphicsContextPS(GraphicsContextBase): - - _capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} - def get_capstyle(self): - return self._capstyles[super().get_capstyle()] - - _joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} + return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()] def get_joinstyle(self): - return self._joinstyles[super().get_joinstyle()] + return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()] class _Orientation(Enum): diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 8afbf60e3444..79e2be045abb 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -27,7 +27,6 @@ from matplotlib.path import Path from matplotlib import _path from matplotlib.transforms import Affine2D, Affine2DBase -from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -572,10 +571,10 @@ def _get_style_dict(self, gc, rgbFace): attrib['stroke-opacity'] = short_float_fmt(rgb[3]) if linewidth != 1.0: attrib['stroke-width'] = short_float_fmt(linewidth) - if gc.get_joinstyle() != JoinStyle.round: - attrib['stroke-linejoin'] = gc.get_joinstyle().name - if gc.get_capstyle() != CapStyle.butt: - attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle().name] + if gc.get_joinstyle() != 'round': + attrib['stroke-linejoin'] = gc.get_joinstyle() + if gc.get_capstyle() != 'butt': + attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()] return attrib diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 7983b6846b5b..7a2c9fd48207 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -28,7 +28,6 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib._types import JoinStyle, CapStyle from matplotlib.widgets import SubplotTool import wx @@ -326,13 +325,13 @@ class GraphicsContextWx(GraphicsContextBase): since wxPython colour management is rather simple, I have not chosen to implement a separate colour manager class. """ - _capd = {CapStyle.butt: wx.CAP_BUTT, - CapStyle.projecting: wx.CAP_PROJECTING, - CapStyle.round: wx.CAP_ROUND} + _capd = {'butt': wx.CAP_BUTT, + 'projecting': wx.CAP_PROJECTING, + 'round': wx.CAP_ROUND} - _joind = {JoinStyle.bevel: wx.JOIN_BEVEL, - JoinStyle.miter: wx.JOIN_MITER, - JoinStyle.round: wx.JOIN_ROUND} + _joind = {'bevel': wx.JOIN_BEVEL, + 'miter': wx.JOIN_MITER, + 'round': wx.JOIN_ROUND} _cache = weakref.WeakKeyDictionary() diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 9957a94f858a..4be70e07bd31 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -659,12 +659,11 @@ def set_capstyle(self, cs): Parameters ---------- cs : `.CapStyle` or {'butt', 'round', 'projecting'} - The capstyle. """ self._capstyle = CapStyle(cs) def get_capstyle(self): - return self._capstyle + return self._capstyle.name def set_joinstyle(self, js): """ @@ -673,12 +672,11 @@ def set_joinstyle(self, js): Parameters ---------- js : `.JoinStyle` or {'miter', 'round', 'bevel'} - The joinstyle. """ self._joinstyle = JoinStyle(js) def get_joinstyle(self): - return self._joinstyle + return self._joinstyle.name @staticmethod def _bcast_lwls(linewidths, dashes): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 1a548278a9a0..58f60a717424 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -255,12 +255,12 @@ class Line2D(Artist): @_api.deprecated("3.4") @_api.classproperty def validCap(cls): - return ('butt', 'round', 'projecting') + return tuple(cs.value for cs in CapStyle) @_api.deprecated("3.4") @_api.classproperty def validJoin(cls): - return ('miter', 'round', 'bevel') + return tuple(js.value for js in JoinStyle) def __str__(self): if self._label != "": @@ -1342,7 +1342,7 @@ def get_dash_joinstyle(self): See also `~.Line2D.set_dash_joinstyle`. """ - return self._dashjoinstyle + return self._dashjoinstyle.name def get_solid_joinstyle(self): """ @@ -1350,7 +1350,7 @@ def get_solid_joinstyle(self): See also `~.Line2D.set_solid_joinstyle`. """ - return self._solidjoinstyle + return self._solidjoinstyle.name def set_dash_capstyle(self, s): """ @@ -1384,7 +1384,7 @@ def get_dash_capstyle(self): See also `~.Line2D.set_dash_capstyle`. """ - return self._dashcapstyle + return self._dashcapstyle.name def get_solid_capstyle(self): """ @@ -1392,7 +1392,7 @@ def get_solid_capstyle(self): See also `~.Line2D.set_solid_capstyle`. """ - return self._solidcapstyle + return self._solidcapstyle.name def is_dashed(self): """ diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index db86ad2eee92..f125f771f913 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -6,13 +6,12 @@ import pytest import matplotlib as mpl +import matplotlib.pyplot as plt import matplotlib.collections as mcollections +import matplotlib.transforms as mtransforms from matplotlib.collections import (Collection, LineCollection, EventCollection, PolyCollection) -import matplotlib.pyplot as plt -from matplotlib._types import JoinStyle, CapStyle from matplotlib.testing.decorators import image_comparison -import matplotlib.transforms as mtransforms def generate_EventCollection_plot(): @@ -504,17 +503,17 @@ def test_lslw_bcast(): @pytest.mark.style('default') def test_capstyle(): col = mcollections.PathCollection([], capstyle='round') - assert col.get_capstyle() == CapStyle.round + assert col.get_capstyle() == 'round' col.set_capstyle('butt') - assert col.get_capstyle() == CapStyle.butt + assert col.get_capstyle() == 'butt' @pytest.mark.style('default') def test_joinstyle(): col = mcollections.PathCollection([], joinstyle='round') - assert col.get_joinstyle() == JoinStyle.round + assert col.get_joinstyle() == 'round' col.set_joinstyle('miter') - assert col.get_joinstyle() == JoinStyle.miter + assert col.get_joinstyle() == 'miter' @image_comparison(['cap_and_joinstyle.png']) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index a6001752bfe5..2a908098364e 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -6,10 +6,9 @@ import pytest from matplotlib.patches import Patch, Polygon, Rectangle, FancyArrowPatch -import matplotlib.pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.transforms import Bbox -from matplotlib._types import JoinStyle, CapStyle +import matplotlib.pyplot as plt from matplotlib import ( collections as mcollections, colors as mcolors, patches as mpatches, path as mpath, style as mstyle, transforms as mtransforms, rcParams) @@ -621,9 +620,9 @@ def test_default_linestyle(): def test_default_capstyle(): patch = Patch() - assert patch.get_capstyle() == CapStyle.butt + assert patch.get_capstyle() == 'butt' def test_default_joinstyle(): patch = Patch() - assert patch.get_joinstyle() == JoinStyle.miter + assert patch.get_joinstyle() == 'miter' diff --git a/src/py_converters.cpp b/src/py_converters.cpp index 5fd0f7638ee4..ace8332dda76 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -130,17 +130,7 @@ int convert_cap(PyObject *capobj, void *capp) int values[] = {agg::butt_cap, agg::round_cap, agg::square_cap}; int result = agg::butt_cap; - PyObject *name = PyUnicode_FromString("name"); - PyObject *cap_string = PyObject_GetAttr(capobj, name); - if (cap_string == NULL) { - Py_DECREF(name); - PyErr_SetString(PyExc_TypeError, "capstyle must be a CapStyle (Enum) object."); - } - - int success = convert_string_enum(cap_string, "capstyle", names, values, &result); - Py_DECREF(name); - Py_DECREF(cap_string); - if (!success) { + if (!convert_string_enum(capobj, "capstyle", names, values, &result)) { return 0; } @@ -153,17 +143,8 @@ int convert_join(PyObject *joinobj, void *joinp) const char *names[] = {"miter", "round", "bevel", NULL}; int values[] = {agg::miter_join_revert, agg::round_join, agg::bevel_join}; int result = agg::miter_join_revert; - PyObject *name = PyUnicode_FromString("name"); - PyObject *join_string = PyObject_GetAttr(joinobj, name); - if (join_string == NULL) { - Py_DECREF(name); - PyErr_SetString(PyExc_TypeError, "joinstyle must be a JoinStyle (Enum) object."); - } - int success = convert_string_enum(join_string, "joinstyle", names, values, &result); - Py_DECREF(name); - Py_DECREF(join_string); - if (!success) { + if (!convert_string_enum(joinobj, "joinstyle", names, values, &result)) { return 0; } From b905275b7ad0bb35cebed9ccff2633a3d3b70c31 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 20 Oct 2020 12:02:43 -0700 Subject: [PATCH 5/8] DOCS: interpd joinstyle/capstyle valid values --- doc/api/_types.rst | 1 + lib/matplotlib/_types.py | 32 ++++++++++++++++++++------------ lib/matplotlib/backend_bases.py | 10 ++++++---- lib/matplotlib/collections.py | 11 ++++++++--- lib/matplotlib/lines.py | 24 +++++++++++++++--------- lib/matplotlib/patches.py | 6 ++++-- lib/matplotlib/rcsetup.py | 2 +- 7 files changed, 55 insertions(+), 31 deletions(-) diff --git a/doc/api/_types.rst b/doc/api/_types.rst index de1be5cb95b4..3d7fce4329ee 100644 --- a/doc/api/_types.rst +++ b/doc/api/_types.rst @@ -7,3 +7,4 @@ :undoc-members: :show-inheritance: + .. autodata:: _AutoStringNameEnum diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_types.py index f32cde398d50..9b7900c90f89 100644 --- a/lib/matplotlib/_types.py +++ b/lib/matplotlib/_types.py @@ -1,9 +1,9 @@ """ -Style description information that is shared across unrelated classses. +Types that would not normally have their own class are documented here. """ from enum import Enum, auto -from matplotlib import cbook +from matplotlib import cbook, docstring class _AutoStringNameEnum(Enum): @@ -52,17 +52,16 @@ class JoinStyle(str, _AutoStringNameEnum): results in corners appearing "rounded", which may not be the desired behavior if you are drawing, for example, a polygon or pointed star. - Matplotlib provides three options for drawing the corners between adjacent - segments. In short: + Supported values: - *miter* is the "arrow-tip" style. Each boundary of the filled-in area - will extend in a straight line parallel to the tangent vector of the - centerline at the point it meets the corner, until they meet in a - sharp point. + will extend in a straight line parallel to the tangent vector of the + centerline at the point it meets the corner, until they meet in a + sharp point. - *round* stokes every point within a radius of ``linewidth/2`` of the - center lines. + center lines. - *bevel* is the "squared-off" style. It can be thought of as a rounded - corner where the "circular" part of the corner has been cut off. + corner where the "circular" part of the corner has been cut off. .. note:: @@ -113,6 +112,9 @@ def plot_angle(ax, x, y, angle, style): ax.set_ylim(-.5, 5.5) ax.set_axis_off() fig.show() +JoinStyle.input_description = "{" \ + + ", ".join([f"'{js.name}'" for js in JoinStyle]) \ + + "}" class CapStyle(str, _AutoStringNameEnum): @@ -133,13 +135,13 @@ class CapStyle(str, _AutoStringNameEnum): from matplotlib._types import CapStyle CapStyle.demo() - Available options: + Supported values: - *butt*: the line is squared off at its endpoint. - *projecting*: the line is squared off as in *butt*, but the filled in - area extends beyond the endpoint a distance of ``linewidth/2``. + area extends beyond the endpoint a distance of ``linewidth/2``. - *round*: like *butt*, but a semicircular cap is added to the end of - the line, of radius ``linewidth/2``. + the line, of radius ``linewidth/2``. """ butt = 'butt' projecting = 'projecting' @@ -168,3 +170,9 @@ def demo(): ax.set_ylim(-.5, 1.5) ax.set_axis_off() fig.show() +CapStyle.input_description = "{" \ + + ", ".join([f"'{cs.name}'" for cs in CapStyle]) \ + + "}" + +docstring.interpd.update({'JoinStyle': JoinStyle.input_description, + 'CapStyle': CapStyle.input_description}) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 3c69fe685780..a35b44137dfe 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -44,8 +44,8 @@ import matplotlib as mpl from matplotlib import ( - _api, backend_tools as tools, cbook, colors, textpath, tight_bbox, - transforms, widgets, get_backend, is_interactive, rcParams) + _api, backend_tools as tools, cbook, colors, docstring, textpath, + tight_bbox, transforms, widgets, get_backend, is_interactive, rcParams) from matplotlib._pylab_helpers import Gcf from matplotlib.backend_managers import ToolManager from matplotlib.cbook import _setattr_cm @@ -917,13 +917,14 @@ def set_antialiased(self, b): # Use ints to make life easier on extension code trying to read the gc. self._antialiased = int(bool(b)) + @docstring.interpd def set_capstyle(self, cs): """ Set how to draw endpoints of lines. Parameters ---------- - cs : `.CapStyle` or {'butt', 'round', 'projecting'} + cs : `.CapStyle` or %(CapStyle)s """ self._capstyle = CapStyle(cs) @@ -982,13 +983,14 @@ def set_foreground(self, fg, isRGBA=False): else: self._rgb = colors.to_rgba(fg) + @docstring.interpd def set_joinstyle(self, js): """ Set how to draw connections between line segments. Parameters ---------- - js : `.JoinStyle` or {'miter', 'round', 'bevel'}. + js : `.JoinStyle` or %(JoinStyle)s """ self._joinstyle = JoinStyle(js) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 4be70e07bd31..75f95d204fab 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -72,6 +72,7 @@ class Collection(artist.Artist, cm.ScalarMappable): _edge_default = False @cbook._delete_parameter("3.3", "offset_position") + @docstring.interpd def __init__(self, edgecolors=None, facecolors=None, @@ -113,8 +114,10 @@ def __init__(self, :doc:`/gallery/lines_bars_and_markers/linestyles`. capstyle : `.CapStyle`-like, default: :rc:`patch.capstyle` Style to use for capping lines for all paths in the collection. + Allowed values are %(CapStyle)s. joinstyle : `.JoinStyle`-like, default: :rc:`patch.joinstyle` Style to use for joining lines for all paths in the collection. + Allowed values are %(JoinStyle)s. antialiaseds : bool or list of bool, default: :rc:`patch.antialiased` Whether each patch in the collection should be drawn with antialiasing. @@ -125,7 +128,7 @@ def __init__(self, transOffset : `~.transforms.Transform`, default: `.IdentityTransform` A single transform which will be applied to each *offsets* vector before it is used. - offset_position : {'screen' (default), 'data' (deprecated)} + offset_position : {{'screen' (default), 'data' (deprecated)}} If set to 'data' (deprecated), *offsets* will be treated as if it is in data coordinates instead of in screen coordinates. norm : `~.colors.Normalize`, optional @@ -652,26 +655,28 @@ def set_linestyle(self, ls): self._linewidths, self._linestyles = self._bcast_lwls( self._us_lw, self._us_linestyles) + @docstring.interpd def set_capstyle(self, cs): """ Set the `.CapStyle` for the collection (for all its elements). Parameters ---------- - cs : `.CapStyle` or {'butt', 'round', 'projecting'} + cs : `.CapStyle` or %(CapStyle)s """ self._capstyle = CapStyle(cs) def get_capstyle(self): return self._capstyle.name + @docstring.interpd def set_joinstyle(self, js): """ Set the `.JoinStyle` for the collection (for all its elements). Parameters ---------- - js : `.JoinStyle` or {'miter', 'round', 'bevel'} + js : `.JoinStyle` or %(JoinStyle)s """ self._joinstyle = JoinStyle(js) diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 58f60a717424..1a3f55d07598 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -1155,7 +1155,7 @@ def set_linestyle(self, ls): self._dashOffset, self._dashSeq = _scale_dashes( self._us_dashOffset, self._us_dashSeq, self._linewidth) - @docstring.dedent_interpd + @docstring.interpd def set_marker(self, marker): """ Set the line marker. @@ -1310,26 +1310,28 @@ def update_from(self, other): self._marker = MarkerStyle(marker=other._marker) self._drawstyle = other._drawstyle + @docstring.interpd def set_dash_joinstyle(self, s): """ - How to join segments of the line if it `~Line2D.is dashed`. + How to join segments of the line if it `~Line2D.is_dashed`. Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) if self._dashjoinstyle != js: self.stale = True self._dashjoinstyle = js + @docstring.interpd def set_solid_joinstyle(self, s): """ How to join segments if the line is solid (not `~Line2D.is_dashed`). Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) if self._solidjoinstyle != js: @@ -1352,26 +1354,28 @@ def get_solid_joinstyle(self): """ return self._solidjoinstyle.name + @docstring.interpd def set_dash_capstyle(self, s): """ How to draw the end caps if the line is `~Line2D.is_dashed`. Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) if self._dashcapstyle != cs: self.stale = True self._dashcapstyle = cs + @docstring.interpd def set_solid_capstyle(self, s): """ How to draw the end caps if the line is solid (not `~Line2D.is_dashed`) Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) if self._solidcapstyle != cs: @@ -1396,8 +1400,10 @@ def get_solid_capstyle(self): def is_dashed(self): """ - Return whether line has a dashed linestyle. A custom linestyle is - assumed to be dashed, we do not inspect the ``onoffseq`` directly. + Return whether line has a dashed linestyle. + + A custom linestyle is assumed to be dashed, we do not inspect the + ``onoffseq`` directly. See also `~.Line2D.set_linestyle`. """ @@ -1529,4 +1535,4 @@ def onpick(self, event): # You can not set the docstring of an instancemethod, # but you can on the underlying function. Go figure. -docstring.dedent_interpd(Line2D.__init__) +docstring.interpd(Line2D.__init__) diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index e303e0f2a53a..649d7aa11a0a 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -472,13 +472,14 @@ def get_fill(self): # attribute. fill = property(get_fill, set_fill) + @docstring.interpd def set_capstyle(self, s): """ Set the `.CapStyle`. Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) self._capstyle = cs @@ -488,13 +489,14 @@ def get_capstyle(self): """Return the capstyle.""" return self._capstyle + @docstring.interpd def set_joinstyle(self, s): """ Set the `.JoinStyle`. Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) self._joinstyle = js diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 7bb26c72583f..b7b604e6cff1 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -22,7 +22,7 @@ import numpy as np -from matplotlib import animation, cbook +from matplotlib import _api, animation, cbook from matplotlib.cbook import ls_mapper from matplotlib.colors import is_color_like from matplotlib.fontconfig_pattern import parse_fontconfig_pattern From a46278cd958dd783382fdfa4b75d74be79cad092 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Mon, 26 Oct 2020 10:23:07 -0700 Subject: [PATCH 6/8] DOCS: clean up JoinStyle, CapStyle --- doc/api/_types.rst | 12 ++++++++++-- lib/matplotlib/_types.py | 42 ++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/doc/api/_types.rst b/doc/api/_types.rst index 3d7fce4329ee..41a94a01c1cc 100644 --- a/doc/api/_types.rst +++ b/doc/api/_types.rst @@ -3,8 +3,16 @@ ********************** .. automodule:: matplotlib._types - :members: - :undoc-members: + :no-members: :show-inheritance: .. autodata:: _AutoStringNameEnum + + .. autoclass:: JoinStyle + :members: demo + :exclude-members: bevel, miter, round, input_description + + .. autoclass:: CapStyle + :members: demo + :exclude-members: butt, round, projecting, input_description + diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_types.py index 9b7900c90f89..f74a5eed6aa3 100644 --- a/lib/matplotlib/_types.py +++ b/lib/matplotlib/_types.py @@ -34,13 +34,7 @@ class JoinStyle(str, _AutoStringNameEnum): Define how the connection between two line segments is drawn. For a visual impression of each *JoinStyle*, `view these docs online - `, or run `JoinStyle.demo`: - - .. plot:: - :alt: Demo of possible JoinStyle's - - from matplotlib._types import JoinStyle - JoinStyle.demo() + `, or run `JoinStyle.demo`. Lines in Matplotlib are typically defined by a 1D `~.path.Path` and a finite ``linewidth``, where the underlying 1D `~.path.Path` represents the @@ -52,15 +46,15 @@ class JoinStyle(str, _AutoStringNameEnum): results in corners appearing "rounded", which may not be the desired behavior if you are drawing, for example, a polygon or pointed star. - Supported values: + **Supported values:** - - *miter* is the "arrow-tip" style. Each boundary of the filled-in area + - *miter*: the "arrow-tip" style. Each boundary of the filled-in area will extend in a straight line parallel to the tangent vector of the centerline at the point it meets the corner, until they meet in a sharp point. - - *round* stokes every point within a radius of ``linewidth/2`` of the + - *round*: stokes every point within a radius of ``linewidth/2`` of the center lines. - - *bevel* is the "squared-off" style. It can be thought of as a rounded + - *bevel*: the "squared-off" style. It can be thought of as a rounded corner where the "circular" part of the corner has been cut off. .. note:: @@ -77,6 +71,13 @@ class JoinStyle(str, _AutoStringNameEnum): A more detailed description of the effect of a miter limit can be found in the `Mozilla Developer Docs `_ + + .. plot:: + :alt: Demo of possible JoinStyle's + + from matplotlib._types import JoinStyle + JoinStyle.demo() + """ miter = auto() @@ -89,6 +90,7 @@ def __init__(self, s): @staticmethod def demo(): + """Demonstrate how each JoinStyle looks for various join angles.""" import numpy as np import matplotlib.pyplot as plt @@ -127,21 +129,22 @@ class CapStyle(str, _AutoStringNameEnum): controlled by the *CapStyle*. For a visual impression of each *CapStyle*, `view these docs online - ` or run `CapStyle.demo`: + ` or run `CapStyle.demo`. - .. plot:: - :alt: Demo of possible CapStyle's - - from matplotlib._types import CapStyle - CapStyle.demo() - - Supported values: + **Supported values:** - *butt*: the line is squared off at its endpoint. - *projecting*: the line is squared off as in *butt*, but the filled in area extends beyond the endpoint a distance of ``linewidth/2``. - *round*: like *butt*, but a semicircular cap is added to the end of the line, of radius ``linewidth/2``. + + .. plot:: + :alt: Demo of possible CapStyle's + + from matplotlib._types import CapStyle + CapStyle.demo() + """ butt = 'butt' projecting = 'projecting' @@ -153,6 +156,7 @@ def __init__(self, s): @staticmethod def demo(): + """Demonstrate how each CapStyle looks for a thick line segment.""" import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(8, 2)) From c8588312f64da809b358bc7864ae1627e8c973c3 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Thu, 3 Dec 2020 11:09:04 -0800 Subject: [PATCH 7/8] DOCS: render JoinStyle/CapStyle better --- doc/_static/mpl.css | 21 ++++++++++-- doc/api/_types.rst | 3 -- lib/matplotlib/_types.py | 74 ++++++++++++++++++++++++++-------------- 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index 0d99cf930ef2..a6499284b64e 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -89,7 +89,7 @@ table.highlighttable td { padding: 0 0.5em 0 0.5em; } -cite, code, tt { +cite, code, tt, dl.value-list dt { font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.95em; letter-spacing: 0.01em; @@ -730,7 +730,6 @@ td.field-body table.property-table tr:last-of-type td { border-bottom-color: #888; } - /* function and class description */ .descclassname { color: #aaa; @@ -806,6 +805,22 @@ dl.class > dd { font-size: 14px; } +/* custom tables for lists of allowed values in "mpl._types" */ +dl.value-list { + display: grid; +} + +dl.value-list dt { + grid-column: 1; + margin: 4px 0; +} + +dl.value-list dd { + grid-column: 2; + margin: 4px 0 4px 20px; + padding: 0; +} + /* parameter section table */ table.docutils.field-list { width: 100%; @@ -1257,4 +1272,4 @@ div.bullet-box li { div#gallery.section .sphx-glr-clear:first-of-type, div#tutorials.section .sphx-glr-clear:first-of-type{ display: none; -} \ No newline at end of file +} diff --git a/doc/api/_types.rst b/doc/api/_types.rst index 41a94a01c1cc..88ded801768c 100644 --- a/doc/api/_types.rst +++ b/doc/api/_types.rst @@ -4,9 +4,6 @@ .. automodule:: matplotlib._types :no-members: - :show-inheritance: - - .. autodata:: _AutoStringNameEnum .. autoclass:: JoinStyle :members: demo diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_types.py index f74a5eed6aa3..06c05c4049da 100644 --- a/lib/matplotlib/_types.py +++ b/lib/matplotlib/_types.py @@ -1,5 +1,13 @@ """ -Types that would not normally have their own class are documented here. +Concepts used by the matplotlib API that do not yet have a dedicated class. + +Matplotlib often uses simple data types like strings or tuples to define a +concept; e.g. the line capstyle can be specified as one of 'butt', 'round', +or 'projecting'. The classes in this module are used internally and document +these concepts formally. + +As an end-user you will not use these classes directly, but only the values +they define. """ from enum import Enum, auto @@ -48,25 +56,29 @@ class JoinStyle(str, _AutoStringNameEnum): **Supported values:** - - *miter*: the "arrow-tip" style. Each boundary of the filled-in area - will extend in a straight line parallel to the tangent vector of the - centerline at the point it meets the corner, until they meet in a - sharp point. - - *round*: stokes every point within a radius of ``linewidth/2`` of the - center lines. - - *bevel*: the "squared-off" style. It can be thought of as a rounded - corner where the "circular" part of the corner has been cut off. + .. rst-class:: value-list + + 'miter' + the "arrow-tip" style. Each boundary of the filled-in area will + extend in a straight line parallel to the tangent vector of the + centerline at the point it meets the corner, until they meet in a + sharp point. + 'round' + stokes every point within a radius of ``linewidth/2`` of the center + lines. + 'bevel' + the "squared-off" style. It can be thought of as a rounded corner + where the "circular" part of the corner has been cut off. .. note:: - The *miter* option can be controlled further by specifying a "miter - limit", which specifies how long a miter tip can get before it is - automatically "bevel"ed off. Matplotlib does not currently expose a - ``miterlimit`` parameter to the user, and most backends simply use the - upstream default value. For example, the PDF backend assumes the - default value of 10 specified by the PDF standard, while the SVG - backend does not even specify the miter limit, resulting in a default - value of 4 per the SVG specification. + Very long miter tips are cut off (to form a *bevel*) after a + backend-dependent limit called the "miter limit", which specifies the + maximum allowed ratio of miter length to line width. For example, the + PDF backend uses the default value of 10 specified by the PDF standard, + while the SVG backend does not even specify the miter limit, resulting + in a default value of 4 per the SVG specification. Matplotlib does not + currently allow the user to adjust this parameter. A more detailed description of the effect of a miter limit can be found in the `Mozilla Developer Docs @@ -102,7 +114,7 @@ def plot_angle(ax, x, y, angle, style): ax.plot(xx, yy, lw=1, color='black') ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) - fig, ax = plt.subplots(figsize=(8, 6)) + fig, ax = plt.subplots(figsize=(5, 4), constrained_layout=True) ax.set_title('Join style') for x, style in enumerate(['miter', 'round', 'bevel']): ax.text(x, 5, style) @@ -114,6 +126,8 @@ def plot_angle(ax, x, y, angle, style): ax.set_ylim(-.5, 5.5) ax.set_axis_off() fig.show() + + JoinStyle.input_description = "{" \ + ", ".join([f"'{js.name}'" for js in JoinStyle]) \ + "}" @@ -133,11 +147,16 @@ class CapStyle(str, _AutoStringNameEnum): **Supported values:** - - *butt*: the line is squared off at its endpoint. - - *projecting*: the line is squared off as in *butt*, but the filled in - area extends beyond the endpoint a distance of ``linewidth/2``. - - *round*: like *butt*, but a semicircular cap is added to the end of - the line, of radius ``linewidth/2``. + .. rst-class:: value-list + + 'butt' + the line is squared off at its endpoint. + 'projecting' + the line is squared off as in *butt*, but the filled in area + extends beyond the endpoint a distance of ``linewidth/2``. + 'round' + like *butt*, but a semicircular cap is added to the end of the + line, of radius ``linewidth/2``. .. plot:: :alt: Demo of possible CapStyle's @@ -159,21 +178,24 @@ def demo(): """Demonstrate how each CapStyle looks for a thick line segment.""" import matplotlib.pyplot as plt - fig, ax = plt.subplots(figsize=(8, 2)) + fig = plt.figure(figsize=(4, 1.2)) + ax = fig.add_axes([0, 0, 1, 0.8]) ax.set_title('Cap style') for x, style in enumerate(['butt', 'round', 'projecting']): - ax.text(x+0.25, 1, style, ha='center') + ax.text(x+0.25, 0.85, style, ha='center') xx = [x, x+0.5] yy = [0, 0] ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) ax.plot(xx, yy, lw=1, color='black') ax.plot(xx, yy, 'o', color='tab:red', markersize=3) - ax.text(2.25, 0.7, '(default)', ha='center') + ax.text(2.25, 0.55, '(default)', ha='center') ax.set_ylim(-.5, 1.5) ax.set_axis_off() fig.show() + + CapStyle.input_description = "{" \ + ", ".join([f"'{cs.name}'" for cs in CapStyle]) \ + "}" From 5a9d12607e1e58ca41ecc2f8521c62bc48127d16 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Thu, 10 Dec 2020 11:57:25 -0800 Subject: [PATCH 8/8] REVERT: valid[join/cap] deprecation Soft-deprecate instead by simply not using them internally. Prevents spewing unnecessary deprecation errors when inspecting docstrings, and there are no real negatives to leaving them in. --- doc/api/next_api_changes/deprecations/18817-BGB.rst | 3 --- lib/matplotlib/lines.py | 2 -- lib/matplotlib/patches.py | 8 ++------ 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 doc/api/next_api_changes/deprecations/18817-BGB.rst diff --git a/doc/api/next_api_changes/deprecations/18817-BGB.rst b/doc/api/next_api_changes/deprecations/18817-BGB.rst deleted file mode 100644 index 3088cd5d0db5..000000000000 --- a/doc/api/next_api_changes/deprecations/18817-BGB.rst +++ /dev/null @@ -1,3 +0,0 @@ -Line2D and Patch no longer duplicate ``validJoin`` and ``validCap`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Validation of joinstyle and capstyles is now centralized in ``rcsetup``. diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 1a3f55d07598..8b275aa9514c 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -252,12 +252,10 @@ class Line2D(Artist): zorder = 2 - @_api.deprecated("3.4") @_api.classproperty def validCap(cls): return tuple(cs.value for cs in CapStyle) - @_api.deprecated("3.4") @_api.classproperty def validJoin(cls): return tuple(js.value for js in JoinStyle) diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 649d7aa11a0a..225eb115d265 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -35,17 +35,13 @@ class Patch(artist.Artist): """ zorder = 1 - @_api.deprecated("3.4") @_api.classproperty def validCap(cls): - with _api.suppress_matplotlib_deprecation_warning(): - return mlines.Line2D.validCap + return tuple(cs.value for cs in CapStyle) - @_api.deprecated("3.4") @_api.classproperty def validJoin(cls): - with _api.suppress_matplotlib_deprecation_warning(): - return mlines.Line2D.validJoin + return tuple(js.value for js in JoinStyle) # Whether to draw an edge by default. Set on a # subclass-by-subclass basis.