Skip to content

Commit 4285ae0

Browse files
authored
Merge pull request #17107 from timhoffm/spines
Add Spines class as a container for all Axes spines
2 parents 79ae33a + 412ad6a commit 4285ae0

30 files changed

+287
-129
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
``Axes.spines``
2+
---------------
3+
4+
``Axes.spines`` is now a dedicated container class `.Spines` for a set of
5+
`.Spine`\s instead of an ``OrderedDict``. On top of dict-like access,
6+
``Axes.spines`` now also supports some ``pandas.Series``-like features.
7+
8+
Accessing single elements by item or by attribute
9+
10+
ax.spines['top'].set_visible(False)
11+
ax.spines.top.set_visible(False)
12+
13+
Accessing a subset of items::
14+
15+
ax.spines[['top', 'right']].set_visible(False)
16+
17+
Accessing all items simultaneously::
18+
19+
ax.spines[:].set_visible(False)

examples/axes_grid1/demo_axes_rgb.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ def demo_rgb2():
6060

6161
for ax in fig.axes:
6262
ax.tick_params(axis='both', direction='in')
63-
for sp1 in ax.spines.values():
64-
sp1.set_color("w")
63+
ax.spines[:].set_color("w")
6564
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
6665
tick.tick1line.set_markeredgecolor("w")
6766
tick.tick2line.set_markeredgecolor("w")

examples/images_contours_and_fields/image_annotated_heatmap.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,7 @@ def heatmap(data, row_labels, col_labels, ax=None,
149149
rotation_mode="anchor")
150150

151151
# Turn spines off and create white grid.
152-
for edge, spine in ax.spines.items():
153-
spine.set_visible(False)
152+
ax.spines[:].set_visible(False)
154153

155154
ax.set_xticks(np.arange(data.shape[1]+1)-.5, minor=True)
156155
ax.set_yticks(np.arange(data.shape[0]+1)-.5, minor=True)

examples/lines_bars_and_markers/linestyles.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ def plot_linestyles(ax, linestyles, title):
5454
yticks=np.arange(len(linestyles)),
5555
yticklabels=yticklabels)
5656
ax.tick_params(left=False, bottom=False, labelbottom=False)
57-
for spine in ax.spines.values():
58-
spine.set_visible(False)
57+
ax.spines[:].set_visible(False)
5958

6059
# For each line style, add a text annotation with a small offset from
6160
# the reference point (0 in Axes coords, y tick value in Data coords).

examples/lines_bars_and_markers/timeline.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@
8989

9090
# remove y axis and spines
9191
ax.yaxis.set_visible(False)
92-
for spine in ["left", "top", "right"]:
93-
ax.spines[spine].set_visible(False)
92+
ax.spines[["left", "top", "right"]].set_visible(False)
9493

9594
ax.margins(y=0.1)
9695
plt.show()

examples/showcase/bachelors_degrees_by_gender.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@
3232
'#17becf', '#9edae5'])
3333

3434
# Remove the plot frame lines. They are unnecessary here.
35-
ax.spines['top'].set_visible(False)
36-
ax.spines['bottom'].set_visible(False)
37-
ax.spines['right'].set_visible(False)
38-
ax.spines['left'].set_visible(False)
35+
ax.spines[:].set_visible(False)
3936

4037
# Ensure that the axis ticks only show up on the bottom and left of the plot.
4138
# Ticks on the right and top of the plot are generally unnecessary.

examples/showcase/integral.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ def func(x):
4242
fig.text(0.9, 0.05, '$x$')
4343
fig.text(0.1, 0.9, '$y$')
4444

45-
ax.spines['right'].set_visible(False)
46-
ax.spines['top'].set_visible(False)
45+
ax.spines.right.set_visible(False)
46+
ax.spines.top.set_visible(False)
4747
ax.xaxis.set_ticks_position('bottom')
4848

4949
ax.set_xticks((a, b))

examples/showcase/xkcd.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
fig = plt.figure()
1818
ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
19-
ax.spines['right'].set_color('none')
20-
ax.spines['top'].set_color('none')
19+
ax.spines.right.set_color('none')
20+
ax.spines.top.set_color('none')
2121
ax.set_xticks([])
2222
ax.set_yticks([])
2323
ax.set_ylim([-30, 10])
@@ -47,8 +47,8 @@
4747
fig = plt.figure()
4848
ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
4949
ax.bar([0, 1], [0, 100], 0.25)
50-
ax.spines['right'].set_color('none')
51-
ax.spines['top'].set_color('none')
50+
ax.spines.right.set_color('none')
51+
ax.spines.top.set_color('none')
5252
ax.xaxis.set_ticks_position('bottom')
5353
ax.set_xticks([0, 1])
5454
ax.set_xticklabels(['CONFIRMED BY\nEXPERIMENT', 'REFUTED BY\nEXPERIMENT'])

examples/specialty_plots/skewt.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ class SkewXAxes(Axes):
8585
def _init_axis(self):
8686
# Taken from Axes and modified to use our modified X-axis
8787
self.xaxis = SkewXAxis(self)
88-
self.spines['top'].register_axis(self.xaxis)
89-
self.spines['bottom'].register_axis(self.xaxis)
88+
self.spines.top.register_axis(self.xaxis)
89+
self.spines.bottom.register_axis(self.xaxis)
9090
self.yaxis = maxis.YAxis(self)
91-
self.spines['left'].register_axis(self.yaxis)
92-
self.spines['right'].register_axis(self.yaxis)
91+
self.spines.left.register_axis(self.yaxis)
92+
self.spines.right.register_axis(self.yaxis)
9393

9494
def _gen_axes_spines(self):
9595
spines = {'top': SkewSpine.linear_spine(self, 'top'),

examples/subplots_axes_and_figures/broken_axis.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
ax2.set_ylim(0, .22) # most of the data
3232

3333
# hide the spines between ax and ax2
34-
ax1.spines['bottom'].set_visible(False)
35-
ax2.spines['top'].set_visible(False)
34+
ax1.spines.bottom.set_visible(False)
35+
ax2.spines.top.set_visible(False)
3636
ax1.xaxis.tick_top()
3737
ax1.tick_params(labeltop=False) # don't put tick labels at the top
3838
ax2.xaxis.tick_bottom()

examples/ticks_and_spines/centered_spines_with_arrows.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414

1515
fig, ax = plt.subplots()
1616
# Move the left and bottom spines to x = 0 and y = 0, respectively.
17-
ax.spines["left"].set_position(("data", 0))
18-
ax.spines["bottom"].set_position(("data", 0))
17+
ax.spines[["left", "bottom"]].set_position(("data", 0))
1918
# Hide the top and right spines.
20-
ax.spines["top"].set_visible(False)
21-
ax.spines["right"].set_visible(False)
19+
ax.spines[["top", "right"]].set_visible(False)
2220

2321
# Draw arrows (as black triangles: ">k"/"^k") at the end of the axes. In each
2422
# case, one of the coordinates (0) is a data coordinate (i.e., y = 0 or x = 0,

examples/ticks_and_spines/multiple_yaxis_with_spines.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
# Offset the right spine of twin2. The ticks and label have already been
2626
# placed on the right by twinx above.
27-
twin2.spines["right"].set_position(("axes", 1.2))
27+
twin2.spines.right.set_position(("axes", 1.2))
2828

2929
p1, = ax.plot([0, 1, 2], [0, 1, 2], "b-", label="Density")
3030
p2, = twin1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature")

examples/ticks_and_spines/spine_placement_demo.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,40 @@
2121
ax = fig.add_subplot(2, 2, 1)
2222
ax.set_title('centered spines')
2323
ax.plot(x, y)
24-
ax.spines['left'].set_position('center')
25-
ax.spines['right'].set_color('none')
26-
ax.spines['bottom'].set_position('center')
27-
ax.spines['top'].set_color('none')
24+
ax.spines.left.set_position('center')
25+
ax.spines.right.set_color('none')
26+
ax.spines.bottom.set_position('center')
27+
ax.spines.top.set_color('none')
2828
ax.xaxis.set_ticks_position('bottom')
2929
ax.yaxis.set_ticks_position('left')
3030

3131
ax = fig.add_subplot(2, 2, 2)
3232
ax.set_title('zeroed spines')
3333
ax.plot(x, y)
34-
ax.spines['left'].set_position('zero')
35-
ax.spines['right'].set_color('none')
36-
ax.spines['bottom'].set_position('zero')
37-
ax.spines['top'].set_color('none')
34+
ax.spines.left.set_position('zero')
35+
ax.spines.right.set_color('none')
36+
ax.spines.bottom.set_position('zero')
37+
ax.spines.top.set_color('none')
3838
ax.xaxis.set_ticks_position('bottom')
3939
ax.yaxis.set_ticks_position('left')
4040

4141
ax = fig.add_subplot(2, 2, 3)
4242
ax.set_title('spines at axes (0.6, 0.1)')
4343
ax.plot(x, y)
44-
ax.spines['left'].set_position(('axes', 0.6))
45-
ax.spines['right'].set_color('none')
46-
ax.spines['bottom'].set_position(('axes', 0.1))
47-
ax.spines['top'].set_color('none')
44+
ax.spines.left.set_position(('axes', 0.6))
45+
ax.spines.right.set_color('none')
46+
ax.spines.bottom.set_position(('axes', 0.1))
47+
ax.spines.top.set_color('none')
4848
ax.xaxis.set_ticks_position('bottom')
4949
ax.yaxis.set_ticks_position('left')
5050

5151
ax = fig.add_subplot(2, 2, 4)
5252
ax.set_title('spines at data (1, 2)')
5353
ax.plot(x, y)
54-
ax.spines['left'].set_position(('data', 1))
55-
ax.spines['right'].set_color('none')
56-
ax.spines['bottom'].set_position(('data', 2))
57-
ax.spines['top'].set_color('none')
54+
ax.spines.left.set_position(('data', 1))
55+
ax.spines.right.set_color('none')
56+
ax.spines.bottom.set_position(('data', 2))
57+
ax.spines.top.set_color('none')
5858
ax.xaxis.set_ticks_position('bottom')
5959
ax.yaxis.set_ticks_position('left')
6060

examples/ticks_and_spines/spines.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@
2626
ax1.set_title('bottom-left spines')
2727

2828
# Hide the right and top spines
29-
ax1.spines['right'].set_visible(False)
30-
ax1.spines['top'].set_visible(False)
29+
ax1.spines.right.set_visible(False)
30+
ax1.spines.top.set_visible(False)
3131
# Only show ticks on the left and bottom spines
3232
ax1.yaxis.set_ticks_position('left')
3333
ax1.xaxis.set_ticks_position('bottom')
3434

3535
ax2.plot(x, y)
3636

3737
# Only draw spine between the y-ticks
38-
ax2.spines['left'].set_bounds(-1, 1)
38+
ax2.spines.left.set_bounds(-1, 1)
3939
# Hide the right and top spines
40-
ax2.spines['right'].set_visible(False)
41-
ax2.spines['top'].set_visible(False)
40+
ax2.spines.right.set_visible(False)
41+
ax2.spines.top.set_visible(False)
4242
# Only show ticks on the left and bottom spines
4343
ax2.yaxis.set_ticks_position('left')
4444
ax2.xaxis.set_ticks_position('bottom')

examples/ticks_and_spines/spines_bounds.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
ax.set_yticks([-1, 0, 1])
2828

2929
# Only draw spine between the y-ticks
30-
ax.spines['left'].set_bounds((-1, 1))
30+
ax.spines.left.set_bounds((-1, 1))
3131
# Hide the right and top spines
32-
ax.spines['right'].set_visible(False)
33-
ax.spines['top'].set_visible(False)
32+
ax.spines.right.set_visible(False)
33+
ax.spines.top.set_visible(False)
3434
# Only show ticks on the left and bottom spines
3535
ax.yaxis.set_ticks_position('left')
3636
ax.xaxis.set_ticks_position('bottom')

examples/ticks_and_spines/spines_dropped.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
ax.set_title('dropped spines')
1919

2020
# Move left and bottom spines outward by 10 points
21-
ax.spines['left'].set_position(('outward', 10))
22-
ax.spines['bottom'].set_position(('outward', 10))
21+
ax.spines.left.set_position(('outward', 10))
22+
ax.spines.bottom.set_position(('outward', 10))
2323
# Hide the right and top spines
24-
ax.spines['right'].set_visible(False)
25-
ax.spines['top'].set_visible(False)
24+
ax.spines.right.set_visible(False)
25+
ax.spines.top.set_visible(False)
2626
# Only show ticks on the left and bottom spines
2727
ax.yaxis.set_ticks_position('left')
2828
ax.xaxis.set_ticks_position('bottom')

examples/ticks_and_spines/tick-formatters.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def setup(ax, title):
1717
"""Set up common parameters for the Axes in the example."""
1818
# only show the bottom spine
1919
ax.yaxis.set_major_locator(ticker.NullLocator())
20-
ax.spines['right'].set_color('none')
21-
ax.spines['left'].set_color('none')
22-
ax.spines['top'].set_color('none')
20+
ax.spines.right.set_color('none')
21+
ax.spines.left.set_color('none')
22+
ax.spines.top.set_color('none')
2323

2424
# define tick positions
2525
ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00))

examples/ticks_and_spines/tick-locators.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def setup(ax, title):
1717
"""Set up common parameters for the Axes in the example."""
1818
# only show the bottom spine
1919
ax.yaxis.set_major_locator(ticker.NullLocator())
20-
ax.spines['right'].set_color('none')
21-
ax.spines['left'].set_color('none')
22-
ax.spines['top'].set_color('none')
20+
ax.spines.right.set_color('none')
21+
ax.spines.left.set_color('none')
22+
ax.spines.top.set_color('none')
2323

2424
ax.xaxis.set_ticks_position('bottom')
2525
ax.tick_params(which='major', width=1.00, length=5)

examples/userdemo/demo_gridspec06.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ def squiggle_xy(a, b, c, d):
3030
# show only the outside spines
3131
for ax in fig.get_axes():
3232
ss = ax.get_subplotspec()
33-
ax.spines['top'].set_visible(ss.is_first_row())
34-
ax.spines['bottom'].set_visible(ss.is_last_row())
35-
ax.spines['left'].set_visible(ss.is_first_col())
36-
ax.spines['right'].set_visible(ss.is_last_col())
33+
ax.spines.top.set_visible(ss.is_first_row())
34+
ax.spines.bottom.set_visible(ss.is_last_row())
35+
ax.spines.left.set_visible(ss.is_first_col())
36+
ax.spines.right.set_visible(ss.is_last_col())
3737

3838
plt.show()

lib/matplotlib/axes/_base.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ def __init__(self, fig, rect,
556556
# placeholder for any colorbars added that use this axes.
557557
# (see colorbar.py):
558558
self._colorbars = []
559-
self.spines = self._gen_axes_spines()
559+
self.spines = mspines.Spines.from_dict(self._gen_axes_spines())
560560

561561
# this call may differ for non-sep axes, e.g., polar
562562
self._init_axis()
@@ -678,11 +678,11 @@ def get_window_extent(self, *args, **kwargs):
678678
def _init_axis(self):
679679
# This is moved out of __init__ because non-separable axes don't use it
680680
self.xaxis = maxis.XAxis(self)
681-
self.spines['bottom'].register_axis(self.xaxis)
682-
self.spines['top'].register_axis(self.xaxis)
681+
self.spines.bottom.register_axis(self.xaxis)
682+
self.spines.top.register_axis(self.xaxis)
683683
self.yaxis = maxis.YAxis(self)
684-
self.spines['left'].register_axis(self.yaxis)
685-
self.spines['right'].register_axis(self.yaxis)
684+
self.spines.left.register_axis(self.yaxis)
685+
self.spines.right.register_axis(self.yaxis)
686686
self._update_transScale()
687687

688688
def set_figure(self, fig):
@@ -781,10 +781,10 @@ def get_xaxis_transform(self, which='grid'):
781781
return self._xaxis_transform
782782
elif which == 'tick1':
783783
# for cartesian projection, this is bottom spine
784-
return self.spines['bottom'].get_spine_transform()
784+
return self.spines.bottom.get_spine_transform()
785785
elif which == 'tick2':
786786
# for cartesian projection, this is top spine
787-
return self.spines['top'].get_spine_transform()
787+
return self.spines.top.get_spine_transform()
788788
else:
789789
raise ValueError('unknown value for which')
790790

@@ -857,10 +857,10 @@ def get_yaxis_transform(self, which='grid'):
857857
return self._yaxis_transform
858858
elif which == 'tick1':
859859
# for cartesian projection, this is bottom spine
860-
return self.spines['left'].get_spine_transform()
860+
return self.spines.left.get_spine_transform()
861861
elif which == 'tick2':
862862
# for cartesian projection, this is top spine
863-
return self.spines['right'].get_spine_transform()
863+
return self.spines.right.get_spine_transform()
864864
else:
865865
raise ValueError('unknown value for which')
866866

lib/matplotlib/axes/_secondary_axes.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ def __init__(self, parent, orientation, location, functions, **kwargs):
4848
otheraxis.set_major_locator(mticker.NullLocator())
4949
otheraxis.set_ticks_position('none')
5050

51-
for st in self._otherstrings:
52-
self.spines[st].set_visible(False)
53-
for st in self._locstrings:
54-
self.spines[st].set_visible(True)
51+
self.spines[self._otherstrings].set_visible(False)
52+
self.spines[self._locstrings].set_visible(True)
5553

5654
if self._pos < 0.5:
5755
# flip the location strings...
@@ -257,13 +255,13 @@ def set_color(self, color):
257255
"""
258256
if self._orientation == 'x':
259257
self.tick_params(axis='x', colors=color)
260-
self.spines['bottom'].set_color(color)
261-
self.spines['top'].set_color(color)
258+
self.spines.bottom.set_color(color)
259+
self.spines.top.set_color(color)
262260
self.xaxis.label.set_color(color)
263261
else:
264262
self.tick_params(axis='y', colors=color)
265-
self.spines['left'].set_color(color)
266-
self.spines['right'].set_color(color)
263+
self.spines.left.set_color(color)
264+
self.spines.right.set_color(color)
267265
self.yaxis.label.set_color(color)
268266

269267

0 commit comments

Comments
 (0)