8000 Merge pull request #15536 from anntzer/backendsavefigkwarg · chaoyi1/matplotlib@3272f8c · GitHub
[go: up one dir, main page]

Skip to content

Commit 3272f8c

Browse files
authored
Merge pull request matplotlib#15536 from anntzer/backendsavefigkwarg
Add a backend kwarg to savefig.
2 parents 105d755 + 154a312 commit 3272f8c

File tree

7 files changed

+70
-15
lines changed
  • tutorials/introductory
  • 7 files changed

    +70
    -15
    lines changed
    Lines changed: 6 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,6 @@
    1+
    ``savefig()`` gained a ``backend`` keyword argument
    2+
    ---------------------------------------------------
    3+
    4+
    The ``backend`` keyword argument to ``savefig`` can now be used to pick the
    5+
    rendering backend without having to globally set the backend; e.g. one can save
    6+
    pdfs using the pgf backend with ``savefig("file.pdf", backend="pgf")``.

    lib/matplotlib/backend_bases.py

    Lines changed: 35 additions & 11 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1951,18 +1951,35 @@ def get_supported_filetypes_grouped(cls):
    19511951
    groupings[name].sort()
    19521952
    return groupings
    19531953

    1954-
    def _get_output_canvas(self, fmt):
    1954+
    def _get_output_canvas(self, backend, fmt):
    19551955
    """
    1956-
    Return a canvas suitable for saving figures to a specified file format.
    1956+
    Set the canvas in preparation for saving the figure.
    19571957
    1958-
    If necessary, this function will switch to a registered backend that
    1959-
    supports the format.
    1960-
    """
    1961-
    # Return the current canvas if it supports the requested format.
    1962-
    if hasattr(self, 'print_{}'.format(fmt)):
    1958+
    Parameters
    1959+
    ----------
    1960+
    backend : str or None
    1961+
    If not None, switch the figure canvas to the ``FigureCanvas`` class
    1962+
    of the given backend.
    1963+
    fmt : str
    1964+
    If *backend* is None, then determine a suitable canvas class for
    1965+
    saving to format *fmt* -- either the current canvas class, if it
    1966+
    supports *fmt*, or whatever `get_registered_canvas_class` returns;
    1967+
    switch the figure canvas to that canvas class.
    1968+
    """
    1969+
    if backend is not None:
    1970+
    # Return a specific canvas class, if requested.
    1971+
    canvas_class = (
    1972+
    importlib.import_module(cbook._backend_module_name(backend))
    1973+
    .FigureCanvas)
    1974+
    if not hasattr(canvas_class, f"print_{fmt}"):
    1975+
    raise ValueError(
    1976+
    f"The {backend!r} backend does not support {fmt} output")
    1977+
    elif hasattr(self, f"print_{fmt}"):
    1978+
    # Return the current canvas if it supports the requested format.
    19631979
    return self
    1964-
    # Return a default canvas for the requested format, if it exists.
    1965-
    canvas_class = get_registered_canvas_class(fmt)
    1980+
    else:
    1981+
    # Return a default canvas for the requested format, if it exists.
    1982+
    canvas_class = get_registered_canvas_class(fmt)
    19661983
    if canvas_class:
    19671984
    return self.switch_backends(canvas_class)
    19681985
    # Else report error for unsupported format.
    @@ -1972,7 +1989,7 @@ def _get_output_canvas(self, fmt):
    19721989

    19731990
    def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
    19741991
    orientation='portrait', format=None,
    1975-
    *, bbox_inches=None, **kwargs):
    1992+
    *, bbox_inches=None, backend=None, **kwargs):
    19761993
    """
    19771994
    Render the figure to hardcopy. Set the figure patch face and edge
    19781995
    colors. This is useful because some of the GUIs have a gray figure
    @@ -2012,6 +20 8000 29,13 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
    20122029
    A list of extra artists that will be considered when the
    20132030
    tight bbox is calculated.
    20142031
    2032+
    backend : str, optional
    2033+
    Use a non-default backend to render the file, e.g. to render a
    2034+
    png file with the "cairo" backend rather than the default "agg",
    2035+
    or a pdf file with the "pgf" backend rather than the default
    2036+
    "pdf". Note that the default backend is normally sufficient. See
    2037+
    :ref:`the-builtin-backends` for a list of valid backends for each
    2038+
    file format. Custom backends can be referenced as "module://...".
    20152039
    """
    20162040
    if format is None:
    20172041
    # get format from filename, or from backend's default filetype
    @@ -2026,7 +2050,7 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
    20262050
    format = format.lower()
    20272051

    20282052
    # get canvas object and print method for format
    2029-
    canvas = self._get_output_canvas(format)
    2053+
    canvas = self._get_output_canvas(backend, format)
    20302054
    print_method = getattr(canvas, 'print_%s' % format)
    20312055

    20322056
    if dpi is None:

    lib/matplotlib/cbook/__init__.py

    Lines changed: 9 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -2212,3 +2212,12 @@ def __init__(self, fget):
    22122212

    22132213
    def __get__(self, instance, owner):
    22142214
    return self._fget(owner)
    2215+
    2216+
    2217+
    def _backend_module_name(name):
    2218+
    """
    2219+
    Convert a backend name (either a standard backend -- "Agg", "TkAgg", ... --
    2220+
    or a custom backend -- "module://...") to the corresponding module name).
    2221+
    """
    2222+
    return (name[9:] if name.startswith("module://")
    2223+
    else "matplotlib.backends.backend_{}".format(name.lower()))

    lib/matplotlib/figure.py

    Lines changed: 8 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -2131,6 +2131,14 @@ def savefig(self, fname, *, transparent=None, **kwargs):
    21312131
    A list of extra artists that will be considered when the
    21322132
    tight bbox is calculated.
    21332133
    2134+
    backend : str, optional
    2135+
    Use a non-default backend to render the file, e.g. to render a
    2136+
    png file with the "cairo" backend rather than the default "agg",
    2137+
    or a pdf file with the "pgf" backend rather than the default
    2138+
    "pdf". Note that the default backend is normally sufficient. See
    2139+
    :ref:`the-builtin-backends` for a list of valid backends for each
    2140+
    file format. Custom backends can be referenced as "module://...".
    2141+
    21342142
    metadata : dict, optional
    21352143
    Key/value pairs to store in the image metadata. The supported keys
    21362144
    and defaults depend on the image format and backend:

    lib/matplotlib/pyplot.py

    Lines changed: 1 addition & 4 deletions
    Original file line numberDiff line numberDiff line change
    @@ -212,10 +212,7 @@ def switch_backend(newbackend):
    212212
    rcParamsOrig["backend"] = "agg"
    213213
    return
    214214

    215-
    backend_name = (
    216-
    newbackend[9:] if newbackend.startswith("module://")
    217-
    else "matplotlib.backends.backend_{}".format(newbackend.lower()))
    218-
    215+
    backend_name = cbook._backend_module_name(newbackend)
    219216
    backend_mod = importlib.import_module(backend_name)
    220217
    Backend = type(
    221218
    "Backend", (matplotlib.backend_bases._Backend,), vars(backend_mod))

    lib/matplotlib/tests/test_figure.py

    Lines changed: 10 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -391,6 +391,16 @@ def test_savefig():
    391391
    fig.savefig("fname1.png", "fname2.png")
    392392

    393393

    394+
    def test_savefig_backend():
    395+
    fig = plt.figure()
    396+
    # Intentionally use an invalid module name.
    397+
    with pytest.raises(ModuleNotFoundError, match="No module named '@absent'"):
    398+
    fig.savefig("test", backend="module://@absent")
    399+
    with pytest.raises(ValueError,
    400+
    match="The 'pdf' backend does not support png output"):
    401+
    fig.savefig("test.png", backend="pdf")
    402+
    403+
    394404
    def test_figure_repr():
    395405
    fig = plt.figure(figsize=(10, 20), dpi=10)
    396406
    assert repr(fig) == "<Figure size 100x200 with 0 Axes>"

    tutorials/introductory/usage.py

    Lines changed: 1 addition & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -350,6 +350,7 @@ def my_plotter(ax, data1, data2, param_dict):
    350350
    # use a different backend. Therefore, you should avoid explicitly calling
    351351
    # `~matplotlib.use` unless absolutely necessary.
    352352
    #
    353+
    # .. _the-builtin-backends:
    353354
    #
    354355
    # The builtin backends
    355356
    # --------------------

    0 commit comments

    Comments
     (0)
    0