Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/axes/_subplots.py : 33%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import functools
2import uuid
4from matplotlib import cbook, docstring
5import matplotlib.artist as martist
6from matplotlib.axes._axes import Axes
7from matplotlib.gridspec import GridSpec, SubplotSpec
8import matplotlib._layoutbox as layoutbox
11class SubplotBase:
12 """
13 Base class for subplots, which are :class:`Axes` instances with
14 additional methods to facilitate generating and manipulating a set
15 of :class:`Axes` within a figure.
16 """
18 def __init__(self, fig, *args, **kwargs):
19 """
20 Parameters
21 ----------
22 fig : `matplotlib.figure.Figure`
24 *args : tuple (*nrows*, *ncols*, *index*) or int
25 The array of subplots in the figure has dimensions ``(nrows,
26 ncols)``, and *index* is the index of the subplot being created.
27 *index* starts at 1 in the upper left corner and increases to the
28 right.
30 If *nrows*, *ncols*, and *index* are all single digit numbers, then
31 *args* can be passed as a single 3-digit number (e.g. 234 for
32 (2, 3, 4)).
33 """
35 self.figure = fig
37 if len(args) == 1:
38 if isinstance(args[0], SubplotSpec):
39 self._subplotspec = args[0]
40 else:
41 try:
42 s = str(int(args[0]))
43 rows, cols, num = map(int, s)
44 except ValueError:
45 raise ValueError('Single argument to subplot must be '
46 'a 3-digit integer')
47 self._subplotspec = GridSpec(rows, cols,
48 figure=self.figure)[num - 1]
49 # num - 1 for converting from MATLAB to python indexing
50 elif len(args) == 3:
51 rows, cols, num = args
52 rows = int(rows)
53 cols = int(cols)
54 if rows <= 0:
55 raise ValueError(f'Number of rows must be > 0, not {rows}')
56 if cols <= 0:
57 raise ValueError(f'Number of columns must be > 0, not {cols}')
58 if isinstance(num, tuple) and len(num) == 2:
59 num = [int(n) for n in num]
60 self._subplotspec = GridSpec(
61 rows, cols,
62 figure=self.figure)[(num[0] - 1):num[1]]
63 else:
64 if num < 1 or num > rows*cols:
65 raise ValueError(
66 f"num must be 1 <= num <= {rows*cols}, not {num}")
67 self._subplotspec = GridSpec(
68 rows, cols, figure=self.figure)[int(num) - 1]
69 # num - 1 for converting from MATLAB to python indexing
70 else:
71 raise ValueError(f'Illegal argument(s) to subplot: {args}')
73 self.update_params()
75 # _axes_class is set in the subplot_class_factory
76 self._axes_class.__init__(self, fig, self.figbox, **kwargs)
77 # add a layout box to this, for both the full axis, and the poss
78 # of the axis. We need both because the axes may become smaller
79 # due to parasitic axes and hence no longer fill the subplotspec.
80 if self._subplotspec._layoutbox is None:
81 self._layoutbox = None
82 self._poslayoutbox = None
83 else:
84 name = self._subplotspec._layoutbox.name + '.ax'
85 name = name + layoutbox.seq_id()
86 self._layoutbox = layoutbox.LayoutBox(
87 parent=self._subplotspec._layoutbox,
88 name=name,
89 artist=self)
90 self._poslayoutbox = layoutbox.LayoutBox(
91 parent=self._layoutbox,
92 name=self._layoutbox.name+'.pos',
93 pos=True, subplot=True, artist=self)
95 def __reduce__(self):
96 # get the first axes class which does not inherit from a subplotbase
97 axes_class = next(
98 c for c in type(self).__mro__
99 if issubclass(c, Axes) and not issubclass(c, SubplotBase))
100 return (_picklable_subplot_class_constructor,
101 (axes_class,),
102 self.__getstate__())
104 def get_geometry(self):
105 """Get the subplot geometry, e.g., (2, 2, 3)."""
106 rows, cols, num1, num2 = self.get_subplotspec().get_geometry()
107 return rows, cols, num1 + 1 # for compatibility
109 # COVERAGE NOTE: Never used internally or from examples
110 def change_geometry(self, numrows, numcols, num):
111 """Change subplot geometry, e.g., from (1, 1, 1) to (2, 2, 3)."""
112 self._subplotspec = GridSpec(numrows, numcols,
113 figure=self.figure)[num - 1]
114 self.update_params()
115 self.set_position(self.figbox)
117 def get_subplotspec(self):
118 """get the SubplotSpec instance associated with the subplot"""
119 return self._subplotspec
121 def set_subplotspec(self, subplotspec):
122 """set the SubplotSpec instance associated with the subplot"""
123 self._subplotspec = subplotspec
125 def get_gridspec(self):
126 """get the GridSpec instance associated with the subplot"""
127 return self._subplotspec.get_gridspec()
129 def update_params(self):
130 """update the subplot position from fig.subplotpars"""
131 self.figbox, _, _, self.numRows, self.numCols = \
132 self.get_subplotspec().get_position(self.figure,
133 return_all=True)
135 @cbook.deprecated("3.2", alternative="ax.get_subplotspec().rowspan.start")
136 @property
137 def rowNum(self):
138 return self.get_subplotspec().rowspan.start
140 @cbook.deprecated("3.2", alternative="ax.get_subplotspec().colspan.start")
141 @property
142 def colNum(self):
143 return self.get_subplotspec().colspan.start
145 def is_first_row(self):
146 return self.get_subplotspec().rowspan.start == 0
148 def is_last_row(self):
149 return self.get_subplotspec().rowspan.stop == self.get_gridspec().nrows
151 def is_first_col(self):
152 return self.get_subplotspec().colspan.start == 0
154 def is_last_col(self):
155 return self.get_subplotspec().colspan.stop == self.get_gridspec().ncols
157 def label_outer(self):
158 """
159 Only show "outer" labels and tick labels.
161 x-labels are only kept for subplots on the last row; y-labels only for
162 subplots on the first column.
163 """
164 lastrow = self.is_last_row()
165 firstcol = self.is_first_col()
166 if not lastrow:
167 for label in self.get_xticklabels(which="both"):
168 label.set_visible(False)
169 self.get_xaxis().get_offset_text().set_visible(False)
170 self.set_xlabel("")
171 if not firstcol:
172 for label in self.get_yticklabels(which="both"):
173 label.set_visible(False)
174 self.get_yaxis().get_offset_text().set_visible(False)
175 self.set_ylabel("")
177 def _make_twin_axes(self, *args, **kwargs):
178 """Make a twinx axes of self. This is used for twinx and twiny."""
179 if 'sharex' in kwargs and 'sharey' in kwargs:
180 # The following line is added in v2.2 to avoid breaking Seaborn,
181 # which currently uses this internal API.
182 if kwargs["sharex"] is not self and kwargs["sharey"] is not self:
183 raise ValueError("Twinned Axes may share only one axis")
184 # The dance here with label is to force add_subplot() to create a new
185 # Axes (by passing in a label never seen before). Note that this does
186 # not affect plot reactivation by subplot() as twin axes can never be
187 # reactivated by subplot().
188 sentinel = str(uuid.uuid4())
189 real_label = kwargs.pop("label", sentinel)
190 twin = self.figure.add_subplot(
191 self.get_subplotspec(), *args, label=sentinel, **kwargs)
192 if real_label is not sentinel:
193 twin.set_label(real_label)
194 self.set_adjustable('datalim')
195 twin.set_adjustable('datalim')
196 if self._layoutbox is not None and twin._layoutbox is not None:
197 # make the layout boxes be explicitly the same
198 twin._layoutbox.constrain_same(self._layoutbox)
199 twin._poslayoutbox.constrain_same(self._poslayoutbox)
200 self._twinned_axes.join(self, twin)
201 return twin
204# this here to support cartopy which was using a private part of the
205# API to register their Axes subclasses.
207# In 3.1 this should be changed to a dict subclass that warns on use
208# In 3.3 to a dict subclass that raises a useful exception on use
209# In 3.4 should be removed
211# The slow timeline is to give cartopy enough time to get several
212# release out before we break them.
213_subplot_classes = {}
216@functools.lru_cache(None)
217def subplot_class_factory(axes_class=None):
218 """
219 This makes a new class that inherits from `.SubplotBase` and the
220 given axes_class (which is assumed to be a subclass of `.axes.Axes`).
221 This is perhaps a little bit roundabout to make a new class on
222 the fly like this, but it means that a new Subplot class does
223 not have to be created for every type of Axes.
224 """
225 if axes_class is None:
226 axes_class = Axes
227 try:
228 # Avoid creating two different instances of GeoAxesSubplot...
229 # Only a temporary backcompat fix. This should be removed in
230 # 3.4
231 return next(cls for cls in SubplotBase.__subclasses__()
232 if cls.__bases__ == (SubplotBase, axes_class))
233 except StopIteration:
234 return type("%sSubplot" % axes_class.__name__,
235 (SubplotBase, axes_class),
236 {'_axes_class': axes_class})
239# This is provided for backward compatibility
240Subplot = subplot_class_factory()
243def _picklable_subplot_class_constructor(axes_class):
244 """
245 This stub class exists to return the appropriate subplot class when called
246 with an axes class. This is purely to allow pickling of Axes and Subplots.
247 """
248 subplot_class = subplot_class_factory(axes_class)
249 return subplot_class.__new__(subplot_class)
252docstring.interpd.update(Axes=martist.kwdoc(Axes))
253docstring.dedent_interpd(Axes.__init__)
255docstring.interpd.update(Subplot=martist.kwdoc(Axes))