Skip to content

Commit e60a0f3

Browse files
Add fig.add_artist method
1 parent aae55fc commit e60a0f3

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Figure has an `~.figure.Figure.add_artist` method
2+
-------------------------------------------------
3+
4+
A method `~.figure.Figure.add_artist` has been added to the
5+
:class:`~.figure.Figure` class, which allows artists to be added directly
6+
to a figure. E.g.
7+
8+
::
9+
10+
circ = plt.Circle((.7, .5), .05)
11+
fig.add_artist(circ)
12+
13+
In case the added artist has no transform set previously, it will be set to
14+
the figure transform (``fig.transFigure``).
15+
This new method may be useful for adding artists to figures without axes or to
16+
easily position static elements in figure coordinates.

lib/matplotlib/figure.py

+36
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,42 @@ def fixlist(args):
10451045
key = fixlist(args), fixitems(kwargs.items())
10461046
return key
10471047

1048+
def add_artist(self, artist, clip=False):
1049+
"""
1050+
Add any :class:`~matplotlib.artist.Artist` to the figure.
1051+
1052+
Usually artists are added to axes objects using
1053+
:meth:`matplotlib.axes.Axes.add_artist`, but use this method in the
1054+
rare cases that adding directly to the figure is necessary.
1055+
1056+
Parameters
1057+
----------
1058+
artist : `~matplotlib.artist.Artist`
1059+
The artist to add to the figure. If the added artist has no
1060+
transform previously set, its transform will be set to
1061+
``figure.transFigure``.
1062+
clip : bool, optional, default ``False``
1063+
An optional parameter ``clip`` determines whether the added artist
1064+
should be clipped by the figure patch. Default is *False*,
1065+
i.e. no clipping.
1066+
1067+
Returns
1068+
-------
1069+
artist : The added `~matplotlib.artist.Artist`
1070+
"""
1071+
artist.set_figure(self)
1072+
self.artists.append(artist)
1073+
artist._remove_method = self.artists.remove
1074+
1075+
if not artist.is_transform_set():
1076+
artist.set_transform(self.transFigure)
1077+
1078+
if clip:
1079+
artist.set_clip_path(self.patch)
1080+
1081+
self.stale = True
1082+
return artist
1083+
10481084
@docstring.dedent_interpd
10491085
def add_axes(self, *args, **kwargs):
10501086
"""

lib/matplotlib/tests/test_figure.py

+29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
import warnings
3+
import io
34

45
from matplotlib import rcParams
56
from matplotlib.testing.decorators import image_comparison
@@ -373,6 +374,34 @@ def test_figure_repr():
373374
assert repr(fig) == "<Figure size 100x200 with 0 Axes>"
374375

375376

377+
def test_add_artist():
378+
fig, ax = plt.subplots(dpi=100)
379+
l1 = plt.Line2D([.2, .7], [.7, .7])
380+
l2 = plt.Line2D([.2, .7], [.8, .8])
381+
r1 = plt.Circle((20, 20), 100, transform=None)
382+
r2 = plt.Circle((.7, .5), .05)
383+
r3 = plt.Circle((4.5, .8), .55, transform=fig.dpi_scale_trans,
384+
facecolor='crimson')
385+
for a in [l1, l2, r1, r2, r3]:
386+
fig.add_artist(a)
387+
l2.remove()
388+
buf1 = io.BytesIO()
389+
fig.savefig(buf1)
390+
391+
fig2, ax2 = plt.subplots(dpi=100)
392+
l1 = plt.Line2D([.2, .7], [.7, .7], transform=fig2.transFigure)
393+
r1 = plt.Circle((20, 20), 100, transform=None, clip_on=False, zorder=20)
394+
r2 = plt.Circle((.7, .5), .05, transform=fig2.transFigure)
395+
r3 = plt.Circle((4.5, .8), .55, transform=fig.dpi_scale_trans,
396+
facecolor='crimson', clip_on=False, zorder=20)
397+
for a in [l1, r1, r2, r3]:
398+
ax2.add_artist(a)
399+
buf2 = io.BytesIO()
400+
fig2.savefig(buf2)
401+
402+
assert buf1.getvalue() == buf2.getvalue()
403+
404+
376405
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
377406
@pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
378407
def test_fspath(fmt, tmpdir):

0 commit comments

Comments
 (0)