Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66290aa

Browse files
committed
Add experimental support for auto-layout of axes on the figure, to
prevent ticks and labels from overlapping things in other axes. svn path=/branches/transforms/; revision=4603
1 parent a2576a7 commit 66290aa

12 files changed

+257
-86
lines changed

examples/auto_layout.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example: simple line plot.
4+
Show how to make and save a simple line plot with labels, title and grid
5+
"""
6+
from pylab import *
7+
8+
t = arange(0.0, 1.0+0.01, 0.01)
9+
s = cos(2*2*pi*t)
10+
ax1 = subplot(211)
11+
plot(t, s, '-', lw=2)
12+
13+
xlabel('xlabel for bottom axes')
14+
ylabel('ylabel on the right')
15+
title('About as simple as it gets, folks')
16+
grid(True)
17+
ax1.yaxis.set_label_position('right')
18+
ax1.xaxis.set_ticklabels(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'])
19+
for label in ax1.get_xticklabels():
20+
label.set_rotation(45)
21+
22+
ax2 = subplot(212)
23+
plot(t, s, '-', lw=2)
24+
grid(True)
25+
xlabel('xlabel for bottom axes (the ticks are on the top for no good reason)')
26+
ylabel('I\'m a lefty')
27+
ax2.xaxis.set_label_position('bottom')
28+
ax2.xaxis.set_ticks_position('top')
29+
30+
31+
#savefig('simple_plot.png')
32+
savefig('simple_plot')
33+
34+
show()

examples/backend_driver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'alignment_test.py',
2424
'arctest.py',
2525
'arrow_demo.py',
26+
'auto_layout.py',
2627
'axes_demo.py',
2728
'axhspan_demo.py',
2829
'bar_stacked.py',
@@ -35,7 +36,7 @@
3536
'cohere_demo.py',
3637
'contour_demo.py',
3738
'contourf_demo.py',
38-
'csd_demo.py',
39+
'csd_demo.py',
3940
'custom_ticker1.py',
4041
'customize_rc.py',
4142
'date_demo1.py',

examples/colorbar_only.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
# Make a figure and axes with dimensions as desired.
99
fig = pylab.figure(figsize=(8,1.5))
10-
ax = fig.add_axes([0.05, 0.4, 0.9, 0.5])
10+
ax = fig.add_axes([0.05, 0.05, 0.9, 0.9])
1111

1212
# Set the colormap and norm to correspond to the data for which
1313
# the colorbar will be used.

examples/figlegend_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22

33
from pylab import *
4-
ax1 = axes([0.1, 0.1, 0.4, 0.7])
4+
ax1 = axes([0.05, 0.1, 0.4, 0.7])
55
ax2 = axes([0.55, 0.1, 0.4, 0.7])
66

77
x = arange(0.0, 2.0, 0.02)

examples/finance_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
raise SystemExit
2323

2424
fig = figure()
25-
fig.subplots_adjust(bottom=0.2)
25+
# fig.subplots_adjust(bottom=0.2)
2626
ax = fig.add_subplot(111)
2727
ax.xaxis.set_major_locator(mondays)
2828
ax.xaxis.set_minor_locator(alldays)

examples/mathtext_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from matplotlib.pyplot import figure, show
88

99
fig = figure()
10-
fig.subplots_adjust(bottom=0.2)
10+
# fig.subplots_adjust(bottom=0.2)
1111

1212
ax = fig.add_subplot(111, axisbg='y')
1313
ax.plot([1,2,3], 'r')

examples/simple_plot.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
ylabel('voltage (mV)')
1414
title('About as simple as it gets, folks')
1515
grid(True)
16+
axes().xaxis.set_label_position('top')
17+
axes().xaxis.set_ticks_position('top')
18+
axes().yaxis.set_label_position('right')
1619

1720
#savefig('simple_plot.png')
1821
savefig('simple_plot')

lib/matplotlib/axes.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -770,13 +770,14 @@ def cla(self):
770770

771771
self.grid(self._gridOn)
772772
props = font_manager.FontProperties(size=rcParams['axes.titlesize'])
773+
self.titleOffsetTrans = mtransforms.Affine2D().translate(0.0, 10.0)
773774
self.title = mtext.Text(
774-
x=0.5, y=1.02, text='',
775+
x=0.5, y=1.0, text='',
775776
fontproperties=props,
776777
verticalalignment='bottom',
777778
horizontalalignment='center',
778779
)
779-
self.title.set_transform(self.transAxes)
780+
self.title.set_transform(self.transAxes + self.titleOffsetTrans)
780781
self.title.set_clip_box(None)
781782

782783
self._set_artist_props(self.title)
@@ -800,6 +801,8 @@ def cla(self):
800801
self.xaxis.set_clip_path(self.axesPatch)
801802
self.yaxis.set_clip_path(self.axesPatch)
802803

804+
self.titleOffsetTrans.clear()
805+
803806
def clear(self):
804807
'clear the axes'
805808
self.cla()
@@ -905,14 +908,14 @@ def get_data_ratio(self):
905908
ysize = max(math.fabs(ymax-ymin), 1e-30)
906909
return ysize/xsize
907910

908-
def apply_aspect(self):
911+
def apply_aspect(self, position):
909912
'''
910913
Use self._aspect and self._adjustable to modify the
911914
axes box or the view limits.
912915
'''
913916
aspect = self.get_aspect()
914917
if aspect == 'auto':
915-
self.set_position( self._originalPosition , 'active')
918+
self.set_position( position , 'active')
916919
return
917920

918921
if aspect == 'equal':
@@ -929,7 +932,7 @@ def apply_aspect(self):
929932
fig_aspect = figH/figW
930933
if self._adjustable == 'box':
931934
box_aspect = A * self.get_data_ratio()
932-
pb = self._originalPosition.frozen()
935+
pb = position.frozen()
933936
pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
934937
self.set_position(pb1.anchored(self.get_anchor(), pb), 'active')
935938
return
@@ -939,7 +942,7 @@ def apply_aspect(self):
939942
ymin,ymax = self.get_ybound()
940943
ysize = max(math.fabs(ymax-ymin), 1e-30)
941944

942-
l,b,w,h = self.get_position(original=True).bounds
945+
l,b,w,h = position.bounds
943946
box_aspect = fig_aspect * (h/w)
944947
data_ratio = box_aspect / A
945948

@@ -1014,7 +1017,7 @@ def axis(self, *v, **kwargs):
10141017
self.set_autoscale_on(True)
10151018
self.set_aspect('auto')
10161019
self.autoscale_view()
1017-
self.apply_aspect()
1020+
# self.apply_aspect()
10181021
if s=='equal':
10191022
self.set_aspect('equal', adjustable='datalim')
10201023
elif s == 'scaled':
@@ -1289,6 +1292,32 @@ def autoscale_view(self, tight=False, scalex=True, scaley=True):
12891292
YL = ylocator.autoscale()
12901293
self.set_ybound(YL)
12911294

1295+
def update_layout(self, renderer):
1296+
pad_pixels = rcParams['xtick.major.pad'] * self.figure.dpi / 72.0
1297+
inverse_transFigure = self.figure.transFigure.inverted()
1298+
t_text, b_text = self.xaxis.get_text_heights(renderer)
1299+
l_text, r_text = self.yaxis.get_text_widths(renderer)
1300+
title_height = self.title.get_window_extent(renderer).height
1301+
title_height += pad_pixels * 2.0
1302+
original_t_text = t_text
1303+
1304+
((l_text, t_text),
1305+
(r_text, b_text),
1306+
(dummy, title_height)) = inverse_transFigure.transform(
1307+
((l_text, t_text),
1308+
(r_text, b_text),
1309+
(0.0, title_height)))
1310+
x0, y0, x1, y1 = self.get_position(True).extents
1311+
# Adjust the title
1312+
self.titleOffsetTrans.clear().translate(
1313+
0, original_t_text + pad_pixels * 2.0)
1314+
1315+
new_position = mtransforms.Bbox.from_extents(
1316+
x0 + l_text, y0 + b_text,
1317+
x1 - r_text, y1 - t_text - title_height)
1318+
1319+
self.set_position(new_position, 'active')
1320+
12921321
#### Drawing
12931322
def draw(self, renderer=None, inframe=False):
12941323
"Draw everything (plot lines, axes, labels)"
@@ -1299,7 +1328,8 @@ def draw(self, renderer=None, inframe=False):
12991328
raise RuntimeError('No renderer defined')
13001329
if not self.get_visible(): return
13011330
renderer.open_group('axes')
1302-
self.apply_aspect()
1331+
1332+
self.apply_aspect(self.get_position())
13031333

13041334
if self.axison and self._frameon:
13051335
self.axesPatch.draw(renderer)

lib/matplotlib/axis.py

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ def set_pad(self, val):
130130
131131
ACCEPTS: float
132132
"""
133-
self._pad.set(val)
133+
self._pad = val
134134

135135
def get_pad(self, val):
136136
'Get the value of the tick label pad in points'
137-
return self._pad.get()
137+
return self._pad
138138

139139
def _get_text1(self):
140140
'Get the default Text 1 instance'
@@ -578,50 +578,74 @@ def _set_artist_props(self, a):
578578
if a is None: return
579579
a.set_figure(self.figure)
580580

581-
def draw(self, renderer, *args, **kwargs):
582-
'Draw the axis lines, grid lines, tick lines and labels'
583-
if not self.get_visible(): return
584-
renderer.open_group(__name__)
585-
ticklabelBoxes = []
586-
ticklabelBoxes2 = []
587-
581+
def iter_ticks(self):
582+
"""
583+
Iterate through all of the major and minor ticks.
584+
"""
588585
majorLocs = self.major.locator()
589586
majorTicks = self.get_major_ticks(len(majorLocs))
590587
self.major.formatter.set_locs(majorLocs)
591588
majorLabels = [self.major.formatter(val, i) for i, val in enumerate(majorLocs)]
592589

590+
minorLocs = self.minor.locator()
591+
minorTicks = self.get_minor_ticks(len(minorLocs))
592+
self.minor.formatter.set_locs(minorLocs)
593+
minorLabels = [self.minor.formatter(val, i) for i, val in enumerate(minorLocs)]
594+
595+
major_minor = [
596+
(majorTicks, majorLocs, majorLabels),
597+
(minorTicks, minorLocs, minorLabels)]
593598

594-
seen = {}
599+
for group in major_minor:
600+
for tick in zip(*group):
601+
yield tick
602+
603+
def get_ticklabel_extents(self, renderer):
604+
"""
605+
Get the extents of the tick labels on either side
606+
of the axes.
607+
"""
608+
ticklabelBoxes = []
609+
ticklabelBoxes2 = []
595610

596611
interval = self.get_view_interval()
597-
for tick, loc, label in zip(majorTicks, majorLocs, majorLabels):
612+
for tick, loc, label in self.iter_ticks():
598613
if tick is None: continue
599614
if not interval_contains(interval, loc): continue
600-
seen[loc] = 1
601615
tick.update_position(loc)
602616
tick.set_label1(label)
603617
tick.set_label2(label)
604-
tick.draw(renderer)
605618
if tick.label1On and tick.label1.get_visible():
606619
extent = tick.label1.get_window_extent(renderer)
607620
ticklabelBoxes.append(extent)
608621
if tick.label2On and tick.label2.get_visible():
609622
extent = tick.label2.get_window_extent(renderer)
610623
ticklabelBoxes2.append(extent)
611624

612-
minorLocs = self.minor.locator()
613-
minorTicks = self.get_minor_ticks(len(minorLocs))
614-
self.minor.formatter.set_locs(minorLocs)
615-
minorLabels = [self.minor.formatter(val, i) for i, val in enumerate(minorLocs)]
625+
if len(ticklabelBoxes):
626+
bbox = Bbox.union(ticklabelBoxes)
627+
else:
628+
bbox = Bbox.from_extents(0, 0, 0, 0)
629+
if len(ticklabelBoxes2):
630+
bbox2 = Bbox.union(ticklabelBoxes2)
631+
else:
632+
bbox2 = Bbox.from_extents(0, 0, 0, 0)
633+
return bbox, bbox2
616634

617-
for tick, loc, label in zip(minorTicks, minorLocs, minorLabels):
635+
def draw(self, renderer, *args, **kwargs):
636+
'Draw the axis lines, grid lines, tick lines and labels'
637+
ticklabelBoxes = []
638+
ticklabelBoxes2 = []
639+
640+
if not self.get_visible(): return
641+
renderer.open_group(__name__)
642+
interval = self.get_view_interval()
643+
for tick, loc, label in self.iter_ticks():
618644
if tick is None: continue
619645
if not interval_contains(interval, loc): continue
620-
#if seen.has_key(loc): continue
621646
tick.update_position(loc)
622647
tick.set_label1(label)
623648
tick.set_label2(label)
624-
625649
tick.draw(renderer)
626650
if tick.label1On and tick.label1.get_visible():
627651
extent = tick.label1.get_window_extent(renderer)
@@ -1142,6 +1166,28 @@ def _update_offset_text_position(self, bboxes, bboxes2):
11421166
bottom = bbox.y0
11431167
self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0))
11441168

1169+
def get_text_heights(self, renderer):
1170+
"""
1171+
Returns the amount of space one should reserve for text
1172+
above and below the axes. Returns a tuple (above, below)
1173+
"""
1174+
bbox, bbox2 = self.get_ticklabel_extents(renderer)
1175+
# MGDTODO: Need a better way to get the pad
1176+
padPixels = self.majorTicks[0]._padPixels
1177+
1178+
above = 0.0
1179+
if bbox2.height:
1180+
above += bbox2.height + padPixels
1181+
below = 0.0
1182+
if bbox.height:
1183+
below += bbox.height + padPixels
1184+
1185+
if self.get_label_position() == 'top':
1186+
above += self.label.get_window_extent(renderer).height + padPixels
1187+
else:
1188+
below += self.label.get_window_extent(renderer).height + padPixels
1189+
return above, below
1190+
11451191
def set_ticks_position(self, position):
11461192
"""
11471193
Set the ticks position (top, bottom, both, default or none)
@@ -1360,6 +1406,24 @@ def set_offset_position(self, position):
13601406
self.offsetText.set_ha(position)
13611407
self.offsetText.set_position((x,y))
13621408

1409+
def get_text_widths(self, renderer):
1410+
bbox, bbox2 = self.get_ticklabel_extents(renderer)
1411+
# MGDTODO: Need a better way to get the pad
1412+
padPixels = self.majorTicks[0]._padPixels
1413+
1414+
left = 0.0
1415+
if bbox.width:
1416+
left += bbox.width + padPixels
1417+
right = 0.0
1418+
if bbox2.width:
1419+
right += bbox2.width + padPixels
1420+
1421+
if self.get_label_position() == 'left':
1422+
left += self.label.get_window_extent(renderer).width + padPixels
1423+
else:
1424+
right += self.label.get_window_extent(renderer).width + padPixels
1425+
return left, right
1426+
13631427
def set_ticks_position(self, position):
13641428
"""
13651429
Set the ticks position (left, right, both or default)

0 commit comments

Comments
 (0)