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

Skip to content

Commit 29d05ac

Browse files
committed
POC
1 parent d8833f4 commit 29d05ac

File tree

2 files changed

+248
-9
lines changed

2 files changed

+248
-9
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
safe_first_element)
3939
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer
4040
from matplotlib.axes._base import _AxesBase, _process_plot_format
41-
from matplotlib.axes._secondary_axes import Secondary_Xaxis
41+
from matplotlib.axes._secondary_axes import Secondary_Xaxis, Secondary_Yaxis
4242

4343
_log = logging.getLogger(__name__)
4444

@@ -678,6 +678,46 @@ def secondary_xaxis(self, loc, conversion, **kwargs):
678678
self.add_child_axes(secondary_ax)
679679
return secondary_ax
680680

681+
682+
def secondary_yaxis(self, loc, conversion, **kwargs):
683+
"""
684+
Add a second y-axis to this axes.
685+
686+
For example if we want to have a second scale for the data plotted on
687+
the xaxis.
688+
689+
Parameters
690+
----------
691+
loc : string or scalar
692+
FIX
693+
The position to put the secondary axis. Strings can be 'top' or
694+
'bottom', scalar can be a float indicating the relative position
695+
on the axes to put the new axes (0 being the bottom, and 1.0 being
696+
the top.)
697+
698+
conversion : tuple of floats or function
699+
conversion between the parent xaxis values and the secondary xaxis
700+
values. If a tuple of floats, the floats are polynomial
701+
co-efficients, with the first entry the highest exponent's
702+
co-efficient (i.e. [2, 3, 1] is the same as
703+
``xnew = 2 x**2 + 3 * x + 1``). If a function is specified it
704+
should accept a float as input and return a float as the
705+
result.
706+
707+
Other Parameters
708+
----------------
709+
**kwargs : `~matplotlib.axes.Axes` properties.
710+
Other miscellaneous axes parameters.
711+
712+
Returns
713+
-------
714+
ax : `~matplotlib.axes.Axes` (??)
715+
716+
"""
717+
secondary_ax = Secondary_Yaxis(self, loc, conversion, **kwargs)
718+
self.add_child_axes(secondary_ax)
719+
return secondary_ax
720+
681721
def text(self, x, y, s, fontdict=None, withdash=False, **kwargs):
682722
"""
683723
Add text to the axes.

lib/matplotlib/axes/_secondary_axes.py

Lines changed: 207 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
import matplotlib.ticker as mticker
66
import matplotlib.transforms as mtransforms
77

8-
from matplotlib.axes._base import _AxesBase, _process_plot_format
9-
10-
# DUPE FROM AXES. FIXME...
8+
from matplotlib.axes._base import _AxesBase
119

1210

1311
def _make_inset_locator(rect, trans, parent):
@@ -39,16 +37,13 @@ def inset_locator(ax, renderer):
3937

4038
class Secondary_Xaxis(_AxesBase):
4139
"""
42-
A class to hold a Secondary_Xaxis.
43-
44-
See `~.axes._axes.Axes.secondary_xaxis` for usage.
45-
40+
General class to hold a Secondary_X/Yaxis.
4641
"""
42+
4743
def __init__(self, parent, location, conversion, **kwargs):
4844
self._conversion = conversion
4945
self._parent = parent
5046

51-
5247
super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs)
5348

5449
self.set_location(location)
@@ -98,13 +93,18 @@ def set_location(self, location):
9893
on the parent axes to put the new axes, 0 being the bottom, and
9994
1.0 being the top.
10095
"""
96+
10197
self._loc = location
10298
# This puts the rectangle into figure-relative coordinates.
10399
if isinstance(self._loc, str):
104100
if self._loc == 'top':
105101
y = 1.
106102
elif self._loc == 'bottom':
107103
y = 0.
104+
else:
105+
raise ValueError("location must be 'bottom', 'top', or a "
106+
"float, not '{}'.".format(self._loc))
107+
108108
else:
109109
y = self._loc
110110
bounds = [0, y, 1., 1e-10]
@@ -232,3 +232,202 @@ def get_tightbbox(self, renderer, call_axes_locator=True):
232232
[b for b in bb if b.width != 0 or b.height != 0])
233233

234234
return _bbox
235+
236+
237+
class Secondary_Yaxis(_AxesBase):
238+
"""
239+
Class to hold a Secondary_Yaxis.
240+
"""
241+
242+
def __init__(self, parent, location, conversion, **kwargs):
243+
self._conversion = conversion
244+
self._parent = parent
245+
self._x = None # set in set_location
246+
247+
super().__init__(self._parent.figure, [1., 0., 0.00001, 1.], **kwargs)
248+
249+
self.set_location(location)
250+
251+
# styling:
252+
self.xaxis.set_major_locator(mticker.NullLocator())
253+
self.xaxis.set_ticks_position('none')
254+
self.spines['top'].set_visible(False)
255+
self.spines['bottom'].set_visible(False)
256+
if self._x > 0.5:
257+
self.set_axis_orientation('right')
258+
else:
259+
self.set_axis_orientation('left')
260+
self.set_conversion(conversion)
261+
262+
def set_axis_orientation(self, orient):
263+
"""
264+
Set if axes spine and labels are drawn at left or right of the
265+
axis.
266+
267+
Parameters
268+
----------
269+
orient :: string
270+
either 'left' or 'right'
271+
272+
"""
273+
274+
self.spines[orient].set_visible(True)
275+
if orient == 'left':
276+
self.spines['right'].set_visible(False)
277+
else:
278+
self.spines['left'].set_visible(False)
279+
280+
self.yaxis.set_ticks_position(orient)
281+
self.yaxis.set_label_position(orient)
282+
283+
def set_location(self, location):
284+
"""
285+
Set the horizontal location of the axes in parent-normalized
286+
co-ordinates.
287+
288+
Parameters
289+
----------
290+
location : string or scalar
291+
The position to put the secondary axis. Strings can be 'left' or
292+
'right', or scalar can be a float indicating the relative position
293+
on the parent axes to put the new axes, 0 being the left, and
294+
1.0 being the right.
295+
"""
296+
297+
self._loc = location
298+
# This puts the rectangle into figure-relative coordinates.
299+
if isinstance(self._loc, str):
300+
if self._loc == 'left':
301+
x = 0.
302+
elif self._loc == 'right':
303+
x = 1.
304+
else:
305+
raise ValueError("location must be 'left', 'right', or a "
306+
"float, not '{}'.".format(self._loc))
307+
else:
308+
x = self._loc
309+
bounds = [x, 0, 1e-10, 1.]
310+
transform = self._parent.transAxes
311+
secondary_locator = _make_inset_locator(bounds,
312+
transform, self._parent)
313+
bb = secondary_locator(None, None)
314+
315+
# this locator lets the axes move if in data coordinates.
316+
# it gets called in `ax.apply_aspect() (of all places)
317+
self.set_axes_locator(secondary_locator)
318+
self._x = x
319+
320+
def set_conversion(self, conversion):
321+
"""
322+
Set how the secondary axis converts limits from the parent axes.
323+
324+
Parameters
325+
----------
326+
conversion : tuple of floats or function
327+
conversion between the parent xaxis values and the secondary xaxis
328+
values. If a tuple of floats, the floats are polynomial
329+
co-efficients, with the first entry the highest exponent's
330+
co-efficient (i.e. [2, 3, 1] is the same as
331+
``xnew = 2 x**2 + 3 * x + 1``, passed to `numpy.polyval`).
332+
If a function is specified it should accept a float as input and
333+
return a float as the result.
334+
"""
335+
336+
# make the _convert function...
337+
if callable(conversion):
338+
self._convert = conversion
339+
else:
340+
if isinstance(conversion, numbers.Number):
341+
conversion = np.asanyarray([conversion])
342+
shp = len(conversion)
343+
if shp < 2:
344+
conversion = np.array([conversion, 0.])
345+
self._convert = lambda x: np.polyval(conversion, x)
346+
347+
def draw(self, renderer=None, inframe=False):
348+
"""
349+
Draw the secondary axes.
350+
351+
Consults the parent axes for its xlimits and converts them
352+
using the converter specified by
353+
`~.axes._secondary_axes.set_conversion` (or *conversion*
354+
parameter when axes initialized.)
355+
356+
"""
357+
lims = self._parent.get_xlim()
358+
self.set_ylim(self._convert(lims))
359+
super().draw(renderer=renderer, inframe=inframe)
360+
361+
def set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs):
362+
"""
363+
Set the label for the secondary y-axis.
364+
365+
Parameters
366+
----------
367+
ylabel : str
368+
The label text.
369+
370+
labelpad : scalar, optional, default: None
371+
Spacing in points between the label and the x-axis.
372+
373+
Other Parameters
374+
----------------
375+
**kwargs : `.Text` properties
376+
`.Text` properties control the appearance of the label.
377+
378+
See also
379+
--------
380+
text : for information on how override and the optional args work
381+
"""
382+
if labelpad is not None:
383+
self.yaxis.labelpad = labelpad
384+
return self.yaxis.set_label_text(ylabel, fontdict, **kwargs)
385+
386+
def set_color(self, color):
387+
"""
388+
Change the color of the secondary axes and all decorators
389+
390+
Parameters
391+
----------
392+
color : Matplotlib color
393+
"""
394+
395+
self.tick_params(axis='y', colors=color)
396+
self.spines['left'].set_color(color)
397+
self.spines['right'].set_color(color)
398+
self.yaxis.label.set_color(color)
399+
400+
def get_tightbbox(self, renderer, call_axes_locator=True):
401+
"""
402+
Return the tight bounding box of the axes.
403+
The dimension of the Bbox in canvas coordinate.
404+
405+
If *call_axes_locator* is *False*, it does not call the
406+
_axes_locator attribute, which is necessary to get the correct
407+
bounding box. ``call_axes_locator==False`` can be used if the
408+
caller is only intereted in the relative size of the tightbbox
409+
compared to the axes bbox.
410+
"""
411+
412+
bb = []
413+
414+
if not self.get_visible():
415+
return None
416+
417+
locator = self.get_axes_locator()
418+
if locator and call_axes_locator:
419+
pos = locator(self, renderer)
420+
self.apply_aspect(pos)
421+
else:
422+
self.apply_aspect()
423+
424+
bb_yaxis = self.yaxis.get_tightbbox(renderer)
425+
if bb_yaxis:
426+
bb.append(bb_yaxis)
427+
428+
bb.append(self.get_window_extent(renderer))
429+
430+
_bbox = mtransforms.Bbox.union(
431+
[b for b in bb if b.width != 0 or b.height != 0])
432+
433+
return _bbox

0 commit comments

Comments
 (0)