From c6bfb7c6c86cd0cb4ce2bcb53d61ac3a9f63f4b9 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Thu, 15 Apr 2021 07:24:13 -0700 Subject: [PATCH 1/3] ENH: add user-facing no-output draw --- .../next_whats_new/fig_draw_no_output.rst | 8 +++++++ lib/matplotlib/figure.py | 9 ++++++- .../tests/test_constrainedlayout.py | 24 +++++++++---------- lib/matplotlib/tests/test_figure.py | 4 ++-- 4 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 doc/users/next_whats_new/fig_draw_no_output.rst diff --git a/doc/users/next_whats_new/fig_draw_no_output.rst b/doc/users/next_whats_new/fig_draw_no_output.rst new file mode 100644 index 000000000000..5948d75a2f70 --- /dev/null +++ b/doc/users/next_whats_new/fig_draw_no_output.rst @@ -0,0 +1,8 @@ +Figure now has draw_no_output method +------------------------------------ + +Rarely, the user will want to trigger a draw without making output to +either the screen or a file. This is useful for determining the final +position of artists on the figure that require a draw like text. +This could be accomplished via ``fig.canvas.draw()`` but that is +not user-facing, so a new method on `.Figure.draw_no_output` is provided. \ No newline at end of file diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 6691e91713a7..0c960efe3196 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -26,7 +26,7 @@ from matplotlib.artist import ( Artist, allow_rasterization, _finalize_rasterization) from matplotlib.backend_bases import ( - FigureCanvasBase, NonGuiException, MouseButton) + FigureCanvasBase, NonGuiException, MouseButton, _no_output_draw) import matplotlib._api as _api import matplotlib.cbook as cbook import matplotlib.colorbar as cbar @@ -2739,6 +2739,13 @@ def draw(self, renderer): self.canvas.draw_event(renderer) + def draw_no_output(self): + """ + Draw the figure with no output. Useful to get the final size of + artists that require a draw before their size is known (e.g. text). + """ + _no_output_draw(self) + def draw_artist(self, a): """ Draw `.Artist` *a* only. diff --git a/lib/matplotlib/tests/test_constrainedlayout.py b/lib/matplotlib/tests/test_constrainedlayout.py index 67474628e7bf..d8bd4cffde94 100644 --- a/lib/matplotlib/tests/test_constrainedlayout.py +++ b/lib/matplotlib/tests/test_constrainedlayout.py @@ -134,7 +134,7 @@ def test_constrained_layout7(): for gs in gsl: fig.add_subplot(gs) # need to trigger a draw to get warning - fig.draw(fig.canvas.get_renderer()) + fig.draw_no_output() @image_comparison(['constrained_layout8.png']) @@ -327,7 +327,7 @@ def test_constrained_layout18(): ax2 = ax.twinx() example_plot(ax) example_plot(ax2, fontsize=24) - fig.canvas.draw() + fig.draw_no_output() assert all(ax.get_position().extents == ax2.get_position().extents) @@ -339,7 +339,7 @@ def test_constrained_layout19(): example_plot(ax2, fontsize=24) ax2.set_title('') ax.set_title('') - fig.canvas.draw() + fig.draw_no_output() assert all(ax.get_position().extents == ax2.get_position().extents) @@ -359,11 +359,11 @@ def test_constrained_layout21(): fig, ax = plt.subplots(constrained_layout=True) fig.suptitle("Suptitle0") - fig.canvas.draw() + fig.draw_no_output() extents0 = np.copy(ax.get_position().extents) fig.suptitle("Suptitle1") - fig.canvas.draw() + fig.draw_no_output() extents1 = np.copy(ax.get_position().extents) np.testing.assert_allclose(extents0, extents1) @@ -373,11 +373,11 @@ def test_constrained_layout22(): """#11035: suptitle should not be include in CL if manually positioned""" fig, ax = plt.subplots(constrained_layout=True) - fig.canvas.draw() + fig.draw_no_output() extents0 = np.copy(ax.get_position().extents) fig.suptitle("Suptitle", y=0.5) - fig.canvas.draw() + fig.draw_no_output() extents1 = np.copy(ax.get_position().extents) np.testing.assert_allclose(extents0, extents1) @@ -425,7 +425,7 @@ def test_hidden_axes(): # (as does a gridspec slot that is empty) fig, axs = plt.subplots(2, 2, constrained_layout=True) axs[0, 1].set_visible(False) - fig.canvas.draw() + fig.draw_no_output() extents1 = np.copy(axs[0, 0].get_position().extents) np.testing.assert_allclose( @@ -451,7 +451,7 @@ def test_colorbar_align(): fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0.1, wspace=0.1) - fig.canvas.draw() + fig.draw_no_output() if location in ['left', 'right']: np.testing.assert_allclose(cbs[0].ax.get_position().x0, cbs[2].ax.get_position().x0) @@ -493,7 +493,7 @@ def test_colorbars_no_overlapH(): def test_manually_set_position(): fig, axs = plt.subplots(1, 2, constrained_layout=True) axs[0].set_position([0.2, 0.2, 0.3, 0.3]) - fig.canvas.draw() + fig.draw_no_output() pp = axs[0].get_position() np.testing.assert_allclose(pp, [[0.2, 0.2], [0.5, 0.5]]) @@ -501,7 +501,7 @@ def test_manually_set_position(): axs[0].set_position([0.2, 0.2, 0.3, 0.3]) pc = axs[0].pcolormesh(np.random.rand(20, 20)) fig.colorbar(pc, ax=axs[0]) - fig.canvas.draw() + fig.draw_no_output() pp = axs[0].get_position() np.testing.assert_allclose(pp, [[0.2, 0.2], [0.44, 0.5]]) @@ -546,7 +546,7 @@ def test_align_labels(): fig.align_ylabels(axs=(ax3, ax1, ax2)) - fig.canvas.draw() + fig.draw_no_output() after_align = [ax1.yaxis.label.get_window_extent(), ax2.yaxis.label.get_window_extent(), ax3.yaxis.label.get_window_extent()] diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 635037c43442..8d589fc5890c 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -415,11 +415,11 @@ def test_autofmt_xdate(which): @pytest.mark.style('default') def test_change_dpi(): fig = plt.figure(figsize=(4, 4)) - fig.canvas.draw() + fig.draw_no_output() assert fig.canvas.renderer.height == 400 assert fig.canvas.renderer.width == 400 fig.dpi = 50 - fig.canvas.draw() + fig.draw_no_output() assert fig.canvas.renderer.height == 200 assert fig.canvas.renderer.width == 200 From 15a62c1f9ee87de7cf70fe59a950529f122978a5 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 16 Apr 2021 12:38:44 -0700 Subject: [PATCH 2/3] ENH: promote no output to figure level --- lib/matplotlib/backend_bases.py | 6 +++--- lib/matplotlib/backends/backend_pdf.py | 4 ++-- lib/matplotlib/backends/backend_pgf.py | 4 ++-- lib/matplotlib/backends/backend_ps.py | 4 ++-- lib/matplotlib/backends/backend_svg.py | 4 ++-- lib/matplotlib/figure.py | 13 +++++++------ 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 59f8e7856bbc..974846f63995 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1584,9 +1584,9 @@ def _draw(renderer): raise Done(renderer) def _no_output_draw(figure): - renderer = _get_renderer(figure) - with renderer._draw_disabled(): - figure.draw(renderer) + # _no_output_draw was promoted to the figure level, but + # keep this here in case someone was calling it... + figure.draw_no_output() def _is_non_interactive_terminal_ipython(ip): diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index c63aaca2024b..530df4b12dd2 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -29,7 +29,7 @@ from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw) + GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font @@ -2718,7 +2718,7 @@ def print_pdf(self, filename, *, file.close() def draw(self): - _no_output_draw(self.figure) + self.figure.draw_no_output() return super().draw() diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 69b32236ad5d..c6db4931e370 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -19,7 +19,7 @@ from matplotlib import _api, cbook, font_manager as fm from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw + GraphicsContextBase, RendererBase ) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.backends.backend_pdf import ( @@ -889,7 +889,7 @@ def get_renderer(self): return RendererPgf(self.figure, None) def draw(self): - _no_output_draw(self.figure) + self.figure.draw_no_output() return super().draw() diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 9da5063cea56..24a483a54906 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -23,7 +23,7 @@ from matplotlib.afm import AFM from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, RendererBase, _no_output_draw) + GraphicsContextBase, RendererBase) 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 @@ -1112,7 +1112,7 @@ def _print_figure_tex( _move_path_to_path_or_stream(tmpfile, outfile) def draw(self): - _no_output_draw(self.figure) + self.figure.draw_no_output() return super().draw() diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index a1311e614dd1..51c0c17b8089 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -17,7 +17,7 @@ from matplotlib import _api, cbook from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, - RendererBase, _no_output_draw) + RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.colors import rgb2hex from matplotlib.dates import UTC @@ -1343,7 +1343,7 @@ def get_default_filetype(self): return 'svg' def draw(self): - _no_output_draw(self.figure) + self.figure.draw_no_output() return super().draw() diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 0c960efe3196..59850a5a1640 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -26,7 +26,7 @@ from matplotlib.artist import ( Artist, allow_rasterization, _finalize_rasterization) from matplotlib.backend_bases import ( - FigureCanvasBase, NonGuiException, MouseButton, _no_output_draw) + FigureCanvasBase, NonGuiException, MouseButton, _get_renderer) import matplotlib._api as _api import matplotlib.cbook as cbook import matplotlib.colorbar as cbar @@ -2744,7 +2744,9 @@ def draw_no_output(self): Draw the figure with no output. Useful to get the final size of artists that require a draw before their size is known (e.g. text). """ - _no_output_draw(self) + renderer = _get_renderer(self) + with renderer._draw_disabled(): + self.draw(renderer) def draw_artist(self, a): """ @@ -3022,7 +3024,6 @@ def execute_constrained_layout(self, renderer=None): """ from matplotlib._constrained_layout import do_constrained_layout - from matplotlib.tight_layout import get_renderer _log.debug('Executing constrainedlayout') if self._layoutgrid is None: @@ -3040,7 +3041,7 @@ def execute_constrained_layout(self, renderer=None): w_pad = w_pad / width h_pad = h_pad / height if renderer is None: - renderer = get_renderer(fig) + renderer = _get_renderer(fig) do_constrained_layout(fig, renderer, h_pad, w_pad, hspace, wspace) def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): @@ -3070,7 +3071,7 @@ def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): """ from .tight_layout import ( - get_renderer, get_subplotspec_list, get_tight_layout_figure) + get_subplotspec_list, get_tight_layout_figure) from contextlib import suppress subplotspec_list = get_subplotspec_list(self.axes) if None in subplotspec_list: @@ -3078,7 +3079,7 @@ def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): "compatible with tight_layout, so results " "might be incorrect.") - renderer = get_renderer(self) + renderer = _get_renderer(self) ctx = (renderer._draw_disabled() if hasattr(renderer, '_draw_disabled') else suppress()) From 7e059752d2aca517e7b9304728819e1effb1b63a Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 16 Apr 2021 12:41:06 -0700 Subject: [PATCH 3/3] DOC: update whats new note --- doc/users/next_whats_new/fig_draw_no_output.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/users/next_whats_new/fig_draw_no_output.rst b/doc/users/next_whats_new/fig_draw_no_output.rst index 5948d75a2f70..293c6590b8c9 100644 --- a/doc/users/next_whats_new/fig_draw_no_output.rst +++ b/doc/users/next_whats_new/fig_draw_no_output.rst @@ -3,6 +3,8 @@ Figure now has draw_no_output method Rarely, the user will want to trigger a draw without making output to either the screen or a file. This is useful for determining the final -position of artists on the figure that require a draw like text. -This could be accomplished via ``fig.canvas.draw()`` but that is -not user-facing, so a new method on `.Figure.draw_no_output` is provided. \ No newline at end of file +position of artists on the figure that require a draw, like text artists. +This could be accomplished via ``fig.canvas.draw()`` but has side effects, +sometimes requires an open file, and is documented on an object most users +do not need to access. The `.Figure.draw_no_output` is provided to trigger +a draw without pushing to the final output, and with fewer side effects. \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy