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

File tree

12 files changed

+257
-86
lines changed

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy