Introduce ArtistList for FigureBase · matplotlib/matplotlib@9ce5e3d · GitHub
Skip to content

Commit 9ce5e3d

Browse files
committed
Introduce ArtistList for FigureBase
Following #18216 for Axes artists, combine all figure artists except axes and subfigures into a single list and deprecate modifying the lists directly.
1 parent d7bc494 commit 9ce5e3d

13 files changed

Lines changed: 289 additions & 75 deletions

File tree

Lines changed: 7 additions & 0 deletions

galleries/examples/widgets/menu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def __init__(self, fig, menuitems):
113113

114114
item.set_extent(left, bottom, width, height, depth)
115115

116-
fig.artists.append(item)
116+
fig.add_artist(item)
117117
y0 -= maxh + MenuItem.pady
118118

119119
fig.canvas.mpl_connect('motion_notify_event', self.on_move)

galleries/tutorials/artists.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -314,44 +314,51 @@ class in the Matplotlib API, and the one you will be working with most
314314
#
315315
#
316316
# The figure also has its own ``images``, ``lines``, ``patches`` and ``text``
317-
# attributes, which you can use to add primitives directly. When doing so, the
318-
# default coordinate system for the ``Figure`` will simply be in pixels (which
319-
# is not usually what you want). If you instead use Figure-level methods to add
320-
# Artists (e.g., using `.Figure.text` to add text), then the default coordinate
321-
# system will be "figure coordinates" where (0, 0) is the bottom-left of the
322-
# figure and (1, 1) is the top-right of the figure.
323-
#
324-
# As with all ``Artist``\s, you can control this coordinate system by setting
325-
# the transform property. You can explicitly use "figure coordinates" by
326-
# setting the ``Artist`` transform to :attr:`!fig.transFigure`:
317+
# attributes, which you can use to access any primitives that are its direct
318+
# children. Artists may be added with the `~.Figure.add_artist` method.
327319

328320
import matplotlib.lines as lines
329321

330322
fig = plt.figure()
331323

332-
l1 = lines.Line2D([0, 1], [0, 1], transform=fig.transFigure, figure=fig)
333-
l2 = lines.Line2D([0, 1], [1, 0], transform=fig.transFigure, figure=fig)
334-
fig.lines.extend([l1, l2])
324+
line1 = lines.Line2D([0, 1], [0, 1])
325+
line2 = lines.Line2D([0, 1], [1, 0])
326+
for line in line1, line2:
327+
fig.add_artist(line)
335328

336329
plt.show()
337330

331+
# %%
332+
#
333+
# As a convenience for images and text, the helper methods `~.Figure.figimage` and
334+
# `~.Figure.text` create the respective Artists and internally add them to the figure.
335+
#
336+
# As with all ``Artist``\s, you can control the coordinate system by setting
337+
# the transform property (see :ref:`transforms_tutorial`). When using
338+
# `~.Figure.figimage`, the default coordinate system is simply pixels. When
339+
# using `~.Figure.text` or `~.Figure.add_artist`, the default coordinate system
340+
# will be "figure coordinates" where (0, 0) is the bottom-left of the figure
341+
# and (1, 1) is the top-right of the figure.
342+
#
338343
# %%
339344
# Here is a summary of the Artists the Figure contains
340345
#
341346
# ================ ============================================================
342347
# Figure attribute Description
343348
# ================ ============================================================
344349
# axes A list of `~.axes.Axes` instances
350+
# subfigures A list of `.SubFigure` instances
345351
# patch The `.Rectangle` background
346-
# images A list of `.FigureImage` patches -
352+
# images An `~.artist.ArtistList` of `.FigureImage` patches -
347353
# useful for raw pixel display
348-
# legends A list of Figure `.Legend` instances
354+
# legends An `~.artist.ArtistList` of Figure `.Legend` instances
349355
# (different from ``Axes.get_legend()``)
350-
# lines A list of Figure `.Line2D` instances
356+
# lines An `~.artist.ArtistList` of Figure `.Line2D` instances
351357
# (rarely used, see ``Axes.lines``)
352-
# patches A list of Figure `.Patch`\s
358+
# patches An `~.artist.ArtistList` of Figure `.Patch`\s
353359
# (rarely used, see ``Axes.patches``)
354-
# texts A list Figure `.Text` instances
360+
# texts An `~.artist.ArtistList` of Figure `.Text` instances
361+
# artists An `~.artist.ArtistList` of all other `.Artist` instances
355362
# ================ ============================================================
356363
#
357364
# .. _axes-container:

galleries/users_explain/text/annotations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ def __call__(self, x0, y0, width, height, mutation_size):
377377

378378
# %%
379379
# Similarly, you can define a custom `.ConnectionStyle` and a custom `.ArrowStyle`. View
380-
# the source code at `.patches` to learn how each class is defined.
380+
# the source code at `~matplotlib.patches` to learn how each class is defined.
381381
#
382382
# .. _annotation_with_custom_arrow:
383383
#

lib/matplotlib/_api/__init__.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import functools
1515
import itertools
1616
import pathlib
17+
import sys
1718
import warnings
1819

1920
from .deprecation import ( # noqa: F401
@@ -470,7 +471,28 @@ def warn_external(message, category=None):
470471
"""
471472
# Go to Python's `site-packages` or `lib` from an editable install.
472473
basedir = pathlib.Path(__file__).parents[2]
473-
skip_file_prefixes = (str(basedir / 'matplotlib'),
474-
str(basedir / 'mpl_toolkits'))
475-
476-
warnings.warn(message, category, skip_file_prefixes=skip_file_prefixes)
474+
skip_file_prefixes = (
475+
str(basedir / 'matplotlib'),
476+
str(basedir / 'mpl_toolkits'),
477+
# If we subclass a collections.abc class, the user may call an abc method that
478+
# calls our method. For example if we warn within insert on a MutableSequence,
479+
# and the user calls append or extend.
480+
'<frozen _collections_abc>')
481+
482+
stacklevel = 2
483+
if sys.version_info[:2] < (3, 14):
484+
# Including the collections.abc string in skip_file_prefixes is not yet honored.
485+
# Add the relevant frame count to the stacklevel instead.
486+
frame = sys._getframe()
487+
while True:
488+
if frame.f_globals.get("__name__") == 'collections.abc':
489+
stacklevel += 1
490+
491+
frame = frame.f_back
492+
if frame is None:
493+
break
494+
495+
del frame
496+
497+
warnings.warn(message, category, skip_file_prefixes=skip_file_prefixes,
498+
stacklevel=stacklevel)

lib/matplotlib/artist.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,22 +1792,21 @@ def pprint_getters(self):
17921792

17931793
class ArtistList(Sequence):
17941794
"""
1795-
A sublist of Axes children based on their type.
1795+
A sublist of Axes or Figure children based on their type.
17961796
1797-
The type-specific children sublists were made immutable in Matplotlib
1797+
The Axes' type-specific children sublists were made immutable in Matplotlib
17981798
3.7. In the future these artist lists may be replaced by tuples. Use
17991799
as if this is a tuple already.
18001800
"""
1801-
def __init__(self, axes, prop_name, valid_types=None, invalid_types=None):
1801+
def __init__(self, parent, prop_name, valid_types=None, invalid_types=None):
18021802
"""
18031803
Parameters
18041804
----------
1805-
axes : `~matplotlib.axes.Axes`
1806-
The Axes from which this sublist will pull the children
1805+
parent : `~matplotlib.axes.Axes` or `~matplotlib.figure.FigureBase`
1806+
The Axes or (Sub)Figure from which this sublist will pull the children
18071807
Artists.
18081808
prop_name : str
1809-
The property name used to access this sublist from the Axes;
1810-
used to generate deprecation warnings.
1809+
The property name used to access this sublist from the parent.
18111810
valid_types : list of type, optional
18121811
A list of types that determine which children will be returned
18131812
by this sublist. If specified, then the Artists in the sublist
@@ -1820,28 +1819,28 @@ def __init__(self, axes, prop_name, valid_types=None, invalid_types=None):
18201819
sublist will never be an instance of these types. Otherwise, no
18211820
types will be excluded.
18221821
"""
1823-
self._axes = axes
1822+
self._parent = parent
18241823
self._prop_name = prop_name
18251824
self._type_check = lambda artist: (
18261825
(not valid_types or isinstance(artist, valid_types)) and
18271826
(not invalid_types or not isinstance(artist, invalid_types))
18281827
)
18291828

18301829
def __repr__(self):
1831-
return f'<Axes.ArtistList of {len(self)} {self._prop_name}>'
1830+
parent_type = self._parent.__class__.__name__
1831+
return f'<{parent_type}.ArtistList of {len(self)} {self._prop_name}>'
18321832

18331833
def __len__(self):
1834-
return sum(self._type_check(artist)
1835-
for artist in self._axes._children)
1834+
return sum(self._type_check(artist) for artist in self._parent._children)
18361835

18371836
def __iter__(self):
1838-
for artist in list(self._axes._children):
1837+
for artist in list(self._parent._children):
18391838
if self._type_check(artist):
18401839
yield artist
18411840

18421841
def __getitem__(self, key):
18431842
return [artist
1844-
for artist in self._axes._children
1843+
for artist in self._parent._children
18451844
if self._type_check(artist)][key]
18461845

18471846
def __add__(self, other):

lib/matplotlib/artist.pyi

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)