FIX/API: `fig.canvas.draw` always updates internal state by tacaswell · Pull Request #18408 · matplotlib/matplotlib · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions lib/matplotlib/backend_bases.py
6 changes: 5 additions & 1 deletion lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
GraphicsContextBase, RendererBase)
GraphicsContextBase, RendererBase, _no_output_draw)
from matplotlib.backends.backend_mixed import MixedModeRenderer
from matplotlib.figure import Figure
from matplotlib.font_manager import findfont, get_font
Expand Down Expand Up @@ -2730,6 +2730,10 @@ def print_pdf(self, filename, *,
else: # we opened the file above; now finish it off
file.close()

def draw(self):
_no_output_draw(self.figure)
return super().draw()
Comment thread
QuLogic marked this conversation as resolved.


FigureManagerPdf = FigureManagerBase

Expand Down
7 changes: 6 additions & 1 deletion lib/matplotlib/backends/backend_pgf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
from matplotlib import _api, cbook, font_manager as fm
from matplotlib.backend_bases import (
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
GraphicsContextBase, RendererBase)
GraphicsContextBase, RendererBase, _no_output_draw
)
from matplotlib.backends.backend_mixed import MixedModeRenderer
from matplotlib.backends.backend_pdf import (
_create_pdf_info_dict, _datetime_to_pdf)
Expand Down Expand Up @@ -906,6 +907,10 @@ def print_png(self, fname_or_fh, *args, **kwargs):
def get_renderer(self):
return RendererPgf(self.figure, None)

def draw(self):
_no_output_draw(self.figure)
return super().draw()


FigureManagerPgf = FigureManagerBase

Expand Down
6 changes: 5 additions & 1 deletion lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from matplotlib.afm import AFM
from matplotlib.backend_bases import (
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
GraphicsContextBase, RendererBase)
GraphicsContextBase, RendererBase, _no_output_draw)
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
Expand Down Expand Up @@ -1129,6 +1129,10 @@ def _print_figure_tex(

_move_path_to_path_or_stream(tmpfile, outfile)

def draw(self):
_no_output_draw(self.figure)
return super().draw()


def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble,
paper_width, paper_height, orientation):
Expand Down
6 changes: 5 additions & 1 deletion lib/matplotlib/backends/backend_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from matplotlib import _api, cbook
from matplotlib.backend_bases import (
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
RendererBase)
RendererBase, _no_output_draw)
from matplotlib.backends.backend_mixed import MixedModeRenderer
from matplotlib.colors import rgb2hex
from matplotlib.dates import UTC
Expand Down Expand Up @@ -1363,6 +1363,10 @@ def _print_svg(self, filename, fh, *, dpi=None, bbox_inches_restore=None,
def get_default_filetype(self):
return 'svg'

def draw(self):
_no_output_draw(self.figure)
return super().draw()


FigureManagerSVG = FigureManagerBase

Expand Down
9 changes: 8 additions & 1 deletion lib/matplotlib/backends/backend_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,14 @@ class methods button_press_event, button_release_event,
"""

def draw(self):
"""Draw the figure using the renderer."""
"""
Draw the figure using the renderer.

It is important that this method actually walk the artist tree
even if not output is produced because this will trigger
deferred work (like computing limits auto-limits and tick
values) that users may want access to before saving to disk.
"""
renderer = RendererTemplate(self.figure.dpi)
self.figure.draw(renderer)

Expand Down
31 changes: 31 additions & 0 deletions lib/matplotlib/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import locale
import logging
import subprocess
from pathlib import Path
from tempfile import TemporaryDirectory

import matplotlib as mpl
from matplotlib import _api
Expand Down Expand Up @@ -44,3 +47,31 @@ def setup():
# are not necessarily the default values as specified in rcsetup.py.
set_font_settings_for_testing()
set_reproducibility_for_testing()


def check_for_pgf(texsystem):
Comment thread
QuLogic marked this conversation as resolved.
"""
Check if a given TeX system + pgf is available

Parameters
----------
texsystem : str
The executable name to check
"""
with TemporaryDirectory() as tmpdir:
tex_path = Path(tmpdir, "test.tex")
tex_path.write_text(r"""
\documentclass{minimal}
\usepackage{pgf}
\begin{document}
\typeout{pgfversion=\pgfversion}
\makeatletter
\@@end
""")
try:
subprocess.check_call(
[texsystem, "-halt-on-error", str(tex_path)], cwd=tmpdir,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except (OSError, subprocess.CalledProcessError):
return False
return True
39 changes: 39 additions & 0 deletions lib/matplotlib/tests/test_backend_bases.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re

from matplotlib.testing import check_for_pgf
from matplotlib.backend_bases import (
FigureCanvasBase, LocationEvent, MouseButton, MouseEvent,
NavigationToolbar2, RendererBase)
Expand All @@ -13,6 +14,9 @@
import numpy as np
import pytest

needs_xelatex = pytest.mark.skipif(not check_for_pgf('xelatex'),
reason='xelatex + pgf is required')


def test_uses_per_path():
id = transforms.Affine2D()
Expand Down Expand Up @@ -187,3 +191,38 @@ def test_toolbar_zoompan():
assert ax.get_navigate_mode() == "ZOOM"
ax.figure.canvas.manager.toolmanager.trigger_tool('pan')
assert ax.get_navigate_mode() == "PAN"


@pytest.mark.parametrize(
"backend", ['svg', 'ps', 'pdf', pytest.param('pgf', marks=needs_xelatex)]
)
def test_draw(backend):
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvas
test_backend = pytest.importorskip(
f'matplotlib.backends.backend_{backend}'
)
TestCanvas = test_backend.FigureCanvas
fig_test = Figure(constrained_layout=True)
TestCanvas(fig_test)
axes_test = fig_test.subplots(2, 2)

# defaults to FigureCanvasBase
fig_agg = Figure(constrained_layout=True)
# put a backends.backend_agg.FigureCanvas on it
FigureCanvas(fig_agg)
axes_agg = fig_agg.subplots(2, 2)

init_pos = [ax.get_position() for ax in axes_test.ravel()]

fig_test.canvas.draw()
fig_agg.canvas.draw()

layed_out_pos_test = [ax.get_position() for ax in axes_test.ravel()]
layed_out_pos_agg = [ax.get_position() for ax in axes_agg.ravel()]

for init, placed in zip(init_pos, layed_out_pos_test):
assert not np.allclose(init, placed, atol=0.005)

for ref, test in zip(layed_out_pos_agg, layed_out_pos_test):
np.testing.assert_allclose(ref, test, atol=0.005)
32 changes: 5 additions & 27 deletions lib/matplotlib/tests/test_backend_pgf.py