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

Skip to content

Commit c9cbc00

Browse files
committed
added twin axes patch
svn path=/trunk/matplotlib/; revision=886
1 parent 0cac5dc commit c9cbc00

4 files changed

Lines changed: 159 additions & 32 deletions

File tree

examples/two_scales.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@
1919
from pylab import *
2020

2121
ax1 = subplot(111)
22-
t = arange(0.0, 10.0, 0.01)
22+
t = arange(0.01, 10.0, 0.01)
2323
s1 = exp(t)
2424
plot(t, s1, 'b-')
25-
ax1.yaxis.tick_left()
25+
xlabel('time (s)')
26+
ylabel('exp')
2627

2728

2829
# turn off the 2nd axes rectangle with frameon kwarg
29-
ax2 = subplot(111, frameon=False)
30+
ax2 = twin()
3031
s2 = sin(2*pi*t)
3132
plot(t, s2, 'r.')
32-
ax2.yaxis.tick_right()
33-
34-
35-
xlabel('time (s)')
33+
ylabel('sin')
34+
xlim(0.01, 10)
35+
set(gca(), xscale='log')
3636

3737
show()

lib/matplotlib/axes.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,14 @@ def _grab_next_args(self, *args, **kwargs):
284284
remaining=remaining[2:]
285285
#yield self._plot_2_args(remaining[:2])
286286
#remaining=args[2:]
287-
287+
288+
BinOpType=type(zero())
289+
def makeValue(v):
290+
if type(v) == BinOpType:
291+
return v
292+
else:
293+
return Value(v)
294+
288295

289296

290297
class Axes(Artist):
@@ -304,7 +311,7 @@ def __init__(self, fig, rect,
304311
axisbg = None, # defaults to rc axes.facecolor
305312
frameon = True):
306313
Artist.__init__(self)
307-
self._position = [Value(val) for val in rect]
314+
self._position = map(makeValue, rect)
308315
self.set_figure(fig)
309316

310317
if axisbg is None: axisbg = rcParams['axes.facecolor']
@@ -3639,3 +3646,45 @@ def __init__(self, fig, *args, **kwargs):
36393646
SubplotBase.__init__(self, *args)
36403647
PolarAxes.__init__(self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs)
36413648

3649+
class TwinAxes(Axes):
3650+
"""
3651+
Create an Axes instance that shares the X axis with another:
3652+
3653+
TwinAxes(existing_axes)
3654+
"""
3655+
def __init__(self, axes):
3656+
Axes.__init__(self, axes.figure, axes._position, frameon=False)
3657+
3658+
self._xscale=axes._xscale
3659+
self.fmt_xdata=axes.fmt_xdata
3660+
3661+
vl_left=axes.viewLim.ll().x()
3662+
vl_right=axes.viewLim.ur().x()
3663+
vl_bottom=self.viewLim.ll().y()
3664+
vl_top=self.viewLim.ur().y()
3665+
self.viewLim=Bbox(Point(vl_left,vl_bottom),Point(vl_right, vl_top))
3666+
3667+
dl_left=axes.dataLim.ll().x()
3668+
dl_right=axes.dataLim.ur().x()
3669+
dl_bottom=self.dataLim.ll().y()
3670+
dl_top=self.dataLim.ur().y()
3671+
self.dataLim=Bbox(Point(dl_left,dl_bottom),Point(dl_right, dl_top))
3672+
3673+
try:
3674+
self.transData = blend_xy_sep_transform(axes.transData, self.transData)
3675+
except RuntimeError:
3676+
raise RuntimeError, "TwinAxes only works with cartesian axes"
3677+
3678+
self.xaxis=axes.xaxis
3679+
3680+
self.yaxis.tick_right()
3681+
self.yaxis.set_label_position('right')
3682+
axes.yaxis.tick_left()
3683+
3684+
def set_figure(self, fig):
3685+
if self.figure is None:
3686+
Axes.set_figure(self, fig)
3687+
return None
3688+
if fig is not self.figure:
3689+
raise RuntimeError, "TwinAxes can only be added to the same figure"
3690+

lib/matplotlib/axis.py

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ def draw(self, renderer, *args, **kwargs):
473473
if not self.get_visible(): return
474474
renderer.open_group(__name__)
475475
ticklabelBoxes = []
476+
ticklabelBoxes2 = []
476477

477478
majorTicks = self.get_major_ticks()
478479
majorLocs = self._majorLocator()
@@ -491,8 +492,12 @@ def draw(self, renderer, *args, **kwargs):
491492
tick.set_label1(label)
492493
tick.set_label2(label)
493494
tick.draw(renderer)
494-
extent = tick.label1.get_window_extent(renderer)
495-
ticklabelBoxes.append(extent)
495+
if tick.label1On:
496+
extent = tick.label1.get_window_extent(renderer)
497+
ticklabelBoxes.append(extent)
498+
if tick.label2On:
499+
extent = tick.label2.get_window_extent(renderer)
500+
ticklabelBoxes2.append(extent)
496501

497502
minorTicks = self.get_minor_ticks()
498503
minorLocs = self._minorLocator()
@@ -508,8 +513,12 @@ def draw(self, renderer, *args, **kwargs):
508513
tick.set_label(label)
509514

510515
tick.draw(renderer)
511-
extent = tick.label1.get_window_extent(renderer)
512-
ticklabelBoxes.append(extent)
516+
if tick.label1On:
517+
extent = tick.label1.get_window_extent(renderer)
518+
ticklabelBoxes.append(extent)
519+
if tick.label2On:
520+
extent = tick.label2.get_window_extent(renderer)
521+
ticklabelBoxes2.append(extent)
513522

514523

515524

@@ -519,7 +528,7 @@ def draw(self, renderer, *args, **kwargs):
519528
# the actual bbox
520529

521530

522-
self._update_label_postion(ticklabelBoxes)
531+
self._update_label_postion(ticklabelBoxes, ticklabelBoxes2)
523532

524533
self.label.draw(renderer) # memory leak here, vertical text
525534

@@ -750,25 +759,54 @@ def _get_label(self):
750759
identity_transform() ))
751760

752761
self._set_artist_props(label)
762+
self.label_position='bottom'
753763
return label
754764

765+
def get_label_position(self):
766+
"""
767+
Return the label position (top or bottom)
768+
"""
769+
return self.label_position
755770

756-
def _update_label_postion(self, bboxes):
771+
def set_label_position(self, position):
772+
"""
773+
Set the label position (top or bottom)
774+
775+
ACCEPTS: [ 'top' | 'bottom' ]
776+
"""
777+
assert position == 'top' or position == 'bottom'
778+
if position == 'top':
779+
self.label.set_verticalalignment('bottom')
780+
else:
781+
self.label.set_verticalalignment('top')
782+
self.label_position=position
783+
784+
def _update_label_postion(self, bboxes, bboxes2):
757785
"""
758786
Update the label position based on the sequence of bounding
759787
boxes of all the ticklabels
760788
"""
761789

762790
x,y = self.label.get_position()
763-
if not len(bboxes):
764-
bottom = self.axes.bbox.ymin()
791+
if self.label_position == 'bottom':
792+
if not len(bboxes):
793+
bottom = self.axes.bbox.ymin()
794+
else:
795+
796+
bbox = bbox_all(bboxes)
797+
bottom = bbox.ymin()
798+
799+
self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0))
800+
765801
else:
802+
if not len(bboxes2):
803+
top = self.axes.bbox.ymax()
804+
else:
766805

767-
bbox = bbox_all(bboxes)
768-
bottom = bbox.ymin()
806+
bbox = bbox_all(bboxes2)
807+
top = bbox.ymax()
769808

770-
self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0))
771-
809+
self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi.get()/72.0))
772810

773811
def tick_top(self):
774812
'use ticks only on top'
@@ -822,24 +860,54 @@ def _get_label(self):
822860
self.axes.transAxes) )
823861

824862
self._set_artist_props(label)
863+
self.label_position='left'
825864
return label
826865

827-
def _update_label_postion(self, bboxes):
866+
def get_label_position(self):
867+
"""
868+
Return the label position (left or right)
869+
"""
870+
return self.label_position
871+
872+
def set_label_position(self, position):
873+
"""
874+
Set the label position (left or right)
875+
876+
ACCEPTS: [ 'left' | 'right' ]
877+
"""
878+
assert position == 'left' or position == 'right'
879+
if position == 'right':
880+
self.label.set_horizontalalignment('left')
881+
else:
882+
self.label.set_horizontalalignment('right')
883+
self.label_position=position
884+
885+
def _update_label_postion(self, bboxes, bboxes2):
828886
"""
829887
Update the label position based on the sequence of bounding
830-
boxes overlaps of all the ticklabels that overlap the current
831-
ticklabel. overlaps are the bounding boxes of the ticklabels
888+
boxes of all the ticklabels
832889
"""
833890

834891
x,y = self.label.get_position()
835-
if not len(bboxes):
836-
left = self.axes.bbox.xmin()
837-
else:
838-
bbox = bbox_all(bboxes)
839-
left = bbox.xmin()
840-
self.label.set_position((left - self.figure.dpi.get()*self.LABELPAD/72.0,y))
892+
if self.label_position == 'left':
893+
if not len(bboxes):
894+
left = self.axes.bbox.xmin()
895+
else:
841896

897+
bbox = bbox_all(bboxes)
898+
left = bbox.xmin()
899+
900+
self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y))
842901

902+
else:
903+
if not len(bboxes2):
904+
right = self.axes.bbox.xmax()
905+
else:
906+
907+
bbox = bbox_all(bboxes2)
908+
right = bbox.xmax()
909+
910+
self.label.set_position( (right+self.LABELPAD*self.figure.dpi.get()/72.0, y))
843911

844912
def tick_right(self):
845913
'use ticks only on right'

lib/matplotlib/pylab.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
import _pylab_helpers
188188
import mlab #so I can override hist, psd, etc...
189189

190-
from axes import Axes, PolarAxes
190+
from axes import Axes, PolarAxes, TwinAxes
191191
from backends import new_figure_manager, error_msg, \
192192
draw_if_interactive, show
193193

@@ -1236,6 +1236,16 @@ def subplot(*args, **kwargs):
12361236
return a
12371237

12381238

1239+
def twin(axes=None):
1240+
if axes is None:
1241+
axes=gca()
1242+
1243+
tw=TwinAxes(axes)
1244+
gcf().add_axes(tw)
1245+
draw_if_interactive()
1246+
return tw
1247+
1248+
12391249
def title(s, *args, **kwargs):
12401250
"""
12411251
Set the title of the current axis to s
@@ -2466,7 +2476,7 @@ def winter():
24662476
'axes', 'delaxes', 'clim', 'close', 'clf', 'colorbar', 'draw',
24672477
'figtext', 'figimage', 'figlegend', 'figure', 'gca', 'gcf', 'gci',
24682478
'get', 'hold', 'ishold', 'isinteractive', 'imread', 'load', 'rc',
2469-
'rcdefaults', 'save', 'savefig', 'set', 'subplot', 'title',
2479+
'rcdefaults', 'save', 'savefig', 'set', 'subplot', 'twin', 'title',
24702480
'xlabel', 'ylabel', 'xlim', 'ylim', 'xticks', 'rgrids',
24712481
'thetagrids', 'yticks', 'polar', 'over', 'ioff', 'ion', 'axhline',
24722482
'axhspan', 'axvline', 'axvspan', 'bar', 'barh', 'cohere',

0 commit comments

Comments
 (0)