From b6326cc47a05ad68b1ae26996b0c30edc984b0af Mon Sep 17 00:00:00 2001 From: ImportanceOfBeingErnest Date: Sat, 12 May 2018 00:33:46 +0200 Subject: [PATCH] Add fig.add_artist method --- doc/users/whats_new.rst | 18 +++++++++++++++ lib/matplotlib/figure.py | 36 +++++++++++++++++++++++++++++ lib/matplotlib/tests/test_figure.py | 28 +++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index dcd0c899b6dd..9a9731faedea 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -190,6 +190,7 @@ specify a number that is close (i.e. ``ax.title.set_position(0.5, 1.01)``) and the title will not be moved via this algorithm. + New convenience methods for GridSpec ------------------------------------ @@ -210,6 +211,23 @@ now call `.Figure.add_gridspec` and for the latter `.SubplotSpec.subgridspec`. fig.add_subplot(gssub[0, i]) +Figure has an `~.figure.Figure.add_artist` method +------------------------------------------------- + +A method `~.figure.Figure.add_artist` has been added to the +:class:`~.figure.Figure` class, which allows artists to be added directly +to a figure. E.g. + +:: + + circ = plt.Circle((.7, .5), .05) + fig.add_artist(circ) + +In case the added artist has no transform set previously, it will be set to +the figure transform (``fig.transFigure``). +This new method may be useful for adding artists to figures without axes or to +easily position static elements in figure coordinates. + diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index ef400898d802..1bfb3c02d71b 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1048,6 +1048,42 @@ def fixlist(args): key = fixlist(args), fixitems(kwargs.items()) return key + def add_artist(self, artist, clip=False): + """ + Add any :class:`~matplotlib.artist.Artist` to the figure. + + Usually artists are added to axes objects using + :meth:`matplotlib.axes.Axes.add_artist`, but use this method in the + rare cases that adding directly to the figure is necessary. + + Parameters + ---------- + artist : `~matplotlib.artist.Artist` + The artist to add to the figure. If the added artist has no + transform previously set, its transform will be set to + ``figure.transFigure``. + clip : bool, optional, default ``False`` + An optional parameter ``clip`` determines whether the added artist + should be clipped by the figure patch. Default is *False*, + i.e. no clipping. + + Returns + ------- + artist : The added `~matplotlib.artist.Artist` + """ + artist.set_figure(self) + self.artists.append(artist) + artist._remove_method = self.artists.remove + + if not artist.is_transform_set(): + artist.set_transform(self.transFigure) + + if clip: + artist.set_clip_path(self.patch) + + self.stale = True + return artist + @docstring.dedent_interpd def add_axes(self, *args, **kwargs): """ diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 3e07838da984..16db9c3ca3ca 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -2,7 +2,7 @@ import warnings from matplotlib import rcParams -from matplotlib.testing.decorators import image_comparison +from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.axes import Axes from matplotlib.ticker import AutoMinorLocator, FixedFormatter import matplotlib.pyplot as plt @@ -381,6 +381,32 @@ def test_warn_cl_plus_tl(): assert not(fig.get_constrained_layout()) +@check_figures_equal(extensions=["png", "pdf", "svg"]) +def test_add_artist(fig_test, fig_ref): + fig_test.set_dpi(100) + fig_ref.set_dpi(100) + + ax = fig_test.subplots() + l1 = plt.Line2D([.2, .7], [.7, .7]) + l2 = plt.Line2D([.2, .7], [.8, .8]) + r1 = plt.Circle((20, 20), 100, transform=None) + r2 = plt.Circle((.7, .5), .05) + r3 = plt.Circle((4.5, .8), .55, transform=fig_test.dpi_scale_trans, + facecolor='crimson') + for a in [l1, l2, r1, r2, r3]: + fig_test.add_artist(a) + l2.remove() + + ax2 = fig_ref.subplots() + l1 = plt.Line2D([.2, .7], [.7, .7], transform=fig_ref.transFigure) + r1 = plt.Circle((20, 20), 100, transform=None, clip_on=False, zorder=20) + r2 = plt.Circle((.7, .5), .05, transform=fig_ref.transFigure) + r3 = plt.Circle((4.5, .8), .55, transform=fig_ref.dpi_scale_trans, + facecolor='crimson', clip_on=False, zorder=20) + for a in [l1, r1, r2, r3]: + ax2.add_artist(a) + + @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+") @pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"]) def test_fspath(fmt, tmpdir):