1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

import functools 

import warnings 

 

from matplotlib import docstring 

import matplotlib.artist as martist 

from matplotlib.axes._axes import Axes 

from matplotlib.gridspec import GridSpec, SubplotSpec 

import matplotlib._layoutbox as layoutbox 

 

 

class SubplotBase(object): 

""" 

Base class for subplots, which are :class:`Axes` instances with 

additional methods to facilitate generating and manipulating a set 

of :class:`Axes` within a figure. 

""" 

 

def __init__(self, fig, *args, **kwargs): 

""" 

*fig* is a :class:`matplotlib.figure.Figure` instance. 

 

*args* is the tuple (*numRows*, *numCols*, *plotNum*), where 

the array of subplots in the figure has dimensions *numRows*, 

*numCols*, and where *plotNum* is the number of the subplot 

being created. *plotNum* starts at 1 in the upper left 

corner and increases to the right. 

 

If *numRows* <= *numCols* <= *plotNum* < 10, *args* can be the 

decimal integer *numRows* * 100 + *numCols* * 10 + *plotNum*. 

""" 

 

self.figure = fig 

 

if len(args) == 1: 

if isinstance(args[0], SubplotSpec): 

self._subplotspec = args[0] 

else: 

try: 

s = str(int(args[0])) 

rows, cols, num = map(int, s) 

except ValueError: 

raise ValueError('Single argument to subplot must be ' 

'a 3-digit integer') 

self._subplotspec = GridSpec(rows, cols, 

figure=self.figure)[num - 1] 

# num - 1 for converting from MATLAB to python indexing 

elif len(args) == 3: 

rows, cols, num = args 

rows = int(rows) 

cols = int(cols) 

if isinstance(num, tuple) and len(num) == 2: 

num = [int(n) for n in num] 

self._subplotspec = GridSpec( 

rows, cols, 

figure=self.figure)[(num[0] - 1):num[1]] 

else: 

if num < 1 or num > rows*cols: 

raise ValueError( 

("num must be 1 <= num <= {maxn}, not {num}" 

).format(maxn=rows*cols, num=num)) 

self._subplotspec = GridSpec( 

rows, cols, figure=self.figure)[int(num) - 1] 

# num - 1 for converting from MATLAB to python indexing 

else: 

raise ValueError('Illegal argument(s) to subplot: %s' % (args,)) 

 

self.update_params() 

 

# _axes_class is set in the subplot_class_factory 

self._axes_class.__init__(self, fig, self.figbox, **kwargs) 

# add a layout box to this, for both the full axis, and the poss 

# of the axis. We need both because the axes may become smaller 

# due to parasitic axes and hence no longer fill the subplotspec. 

if self._subplotspec._layoutbox is None: 

self._layoutbox = None 

self._poslayoutbox = None 

else: 

name = self._subplotspec._layoutbox.name + '.ax' 

name = name + layoutbox.seq_id() 

self._layoutbox = layoutbox.LayoutBox( 

parent=self._subplotspec._layoutbox, 

name=name, 

artist=self) 

self._poslayoutbox = layoutbox.LayoutBox( 

parent=self._layoutbox, 

name=self._layoutbox.name+'.pos', 

pos=True, subplot=True, artist=self) 

 

def __reduce__(self): 

# get the first axes class which does not inherit from a subplotbase 

axes_class = next( 

c for c in type(self).__mro__ 

if issubclass(c, Axes) and not issubclass(c, SubplotBase)) 

return (_picklable_subplot_class_constructor, 

(axes_class,), 

self.__getstate__()) 

 

def get_geometry(self): 

"""get the subplot geometry, e.g., 2,2,3""" 

rows, cols, num1, num2 = self.get_subplotspec().get_geometry() 

return rows, cols, num1 + 1 # for compatibility 

 

# COVERAGE NOTE: Never used internally or from examples 

def change_geometry(self, numrows, numcols, num): 

"""change subplot geometry, e.g., from 1,1,1 to 2,2,3""" 

self._subplotspec = GridSpec(numrows, numcols, 

figure=self.figure)[num - 1] 

self.update_params() 

self.set_position(self.figbox) 

 

def get_subplotspec(self): 

"""get the SubplotSpec instance associated with the subplot""" 

return self._subplotspec 

 

def set_subplotspec(self, subplotspec): 

"""set the SubplotSpec instance associated with the subplot""" 

self._subplotspec = subplotspec 

 

def get_gridspec(self): 

"""get the GridSpec instance associated with the subplot""" 

return self._subplotspec.get_gridspec() 

 

def update_params(self): 

"""update the subplot position from fig.subplotpars""" 

 

self.figbox, self.rowNum, self.colNum, self.numRows, self.numCols = \ 

self.get_subplotspec().get_position(self.figure, 

return_all=True) 

 

def is_first_col(self): 

return self.colNum == 0 

 

def is_first_row(self): 

return self.rowNum == 0 

 

def is_last_row(self): 

return self.rowNum == self.numRows - 1 

 

def is_last_col(self): 

return self.colNum == self.numCols - 1 

 

# COVERAGE NOTE: Never used internally. 

def label_outer(self): 

"""Only show "outer" labels and tick labels. 

 

x-labels are only kept for subplots on the last row; y-labels only for 

subplots on the first column. 

""" 

lastrow = self.is_last_row() 

firstcol = self.is_first_col() 

if not lastrow: 

for label in self.get_xticklabels(which="both"): 

label.set_visible(False) 

self.get_xaxis().get_offset_text().set_visible(False) 

self.set_xlabel("") 

if not firstcol: 

for label in self.get_yticklabels(which="both"): 

label.set_visible(False) 

self.get_yaxis().get_offset_text().set_visible(False) 

self.set_ylabel("") 

 

def _make_twin_axes(self, *kl, **kwargs): 

""" 

Make a twinx axes of self. This is used for twinx and twiny. 

""" 

from matplotlib.projections import process_projection_requirements 

if 'sharex' in kwargs and 'sharey' in kwargs: 

# The following line is added in v2.2 to avoid breaking Seaborn, 

# which currently uses this internal API. 

if kwargs["sharex"] is not self and kwargs["sharey"] is not self: 

raise ValueError("Twinned Axes may share only one axis.") 

kl = (self.get_subplotspec(),) + kl 

projection_class, kwargs, key = process_projection_requirements( 

self.figure, *kl, **kwargs) 

 

ax2 = subplot_class_factory(projection_class)(self.figure, 

*kl, **kwargs) 

self.figure.add_subplot(ax2) 

self.set_adjustable('datalim') 

ax2.set_adjustable('datalim') 

 

if self._layoutbox is not None and ax2._layoutbox is not None: 

# make the layout boxes be explicitly the same 

ax2._layoutbox.constrain_same(self._layoutbox) 

ax2._poslayoutbox.constrain_same(self._poslayoutbox) 

 

self._twinned_axes.join(self, ax2) 

return ax2 

 

 

# this here to support cartopy which was using a private part of the 

# API to register their Axes subclasses. 

 

# In 3.1 this should be changed to a dict subclass that warns on use 

# In 3.3 to a dict subclass that raises a useful exception on use 

# In 3.4 should be removed 

 

# The slow timeline is to give cartopy enough time to get several 

# release out before we break them. 

_subplot_classes = {} 

 

 

@functools.lru_cache(None) 

def subplot_class_factory(axes_class=None): 

""" 

This makes a new class that inherits from `.SubplotBase` and the 

given axes_class (which is assumed to be a subclass of `.axes.Axes`). 

This is perhaps a little bit roundabout to make a new class on 

the fly like this, but it means that a new Subplot class does 

not have to be created for every type of Axes. 

""" 

if axes_class is None: 

axes_class = Axes 

try: 

# Avoid creating two different instances of GeoAxesSubplot... 

# Only a temporary backcompat fix. This should be removed in 

# 3.4 

return next(cls for cls in SubplotBase.__subclasses__() 

if cls.__bases__ == (SubplotBase, axes_class)) 

except StopIteration: 

return type("%sSubplot" % axes_class.__name__, 

(SubplotBase, axes_class), 

{'_axes_class': axes_class}) 

 

 

# This is provided for backward compatibility 

Subplot = subplot_class_factory() 

 

 

def _picklable_subplot_class_constructor(axes_class): 

""" 

This stub class exists to return the appropriate subplot class when called 

with an axes class. This is purely to allow pickling of Axes and Subplots. 

""" 

subplot_class = subplot_class_factory(axes_class) 

return subplot_class.__new__(subplot_class) 

 

 

docstring.interpd.update(Axes=martist.kwdoc(Axes)) 

docstring.dedent_interpd(Axes.__init__) 

 

docstring.interpd.update(Subplot=martist.kwdoc(Axes))