Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
|
@ -0,0 +1,5 @@
|
|||
from . import axes_size as Size
|
||||
from .axes_divider import Divider, SubplotDivider, make_axes_locatable
|
||||
from .axes_grid import Grid, ImageGrid, AxesGrid
|
||||
|
||||
from .parasite_axes import host_subplot, host_axes
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,564 @@
|
|||
from matplotlib import transforms
|
||||
from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox,
|
||||
DrawingArea, TextArea, VPacker)
|
||||
from matplotlib.patches import (Rectangle, Ellipse, ArrowStyle,
|
||||
FancyArrowPatch, PathPatch)
|
||||
from matplotlib.text import TextPath
|
||||
|
||||
__all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox',
|
||||
'AnchoredEllipse', 'AnchoredSizeBar', 'AnchoredDirectionArrows']
|
||||
|
||||
|
||||
class AnchoredDrawingArea(AnchoredOffsetbox):
|
||||
def __init__(self, width, height, xdescent, ydescent,
|
||||
loc, pad=0.4, borderpad=0.5, prop=None, frameon=True,
|
||||
**kwargs):
|
||||
"""
|
||||
An anchored container with a fixed size and fillable DrawingArea.
|
||||
|
||||
Artists added to the *drawing_area* will have their coordinates
|
||||
interpreted as pixels. Any transformations set on the artists will be
|
||||
overridden.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
width, height : float
|
||||
width and height of the container, in pixels.
|
||||
|
||||
xdescent, ydescent : float
|
||||
descent of the container in the x- and y- direction, in pixels.
|
||||
|
||||
loc : int
|
||||
Location of this artist. Valid location codes are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
pad : float, default: 0.4
|
||||
Padding around the child objects, in fraction of the font size.
|
||||
|
||||
borderpad : float, default: 0.5
|
||||
Border padding, in fraction of the font size.
|
||||
|
||||
prop : `matplotlib.font_manager.FontProperties`, optional
|
||||
Font property used as a reference for paddings.
|
||||
|
||||
frameon : bool, default: True
|
||||
If True, draw a box around this artists.
|
||||
|
||||
**kwargs
|
||||
Keyworded arguments to pass to
|
||||
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
drawing_area : `matplotlib.offsetbox.DrawingArea`
|
||||
A container for artists to display.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To display blue and red circles of different sizes in the upper right
|
||||
of an axes *ax*:
|
||||
|
||||
>>> ada = AnchoredDrawingArea(20, 20, 0, 0,
|
||||
... loc='upper right', frameon=False)
|
||||
>>> ada.drawing_area.add_artist(Circle((10, 10), 10, fc="b"))
|
||||
>>> ada.drawing_area.add_artist(Circle((30, 10), 5, fc="r"))
|
||||
>>> ax.add_artist(ada)
|
||||
"""
|
||||
self.da = DrawingArea(width, height, xdescent, ydescent)
|
||||
self.drawing_area = self.da
|
||||
|
||||
super().__init__(
|
||||
loc, pad=pad, borderpad=borderpad, child=self.da, prop=None,
|
||||
frameon=frameon, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class AnchoredAuxTransformBox(AnchoredOffsetbox):
|
||||
def __init__(self, transform, loc,
|
||||
pad=0.4, borderpad=0.5, prop=None, frameon=True, **kwargs):
|
||||
"""
|
||||
An anchored container with transformed coordinates.
|
||||
|
||||
Artists added to the *drawing_area* are scaled according to the
|
||||
coordinates of the transformation used. The dimensions of this artist
|
||||
will scale to contain the artists added.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
transform : `matplotlib.transforms.Transform`
|
||||
The transformation object for the coordinate system in use, i.e.,
|
||||
:attr:`matplotlib.axes.Axes.transData`.
|
||||
|
||||
loc : int
|
||||
Location of this artist. Valid location codes are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
pad : float, default: 0.4
|
||||
Padding around the child objects, in fraction of the font size.
|
||||
|
||||
borderpad : float, default: 0.5
|
||||
Border padding, in fraction of the font size.
|
||||
|
||||
prop : `matplotlib.font_manager.FontProperties`, optional
|
||||
Font property used as a reference for paddings.
|
||||
|
||||
frameon : bool, default: True
|
||||
If True, draw a box around this artists.
|
||||
|
||||
**kwargs
|
||||
Keyworded arguments to pass to
|
||||
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
drawing_area : `matplotlib.offsetbox.AuxTransformBox`
|
||||
A container for artists to display.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To display an ellipse in the upper left, with a width of 0.1 and
|
||||
height of 0.4 in data coordinates:
|
||||
|
||||
>>> box = AnchoredAuxTransformBox(ax.transData, loc='upper left')
|
||||
>>> el = Ellipse((0, 0), width=0.1, height=0.4, angle=30)
|
||||
>>> box.drawing_area.add_artist(el)
|
||||
>>> ax.add_artist(box)
|
||||
"""
|
||||
self.drawing_area = AuxTransformBox(transform)
|
||||
|
||||
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
|
||||
child=self.drawing_area,
|
||||
prop=prop,
|
||||
frameon=frameon,
|
||||
**kwargs)
|
||||
|
||||
|
||||
class AnchoredEllipse(AnchoredOffsetbox):
|
||||
def __init__(self, transform, width, height, angle, loc,
|
||||
pad=0.1, borderpad=0.1, prop=None, frameon=True, **kwargs):
|
||||
"""
|
||||
Draw an anchored ellipse of a given size.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
transform : `matplotlib.transforms.Transform`
|
||||
The transformation object for the coordinate system in use, i.e.,
|
||||
:attr:`matplotlib.axes.Axes.transData`.
|
||||
|
||||
width, height : float
|
||||
Width and height of the ellipse, given in coordinates of
|
||||
*transform*.
|
||||
|
||||
angle : float
|
||||
Rotation of the ellipse, in degrees, anti-clockwise.
|
||||
|
||||
loc : int
|
||||
Location of this size bar. Valid location codes are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
pad : float, optional
|
||||
Padding around the ellipse, in fraction of the font size. Defaults
|
||||
to 0.1.
|
||||
|
||||
borderpad : float, default: 0.1
|
||||
Border padding, in fraction of the font size.
|
||||
|
||||
frameon : bool, default: True
|
||||
If True, draw a box around the ellipse.
|
||||
|
||||
prop : `matplotlib.font_manager.FontProperties`, optional
|
||||
Font property used as a reference for paddings.
|
||||
|
||||
**kwargs
|
||||
Keyworded arguments to pass to
|
||||
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
ellipse : `matplotlib.patches.Ellipse`
|
||||
Ellipse patch drawn.
|
||||
"""
|
||||
self._box = AuxTransformBox(transform)
|
||||
self.ellipse = Ellipse((0, 0), width, height, angle)
|
||||
self._box.add_artist(self.ellipse)
|
||||
|
||||
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
|
||||
child=self._box,
|
||||
prop=prop,
|
||||
frameon=frameon, **kwargs)
|
||||
|
||||
|
||||
class AnchoredSizeBar(AnchoredOffsetbox):
|
||||
def __init__(self, transform, size, label, loc,
|
||||
pad=0.1, borderpad=0.1, sep=2,
|
||||
frameon=True, size_vertical=0, color='black',
|
||||
label_top=False, fontproperties=None, fill_bar=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Draw a horizontal scale bar with a center-aligned label underneath.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
transform : `matplotlib.transforms.Transform`
|
||||
The transformation object for the coordinate system in use, i.e.,
|
||||
:attr:`matplotlib.axes.Axes.transData`.
|
||||
|
||||
size : float
|
||||
Horizontal length of the size bar, given in coordinates of
|
||||
*transform*.
|
||||
|
||||
label : str
|
||||
Label to display.
|
||||
|
||||
loc : int
|
||||
Location of this size bar. Valid location codes are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
pad : float, default: 0.1
|
||||
Padding around the label and size bar, in fraction of the font
|
||||
size.
|
||||
|
||||
borderpad : float, default: 0.1
|
||||
Border padding, in fraction of the font size.
|
||||
|
||||
sep : float, default: 2
|
||||
Separation between the label and the size bar, in points.
|
||||
|
||||
frameon : bool, default: True
|
||||
If True, draw a box around the horizontal bar and label.
|
||||
|
||||
size_vertical : float, default: 0
|
||||
Vertical length of the size bar, given in coordinates of
|
||||
*transform*.
|
||||
|
||||
color : str, default: 'black'
|
||||
Color for the size bar and label.
|
||||
|
||||
label_top : bool, default: False
|
||||
If True, the label will be over the size bar.
|
||||
|
||||
fontproperties : `matplotlib.font_manager.FontProperties`, optional
|
||||
Font properties for the label text.
|
||||
|
||||
fill_bar : bool, optional
|
||||
If True and if size_vertical is nonzero, the size bar will
|
||||
be filled in with the color specified by the size bar.
|
||||
Defaults to True if `size_vertical` is greater than
|
||||
zero and False otherwise.
|
||||
|
||||
**kwargs
|
||||
Keyworded arguments to pass to
|
||||
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
size_bar : `matplotlib.offsetbox.AuxTransformBox`
|
||||
Container for the size bar.
|
||||
|
||||
txt_label : `matplotlib.offsetbox.TextArea`
|
||||
Container for the label of the size bar.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If *prop* is passed as a keyworded argument, but *fontproperties* is
|
||||
not, then *prop* is be assumed to be the intended *fontproperties*.
|
||||
Using both *prop* and *fontproperties* is not supported.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> import numpy as np
|
||||
>>> from mpl_toolkits.axes_grid1.anchored_artists import (
|
||||
... AnchoredSizeBar)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.imshow(np.random.random((10, 10)))
|
||||
>>> bar = AnchoredSizeBar(ax.transData, 3, '3 data units', 4)
|
||||
>>> ax.add_artist(bar)
|
||||
>>> fig.show()
|
||||
|
||||
Using all the optional parameters
|
||||
|
||||
>>> import matplotlib.font_manager as fm
|
||||
>>> fontprops = fm.FontProperties(size=14, family='monospace')
|
||||
>>> bar = AnchoredSizeBar(ax.transData, 3, '3 units', 4, pad=0.5,
|
||||
... sep=5, borderpad=0.5, frameon=False,
|
||||
... size_vertical=0.5, color='white',
|
||||
... fontproperties=fontprops)
|
||||
"""
|
||||
if fill_bar is None:
|
||||
fill_bar = size_vertical > 0
|
||||
|
||||
self.size_bar = AuxTransformBox(transform)
|
||||
self.size_bar.add_artist(Rectangle((0, 0), size, size_vertical,
|
||||
fill=fill_bar, facecolor=color,
|
||||
edgecolor=color))
|
||||
|
||||
if fontproperties is None and 'prop' in kwargs:
|
||||
fontproperties = kwargs.pop('prop')
|
||||
|
||||
if fontproperties is None:
|
||||
textprops = {'color': color}
|
||||
else:
|
||||
textprops = {'color': color, 'fontproperties': fontproperties}
|
||||
|
||||
self.txt_label = TextArea(
|
||||
label,
|
||||
minimumdescent=False,
|
||||
textprops=textprops)
|
||||
|
||||
if label_top:
|
||||
_box_children = [self.txt_label, self.size_bar]
|
||||
else:
|
||||
_box_children = [self.size_bar, self.txt_label]
|
||||
|
||||
self._box = VPacker(children=_box_children,
|
||||
align="center",
|
||||
pad=0, sep=sep)
|
||||
|
||||
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
|
||||
child=self._box,
|
||||
prop=fontproperties,
|
||||
frameon=frameon, **kwargs)
|
||||
|
||||
|
||||
class AnchoredDirectionArrows(AnchoredOffsetbox):
|
||||
def __init__(self, transform, label_x, label_y, length=0.15,
|
||||
fontsize=0.08, loc=2, angle=0, aspect_ratio=1, pad=0.4,
|
||||
borderpad=0.4, frameon=False, color='w', alpha=1,
|
||||
sep_x=0.01, sep_y=0, fontproperties=None, back_length=0.15,
|
||||
head_width=10, head_length=15, tail_width=2,
|
||||
text_props=None, arrow_props=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Draw two perpendicular arrows to indicate directions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
transform : `matplotlib.transforms.Transform`
|
||||
The transformation object for the coordinate system in use, i.e.,
|
||||
:attr:`matplotlib.axes.Axes.transAxes`.
|
||||
|
||||
label_x, label_y : str
|
||||
Label text for the x and y arrows
|
||||
|
||||
length : float, default: 0.15
|
||||
Length of the arrow, given in coordinates of *transform*.
|
||||
|
||||
fontsize : float, default: 0.08
|
||||
Size of label strings, given in coordinates of *transform*.
|
||||
|
||||
loc : int, default: 2
|
||||
Location of the direction arrows. Valid location codes are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
angle : float, default: 0
|
||||
The angle of the arrows in degrees.
|
||||
|
||||
aspect_ratio : float, default: 1
|
||||
The ratio of the length of arrow_x and arrow_y.
|
||||
Negative numbers can be used to change the direction.
|
||||
|
||||
pad : float, default: 0.4
|
||||
Padding around the labels and arrows, in fraction of the font size.
|
||||
|
||||
borderpad : float, default: 0.4
|
||||
Border padding, in fraction of the font size.
|
||||
|
||||
frameon : bool, default: False
|
||||
If True, draw a box around the arrows and labels.
|
||||
|
||||
color : str, default: 'white'
|
||||
Color for the arrows and labels.
|
||||
|
||||
alpha : float, default: 1
|
||||
Alpha values of the arrows and labels
|
||||
|
||||
sep_x, sep_y : float, default: 0.01 and 0 respectively
|
||||
Separation between the arrows and labels in coordinates of
|
||||
*transform*.
|
||||
|
||||
fontproperties : `matplotlib.font_manager.FontProperties`, optional
|
||||
Font properties for the label text.
|
||||
|
||||
back_length : float, default: 0.15
|
||||
Fraction of the arrow behind the arrow crossing.
|
||||
|
||||
head_width : float, default: 10
|
||||
Width of arrow head, sent to ArrowStyle.
|
||||
|
||||
head_length : float, default: 15
|
||||
Length of arrow head, sent to ArrowStyle.
|
||||
|
||||
tail_width : float, default: 2
|
||||
Width of arrow tail, sent to ArrowStyle.
|
||||
|
||||
text_props, arrow_props : dict
|
||||
Properties of the text and arrows, passed to
|
||||
`.textpath.TextPath` and `.patches.FancyArrowPatch`.
|
||||
|
||||
**kwargs
|
||||
Keyworded arguments to pass to
|
||||
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
arrow_x, arrow_y : `matplotlib.patches.FancyArrowPatch`
|
||||
Arrow x and y
|
||||
|
||||
text_path_x, text_path_y : `matplotlib.textpath.TextPath`
|
||||
Path for arrow labels
|
||||
|
||||
p_x, p_y : `matplotlib.patches.PathPatch`
|
||||
Patch for arrow labels
|
||||
|
||||
box : `matplotlib.offsetbox.AuxTransformBox`
|
||||
Container for the arrows and labels.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If *prop* is passed as a keyword argument, but *fontproperties* is
|
||||
not, then *prop* is be assumed to be the intended *fontproperties*.
|
||||
Using both *prop* and *fontproperties* is not supported.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> import numpy as np
|
||||
>>> from mpl_toolkits.axes_grid1.anchored_artists import (
|
||||
... AnchoredDirectionArrows)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.imshow(np.random.random((10, 10)))
|
||||
>>> arrows = AnchoredDirectionArrows(ax.transAxes, '111', '110')
|
||||
>>> ax.add_artist(arrows)
|
||||
>>> fig.show()
|
||||
|
||||
Using several of the optional parameters, creating downward pointing
|
||||
arrow and high contrast text labels.
|
||||
|
||||
>>> import matplotlib.font_manager as fm
|
||||
>>> fontprops = fm.FontProperties(family='monospace')
|
||||
>>> arrows = AnchoredDirectionArrows(ax.transAxes, 'East', 'South',
|
||||
... loc='lower left', color='k',
|
||||
... aspect_ratio=-1, sep_x=0.02,
|
||||
... sep_y=-0.01,
|
||||
... text_props={'ec':'w', 'fc':'k'},
|
||||
... fontproperties=fontprops)
|
||||
"""
|
||||
if arrow_props is None:
|
||||
arrow_props = {}
|
||||
|
||||
if text_props is None:
|
||||
text_props = {}
|
||||
|
||||
arrowstyle = ArrowStyle("Simple",
|
||||
head_width=head_width,
|
||||
head_length=head_length,
|
||||
tail_width=tail_width)
|
||||
|
||||
if fontproperties is None and 'prop' in kwargs:
|
||||
fontproperties = kwargs.pop('prop')
|
||||
|
||||
if 'color' not in arrow_props:
|
||||
arrow_props['color'] = color
|
||||
|
||||
if 'alpha' not in arrow_props:
|
||||
arrow_props['alpha'] = alpha
|
||||
|
||||
if 'color' not in text_props:
|
||||
text_props['color'] = color
|
||||
|
||||
if 'alpha' not in text_props:
|
||||
text_props['alpha'] = alpha
|
||||
|
||||
t_start = transform
|
||||
t_end = t_start + transforms.Affine2D().rotate_deg(angle)
|
||||
|
||||
self.box = AuxTransformBox(t_end)
|
||||
|
||||
length_x = length
|
||||
length_y = length*aspect_ratio
|
||||
|
||||
self.arrow_x = FancyArrowPatch(
|
||||
(0, back_length*length_y),
|
||||
(length_x, back_length*length_y),
|
||||
arrowstyle=arrowstyle,
|
||||
shrinkA=0.0,
|
||||
shrinkB=0.0,
|
||||
**arrow_props)
|
||||
|
||||
self.arrow_y = FancyArrowPatch(
|
||||
(back_length*length_x, 0),
|
||||
(back_length*length_x, length_y),
|
||||
arrowstyle=arrowstyle,
|
||||
shrinkA=0.0,
|
||||
shrinkB=0.0,
|
||||
**arrow_props)
|
||||
|
||||
self.box.add_artist(self.arrow_x)
|
||||
self.box.add_artist(self.arrow_y)
|
||||
|
||||
text_path_x = TextPath((
|
||||
length_x+sep_x, back_length*length_y+sep_y), label_x,
|
||||
size=fontsize, prop=fontproperties)
|
||||
self.p_x = PathPatch(text_path_x, transform=t_start, **text_props)
|
||||
self.box.add_artist(self.p_x)
|
||||
|
||||
text_path_y = TextPath((
|
||||
length_x*back_length+sep_x, length_y*(1-back_length)+sep_y),
|
||||
label_y, size=fontsize, prop=fontproperties)
|
||||
self.p_y = PathPatch(text_path_y, **text_props)
|
||||
self.box.add_artist(self.p_y)
|
||||
|
||||
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
|
||||
child=self.box,
|
||||
frameon=frameon, **kwargs)
|
757
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_divider.py
Normal file
757
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_divider.py
Normal file
|
@ -0,0 +1,757 @@
|
|||
"""
|
||||
Helper classes to adjust the positions of multiple axes at drawing time.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from matplotlib import cbook
|
||||
from matplotlib.axes import SubplotBase
|
||||
from matplotlib.gridspec import SubplotSpec, GridSpec
|
||||
import matplotlib.transforms as mtransforms
|
||||
from . import axes_size as Size
|
||||
|
||||
|
||||
class Divider:
|
||||
"""
|
||||
An Axes positioning class.
|
||||
|
||||
The divider is initialized with lists of horizontal and vertical sizes
|
||||
(:mod:`mpl_toolkits.axes_grid1.axes_size`) based on which a given
|
||||
rectangular area will be divided.
|
||||
|
||||
The `new_locator` method then creates a callable object
|
||||
that can be used as the *axes_locator* of the axes.
|
||||
"""
|
||||
|
||||
def __init__(self, fig, pos, horizontal, vertical,
|
||||
aspect=None, anchor="C"):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fig : Figure
|
||||
pos : tuple of 4 floats
|
||||
Position of the rectangle that will be divided.
|
||||
horizontal : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
|
||||
Sizes for horizontal division.
|
||||
vertical : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
|
||||
Sizes for vertical division.
|
||||
aspect : bool
|
||||
Whether overall rectangular area is reduced so that the relative
|
||||
part of the horizontal and vertical scales have the same scale.
|
||||
anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
|
||||
Placement of the reduced rectangle, when *aspect* is True.
|
||||
"""
|
||||
|
||||
self._fig = fig
|
||||
self._pos = pos
|
||||
self._horizontal = horizontal
|
||||
self._vertical = vertical
|
||||
self._anchor = anchor
|
||||
self._aspect = aspect
|
||||
self._xrefindex = 0
|
||||
self._yrefindex = 0
|
||||
self._locator = None
|
||||
|
||||
def get_horizontal_sizes(self, renderer):
|
||||
return [s.get_size(renderer) for s in self.get_horizontal()]
|
||||
|
||||
def get_vertical_sizes(self, renderer):
|
||||
return [s.get_size(renderer) for s in self.get_vertical()]
|
||||
|
||||
def get_vsize_hsize(self):
|
||||
vsize = Size.AddList(self.get_vertical())
|
||||
hsize = Size.AddList(self.get_horizontal())
|
||||
return vsize, hsize
|
||||
|
||||
@staticmethod
|
||||
def _calc_k(l, total_size):
|
||||
|
||||
rs_sum, as_sum = 0., 0.
|
||||
|
||||
for _rs, _as in l:
|
||||
rs_sum += _rs
|
||||
as_sum += _as
|
||||
|
||||
if rs_sum != 0.:
|
||||
k = (total_size - as_sum) / rs_sum
|
||||
return k
|
||||
else:
|
||||
return 0.
|
||||
|
||||
@staticmethod
|
||||
def _calc_offsets(l, k):
|
||||
offsets = [0.]
|
||||
for _rs, _as in l:
|
||||
offsets.append(offsets[-1] + _rs*k + _as)
|
||||
return offsets
|
||||
|
||||
def set_position(self, pos):
|
||||
"""
|
||||
Set the position of the rectangle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pos : tuple of 4 floats
|
||||
position of the rectangle that will be divided
|
||||
"""
|
||||
self._pos = pos
|
||||
|
||||
def get_position(self):
|
||||
"""Return the position of the rectangle."""
|
||||
return self._pos
|
||||
|
||||
def set_anchor(self, anchor):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
|
||||
anchor position
|
||||
|
||||
===== ============
|
||||
value description
|
||||
===== ============
|
||||
'C' Center
|
||||
'SW' bottom left
|
||||
'S' bottom
|
||||
'SE' bottom right
|
||||
'E' right
|
||||
'NE' top right
|
||||
'N' top
|
||||
'NW' top left
|
||||
'W' left
|
||||
===== ============
|
||||
|
||||
"""
|
||||
if len(anchor) != 2:
|
||||
cbook._check_in_list(mtransforms.Bbox.coefs, anchor=anchor)
|
||||
self._anchor = anchor
|
||||
|
||||
def get_anchor(self):
|
||||
"""Return the anchor."""
|
||||
return self._anchor
|
||||
|
||||
def set_horizontal(self, h):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
h : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
|
||||
sizes for horizontal division
|
||||
"""
|
||||
self._horizontal = h
|
||||
|
||||
def get_horizontal(self):
|
||||
"""Return horizontal sizes."""
|
||||
return self._horizontal
|
||||
|
||||
def set_vertical(self, v):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
v : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
|
||||
sizes for vertical division
|
||||
"""
|
||||
self._vertical = v
|
||||
|
||||
def get_vertical(self):
|
||||
"""Return vertical sizes."""
|
||||
return self._vertical
|
||||
|
||||
def set_aspect(self, aspect=False):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
aspect : bool
|
||||
"""
|
||||
self._aspect = aspect
|
||||
|
||||
def get_aspect(self):
|
||||
"""Return aspect."""
|
||||
return self._aspect
|
||||
|
||||
def set_locator(self, _locator):
|
||||
self._locator = _locator
|
||||
|
||||
def get_locator(self):
|
||||
return self._locator
|
||||
|
||||
def get_position_runtime(self, ax, renderer):
|
||||
if self._locator is None:
|
||||
return self.get_position()
|
||||
else:
|
||||
return self._locator(ax, renderer).bounds
|
||||
|
||||
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
axes
|
||||
renderer
|
||||
"""
|
||||
|
||||
figW, figH = self._fig.get_size_inches()
|
||||
x, y, w, h = self.get_position_runtime(axes, renderer)
|
||||
|
||||
hsizes = self.get_horizontal_sizes(renderer)
|
||||
vsizes = self.get_vertical_sizes(renderer)
|
||||
k_h = self._calc_k(hsizes, figW*w)
|
||||
k_v = self._calc_k(vsizes, figH*h)
|
||||
|
||||
if self.get_aspect():
|
||||
k = min(k_h, k_v)
|
||||
ox = self._calc_offsets(hsizes, k)
|
||||
oy = self._calc_offsets(vsizes, k)
|
||||
|
||||
ww = (ox[-1] - ox[0]) / figW
|
||||
hh = (oy[-1] - oy[0]) / figH
|
||||
pb = mtransforms.Bbox.from_bounds(x, y, w, h)
|
||||
pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
|
||||
pb1_anchored = pb1.anchored(self.get_anchor(), pb)
|
||||
x0, y0 = pb1_anchored.x0, pb1_anchored.y0
|
||||
|
||||
else:
|
||||
ox = self._calc_offsets(hsizes, k_h)
|
||||
oy = self._calc_offsets(vsizes, k_v)
|
||||
x0, y0 = x, y
|
||||
|
||||
if nx1 is None:
|
||||
nx1 = nx + 1
|
||||
if ny1 is None:
|
||||
ny1 = ny + 1
|
||||
|
||||
x1, w1 = x0 + ox[nx] / figW, (ox[nx1] - ox[nx]) / figW
|
||||
y1, h1 = y0 + oy[ny] / figH, (oy[ny1] - oy[ny]) / figH
|
||||
|
||||
return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
|
||||
|
||||
def new_locator(self, nx, ny, nx1=None, ny1=None):
|
||||
"""
|
||||
Return a new `AxesLocator` for the specified cell.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
"""
|
||||
return AxesLocator(self, nx, ny, nx1, ny1)
|
||||
|
||||
def append_size(self, position, size):
|
||||
if position == "left":
|
||||
self._horizontal.insert(0, size)
|
||||
self._xrefindex += 1
|
||||
elif position == "right":
|
||||
self._horizontal.append(size)
|
||||
elif position == "bottom":
|
||||
self._vertical.insert(0, size)
|
||||
self._yrefindex += 1
|
||||
elif position == "top":
|
||||
self._vertical.append(size)
|
||||
else:
|
||||
cbook._check_in_list(["left", "right", "bottom", "top"],
|
||||
position=position)
|
||||
|
||||
def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None):
|
||||
if adjust_dirs is None:
|
||||
adjust_dirs = ["left", "right", "bottom", "top"]
|
||||
from .axes_size import Padded, SizeFromFunc, GetExtentHelper
|
||||
for d in adjust_dirs:
|
||||
helper = GetExtentHelper(use_axes, d)
|
||||
size = SizeFromFunc(helper)
|
||||
padded_size = Padded(size, pad) # pad in inch
|
||||
self.append_size(d, padded_size)
|
||||
|
||||
|
||||
class AxesLocator:
|
||||
"""
|
||||
A simple callable object, initialized with AxesDivider class,
|
||||
returns the position and size of the given cell.
|
||||
"""
|
||||
def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
axes_divider : AxesDivider
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
"""
|
||||
self._axes_divider = axes_divider
|
||||
|
||||
_xrefindex = axes_divider._xrefindex
|
||||
_yrefindex = axes_divider._yrefindex
|
||||
|
||||
self._nx, self._ny = nx - _xrefindex, ny - _yrefindex
|
||||
|
||||
if nx1 is None:
|
||||
nx1 = nx + 1
|
||||
if ny1 is None:
|
||||
ny1 = ny + 1
|
||||
|
||||
self._nx1 = nx1 - _xrefindex
|
||||
self._ny1 = ny1 - _yrefindex
|
||||
|
||||
def __call__(self, axes, renderer):
|
||||
|
||||
_xrefindex = self._axes_divider._xrefindex
|
||||
_yrefindex = self._axes_divider._yrefindex
|
||||
|
||||
return self._axes_divider.locate(self._nx + _xrefindex,
|
||||
self._ny + _yrefindex,
|
||||
self._nx1 + _xrefindex,
|
||||
self._ny1 + _yrefindex,
|
||||
axes,
|
||||
renderer)
|
||||
|
||||
def get_subplotspec(self):
|
||||
if hasattr(self._axes_divider, "get_subplotspec"):
|
||||
return self._axes_divider.get_subplotspec()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class SubplotDivider(Divider):
|
||||
"""
|
||||
The Divider class whose rectangle area is specified as a subplot geometry.
|
||||
"""
|
||||
|
||||
def __init__(self, fig, *args, horizontal=None, vertical=None,
|
||||
aspect=None, anchor='C'):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fig : `matplotlib.figure.Figure`
|
||||
|
||||
*args : tuple (*nrows*, *ncols*, *index*) or int
|
||||
The array of subplots in the figure has dimensions ``(nrows,
|
||||
ncols)``, and *index* is the index of the subplot being created.
|
||||
*index* starts at 1 in the upper left corner and increases to the
|
||||
right.
|
||||
|
||||
If *nrows*, *ncols*, and *index* are all single digit numbers, then
|
||||
*args* can be passed as a single 3-digit number (e.g. 234 for
|
||||
(2, 3, 4)).
|
||||
"""
|
||||
self.figure = fig
|
||||
self._subplotspec = SubplotSpec._from_subplot_args(fig, args)
|
||||
self.update_params() # sets self.figbox
|
||||
Divider.__init__(self, fig, pos=self.figbox.bounds,
|
||||
horizontal=horizontal or [], vertical=vertical or [],
|
||||
aspect=aspect, anchor=anchor)
|
||||
|
||||
def get_position(self):
|
||||
"""Return the bounds of the subplot box."""
|
||||
self.update_params() # update self.figbox
|
||||
return self.figbox.bounds
|
||||
|
||||
def update_params(self):
|
||||
"""Update the subplot position from fig.subplotpars."""
|
||||
self.figbox = self.get_subplotspec().get_position(self.figure)
|
||||
|
||||
def get_geometry(self):
|
||||
"""Get the subplot geometry, e.g., (2, 2, 3)."""
|
||||
rows, cols, num1, num2 = self.get_subplotspec().get_geometry()
|
||||
return rows, cols, num1 + 1 # for compatibility
|
||||
|
||||
# COVERAGE NOTE: Never used internally or from examples
|
||||
def change_geometry(self, numrows, numcols, num):
|
||||
"""Change subplot geometry, e.g., from (1, 1, 1) to (2, 2, 3)."""
|
||||
self._subplotspec = GridSpec(numrows, numcols)[num-1]
|
||||
self.update_params()
|
||||
self.set_position(self.figbox)
|
||||
|
||||
def get_subplotspec(self):
|
||||
"""Get the SubplotSpec instance."""
|
||||
return self._subplotspec
|
||||
|
||||
def set_subplotspec(self, subplotspec):
|
||||
"""Set the SubplotSpec instance."""
|
||||
self._subplotspec = subplotspec
|
||||
|
||||
|
||||
class AxesDivider(Divider):
|
||||
"""
|
||||
Divider based on the pre-existing axes.
|
||||
"""
|
||||
|
||||
def __init__(self, axes, xref=None, yref=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
axes : :class:`~matplotlib.axes.Axes`
|
||||
xref
|
||||
yref
|
||||
"""
|
||||
self._axes = axes
|
||||
if xref is None:
|
||||
self._xref = Size.AxesX(axes)
|
||||
else:
|
||||
self._xref = xref
|
||||
if yref is None:
|
||||
self._yref = Size.AxesY(axes)
|
||||
else:
|
||||
self._yref = yref
|
||||
|
||||
Divider.__init__(self, fig=axes.get_figure(), pos=None,
|
||||
horizontal=[self._xref], vertical=[self._yref],
|
||||
aspect=None, anchor="C")
|
||||
|
||||
def _get_new_axes(self, *, axes_class=None, **kwargs):
|
||||
axes = self._axes
|
||||
if axes_class is None:
|
||||
if isinstance(axes, SubplotBase):
|
||||
axes_class = axes._axes_class
|
||||
else:
|
||||
axes_class = type(axes)
|
||||
return axes_class(axes.get_figure(), axes.get_position(original=True),
|
||||
**kwargs)
|
||||
|
||||
def new_horizontal(self, size, pad=None, pack_start=False, **kwargs):
|
||||
"""
|
||||
Add a new axes on the right (or left) side of the main axes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
size : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
|
||||
A width of the axes. If float or string is given, *from_any*
|
||||
function is used to create the size, with *ref_size* set to AxesX
|
||||
instance of the current axes.
|
||||
pad : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
|
||||
Pad between the axes. It takes same argument as *size*.
|
||||
pack_start : bool
|
||||
If False, the new axes is appended at the end
|
||||
of the list, i.e., it became the right-most axes. If True, it is
|
||||
inserted at the start of the list, and becomes the left-most axes.
|
||||
**kwargs
|
||||
All extra keywords arguments are passed to the created axes.
|
||||
If *axes_class* is given, the new axes will be created as an
|
||||
instance of the given class. Otherwise, the same class of the
|
||||
main axes will be used.
|
||||
"""
|
||||
if pad is None:
|
||||
cbook.warn_deprecated(
|
||||
"3.2", message="In a future version, 'pad' will default to "
|
||||
"rcParams['figure.subplot.wspace']. Set pad=0 to keep the "
|
||||
"old behavior.")
|
||||
if pad:
|
||||
if not isinstance(pad, Size._Base):
|
||||
pad = Size.from_any(pad, fraction_ref=self._xref)
|
||||
if pack_start:
|
||||
self._horizontal.insert(0, pad)
|
||||
self._xrefindex += 1
|
||||
else:
|
||||
self._horizontal.append(pad)
|
||||
if not isinstance(size, Size._Base):
|
||||
size = Size.from_any(size, fraction_ref=self._xref)
|
||||
if pack_start:
|
||||
self._horizontal.insert(0, size)
|
||||
self._xrefindex += 1
|
||||
locator = self.new_locator(nx=0, ny=self._yrefindex)
|
||||
else:
|
||||
self._horizontal.append(size)
|
||||
locator = self.new_locator(
|
||||
nx=len(self._horizontal) - 1, ny=self._yrefindex)
|
||||
ax = self._get_new_axes(**kwargs)
|
||||
ax.set_axes_locator(locator)
|
||||
return ax
|
||||
|
||||
def new_vertical(self, size, pad=None, pack_start=False, **kwargs):
|
||||
"""
|
||||
Add a new axes on the top (or bottom) side of the main axes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
size : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
|
||||
A height of the axes. If float or string is given, *from_any*
|
||||
function is used to create the size, with *ref_size* set to AxesX
|
||||
instance of the current axes.
|
||||
pad : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
|
||||
Pad between the axes. It takes same argument as *size*.
|
||||
pack_start : bool
|
||||
If False, the new axes is appended at the end
|
||||
of the list, i.e., it became the right-most axes. If True, it is
|
||||
inserted at the start of the list, and becomes the left-most axes.
|
||||
**kwargs
|
||||
All extra keywords arguments are passed to the created axes.
|
||||
If *axes_class* is given, the new axes will be created as an
|
||||
instance of the given class. Otherwise, the same class of the
|
||||
main axes will be used.
|
||||
"""
|
||||
if pad is None:
|
||||
cbook.warn_deprecated(
|
||||
"3.2", message="In a future version, 'pad' will default to "
|
||||
"rcParams['figure.subplot.hspace']. Set pad=0 to keep the "
|
||||
"old behavior.")
|
||||
if pad:
|
||||
if not isinstance(pad, Size._Base):
|
||||
pad = Size.from_any(pad, fraction_ref=self._yref)
|
||||
if pack_start:
|
||||
self._vertical.insert(0, pad)
|
||||
self._yrefindex += 1
|
||||
else:
|
||||
self._vertical.append(pad)
|
||||
if not isinstance(size, Size._Base):
|
||||
size = Size.from_any(size, fraction_ref=self._yref)
|
||||
if pack_start:
|
||||
self._vertical.insert(0, size)
|
||||
self._yrefindex += 1
|
||||
locator = self.new_locator(nx=self._xrefindex, ny=0)
|
||||
else:
|
||||
self._vertical.append(size)
|
||||
locator = self.new_locator(
|
||||
nx=self._xrefindex, ny=len(self._vertical)-1)
|
||||
ax = self._get_new_axes(**kwargs)
|
||||
ax.set_axes_locator(locator)
|
||||
return ax
|
||||
|
||||
def append_axes(self, position, size, pad=None, add_to_figure=True,
|
||||
**kwargs):
|
||||
"""
|
||||
Create an axes at the given *position* with the same height
|
||||
(or width) of the main axes.
|
||||
|
||||
*position*
|
||||
["left"|"right"|"bottom"|"top"]
|
||||
|
||||
*size* and *pad* should be axes_grid.axes_size compatible.
|
||||
"""
|
||||
if position == "left":
|
||||
ax = self.new_horizontal(size, pad, pack_start=True, **kwargs)
|
||||
elif position == "right":
|
||||
ax = self.new_horizontal(size, pad, pack_start=False, **kwargs)
|
||||
elif position == "bottom":
|
||||
ax = self.new_vertical(size, pad, pack_start=True, **kwargs)
|
||||
elif position == "top":
|
||||
ax = self.new_vertical(size, pad, pack_start=False, **kwargs)
|
||||
else:
|
||||
cbook._check_in_list(["left", "right", "bottom", "top"],
|
||||
position=position)
|
||||
if add_to_figure:
|
||||
self._fig.add_axes(ax)
|
||||
return ax
|
||||
|
||||
def get_aspect(self):
|
||||
if self._aspect is None:
|
||||
aspect = self._axes.get_aspect()
|
||||
if aspect == "auto":
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return self._aspect
|
||||
|
||||
def get_position(self):
|
||||
if self._pos is None:
|
||||
bbox = self._axes.get_position(original=True)
|
||||
return bbox.bounds
|
||||
else:
|
||||
return self._pos
|
||||
|
||||
def get_anchor(self):
|
||||
if self._anchor is None:
|
||||
return self._axes.get_anchor()
|
||||
else:
|
||||
return self._anchor
|
||||
|
||||
def get_subplotspec(self):
|
||||
if hasattr(self._axes, "get_subplotspec"):
|
||||
return self._axes.get_subplotspec()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class HBoxDivider(SubplotDivider):
|
||||
|
||||
@staticmethod
|
||||
def _determine_karray(equivalent_sizes, appended_sizes,
|
||||
max_equivalent_size,
|
||||
total_appended_size):
|
||||
|
||||
n = len(equivalent_sizes)
|
||||
eq_rs, eq_as = np.asarray(equivalent_sizes).T
|
||||
ap_rs, ap_as = np.asarray(appended_sizes).T
|
||||
A = np.zeros((n + 1, n + 1))
|
||||
B = np.zeros(n + 1)
|
||||
np.fill_diagonal(A[:n, :n], eq_rs)
|
||||
A[:n, -1] = -1
|
||||
A[-1, :-1] = ap_rs
|
||||
B[:n] = -eq_as
|
||||
B[-1] = total_appended_size - sum(ap_as)
|
||||
|
||||
karray_H = np.linalg.solve(A, B) # A @ K = B
|
||||
karray = karray_H[:-1]
|
||||
H = karray_H[-1]
|
||||
|
||||
if H > max_equivalent_size:
|
||||
karray = (max_equivalent_size - eq_as) / eq_rs
|
||||
return karray
|
||||
|
||||
@staticmethod
|
||||
def _calc_offsets(appended_sizes, karray):
|
||||
offsets = [0.]
|
||||
for (r, a), k in zip(appended_sizes, karray):
|
||||
offsets.append(offsets[-1] + r*k + a)
|
||||
return offsets
|
||||
|
||||
def new_locator(self, nx, nx1=None):
|
||||
"""
|
||||
Create a new `~mpl_toolkits.axes_grid.axes_divider.AxesLocator` for
|
||||
the specified cell.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
"""
|
||||
return AxesLocator(self, nx, 0, nx1, None)
|
||||
|
||||
def _locate(self, x, y, w, h,
|
||||
y_equivalent_sizes, x_appended_sizes,
|
||||
figW, figH):
|
||||
equivalent_sizes = y_equivalent_sizes
|
||||
appended_sizes = x_appended_sizes
|
||||
|
||||
max_equivalent_size = figH * h
|
||||
total_appended_size = figW * w
|
||||
karray = self._determine_karray(equivalent_sizes, appended_sizes,
|
||||
max_equivalent_size,
|
||||
total_appended_size)
|
||||
|
||||
ox = self._calc_offsets(appended_sizes, karray)
|
||||
|
||||
ww = (ox[-1] - ox[0]) / figW
|
||||
ref_h = equivalent_sizes[0]
|
||||
hh = (karray[0]*ref_h[0] + ref_h[1]) / figH
|
||||
pb = mtransforms.Bbox.from_bounds(x, y, w, h)
|
||||
pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
|
||||
pb1_anchored = pb1.anchored(self.get_anchor(), pb)
|
||||
x0, y0 = pb1_anchored.x0, pb1_anchored.y0
|
||||
|
||||
return x0, y0, ox, hh
|
||||
|
||||
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
axes_divider : AxesDivider
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
axes
|
||||
renderer
|
||||
"""
|
||||
|
||||
figW, figH = self._fig.get_size_inches()
|
||||
x, y, w, h = self.get_position_runtime(axes, renderer)
|
||||
|
||||
y_equivalent_sizes = self.get_vertical_sizes(renderer)
|
||||
x_appended_sizes = self.get_horizontal_sizes(renderer)
|
||||
x0, y0, ox, hh = self._locate(x, y, w, h,
|
||||
y_equivalent_sizes, x_appended_sizes,
|
||||
figW, figH)
|
||||
if nx1 is None:
|
||||
nx1 = nx + 1
|
||||
|
||||
x1, w1 = x0 + ox[nx] / figW, (ox[nx1] - ox[nx]) / figW
|
||||
y1, h1 = y0, hh
|
||||
|
||||
return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
|
||||
|
||||
|
||||
class VBoxDivider(HBoxDivider):
|
||||
"""
|
||||
The Divider class whose rectangle area is specified as a subplot geometry.
|
||||
"""
|
||||
|
||||
def new_locator(self, ny, ny1=None):
|
||||
"""
|
||||
Create a new `~mpl_toolkits.axes_grid.axes_divider.AxesLocator` for
|
||||
the specified cell.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ny, ny1 : int
|
||||
Integers specifying the row-position of the
|
||||
cell. When *ny1* is None, a single *ny*-th row is
|
||||
specified. Otherwise location of rows spanning between *ny*
|
||||
to *ny1* (but excluding *ny1*-th row) is specified.
|
||||
"""
|
||||
return AxesLocator(self, 0, ny, None, ny1)
|
||||
|
||||
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
axes_divider : AxesDivider
|
||||
nx, nx1 : int
|
||||
Integers specifying the column-position of the
|
||||
cell. When *nx1* is None, a single *nx*-th column is
|
||||
specified. Otherwise location of columns spanning between *nx*
|
||||
to *nx1* (but excluding *nx1*-th column) is specified.
|
||||
ny, ny1 : int
|
||||
Same as *nx* and *nx1*, but for row positions.
|
||||
axes
|
||||
renderer
|
||||
"""
|
||||
|
||||
figW, figH = self._fig.get_size_inches()
|
||||
x, y, w, h = self.get_position_runtime(axes, renderer)
|
||||
|
||||
x_equivalent_sizes = self.get_horizontal_sizes(renderer)
|
||||
y_appended_sizes = self.get_vertical_sizes(renderer)
|
||||
|
||||
y0, x0, oy, ww = self._locate(y, x, h, w,
|
||||
x_equivalent_sizes, y_appended_sizes,
|
||||
figH, figW)
|
||||
if ny1 is None:
|
||||
ny1 = ny + 1
|
||||
|
||||
x1, w1 = x0, ww
|
||||
y1, h1 = y0 + oy[ny] / figH, (oy[ny1] - oy[ny]) / figH
|
||||
|
||||
return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
|
||||
|
||||
|
||||
def make_axes_locatable(axes):
|
||||
divider = AxesDivider(axes)
|
||||
locator = divider.new_locator(nx=0, ny=0)
|
||||
axes.set_axes_locator(locator)
|
||||
|
||||
return divider
|
||||
|
||||
|
||||
def make_axes_area_auto_adjustable(ax,
|
||||
use_axes=None, pad=0.1,
|
||||
adjust_dirs=None):
|
||||
if adjust_dirs is None:
|
||||
adjust_dirs = ["left", "right", "bottom", "top"]
|
||||
divider = make_axes_locatable(ax)
|
||||
|
||||
if use_axes is None:
|
||||
use_axes = ax
|
||||
|
||||
divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad,
|
||||
adjust_dirs=adjust_dirs)
|
624
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_grid.py
Normal file
624
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_grid.py
Normal file
|
@ -0,0 +1,624 @@
|
|||
from numbers import Number
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import cbook
|
||||
import matplotlib.ticker as ticker
|
||||
from matplotlib.gridspec import SubplotSpec
|
||||
|
||||
from .axes_divider import Size, SubplotDivider, Divider
|
||||
from .mpl_axes import Axes
|
||||
|
||||
|
||||
def _tick_only(ax, bottom_on, left_on):
|
||||
bottom_off = not bottom_on
|
||||
left_off = not left_on
|
||||
ax.axis["bottom"].toggle(ticklabels=bottom_off, label=bottom_off)
|
||||
ax.axis["left"].toggle(ticklabels=left_off, label=left_off)
|
||||
|
||||
|
||||
class CbarAxesBase:
|
||||
def __init__(self, *args, orientation, **kwargs):
|
||||
self.orientation = orientation
|
||||
self._default_label_on = True
|
||||
self._locator = None # deprecated.
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@cbook._rename_parameter("3.2", "locator", "ticks")
|
||||
def colorbar(self, mappable, *, ticks=None, **kwargs):
|
||||
|
||||
if self.orientation in ["top", "bottom"]:
|
||||
orientation = "horizontal"
|
||||
else:
|
||||
orientation = "vertical"
|
||||
|
||||
if mpl.rcParams["mpl_toolkits.legacy_colorbar"]:
|
||||
cbook.warn_deprecated(
|
||||
"3.2", message="Since %(since)s, mpl_toolkits's own colorbar "
|
||||
"implementation is deprecated; it will be removed "
|
||||
"%(removal)s. Set the 'mpl_toolkits.legacy_colorbar' rcParam "
|
||||
"to False to use Matplotlib's default colorbar implementation "
|
||||
"and suppress this deprecation warning.")
|
||||
if ticks is None:
|
||||
ticks = ticker.MaxNLocator(5) # For backcompat.
|
||||
from .colorbar import Colorbar
|
||||
cb = Colorbar(
|
||||
self, mappable, orientation=orientation, ticks=ticks, **kwargs)
|
||||
self._cbid = mappable.callbacksSM.connect(
|
||||
'changed', cb.update_normal)
|
||||
mappable.colorbar = cb
|
||||
self._locator = cb.cbar_axis.get_major_locator()
|
||||
else:
|
||||
cb = mpl.colorbar.colorbar_factory(
|
||||
self, mappable, orientation=orientation, ticks=ticks, **kwargs)
|
||||
self._cbid = mappable.colorbar_cid # deprecated.
|
||||
self._locator = cb.locator # deprecated.
|
||||
|
||||
self._config_axes()
|
||||
return cb
|
||||
|
||||
cbid = cbook._deprecate_privatize_attribute(
|
||||
"3.3", alternative="mappable.colorbar_cid")
|
||||
locator = cbook._deprecate_privatize_attribute(
|
||||
"3.3", alternative=".colorbar().locator")
|
||||
|
||||
def _config_axes(self):
|
||||
"""Make an axes patch and outline."""
|
||||
ax = self
|
||||
ax.set_navigate(False)
|
||||
ax.axis[:].toggle(all=False)
|
||||
b = self._default_label_on
|
||||
ax.axis[self.orientation].toggle(all=b)
|
||||
|
||||
def toggle_label(self, b):
|
||||
self._default_label_on = b
|
||||
axis = self.axis[self.orientation]
|
||||
axis.toggle(ticklabels=b, label=b)
|
||||
|
||||
def cla(self):
|
||||
super().cla()
|
||||
self._config_axes()
|
||||
|
||||
|
||||
class CbarAxes(CbarAxesBase, Axes):
|
||||
pass
|
||||
|
||||
|
||||
class Grid:
|
||||
"""
|
||||
A grid of Axes.
|
||||
|
||||
In Matplotlib, the axes location (and size) is specified in normalized
|
||||
figure coordinates. This may not be ideal for images that needs to be
|
||||
displayed with a given aspect ratio; for example, it is difficult to
|
||||
display multiple images of a same size with some fixed padding between
|
||||
them. AxesGrid can be used in such case.
|
||||
"""
|
||||
|
||||
_defaultAxesClass = Axes
|
||||
|
||||
@cbook._delete_parameter("3.3", "add_all")
|
||||
def __init__(self, fig,
|
||||
rect,
|
||||
nrows_ncols,
|
||||
ngrids=None,
|
||||
direction="row",
|
||||
axes_pad=0.02,
|
||||
add_all=True,
|
||||
share_all=False,
|
||||
share_x=True,
|
||||
share_y=True,
|
||||
label_mode="L",
|
||||
axes_class=None,
|
||||
*,
|
||||
aspect=False,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fig : `.Figure`
|
||||
The parent figure.
|
||||
rect : (float, float, float, float) or int
|
||||
The axes position, as a ``(left, bottom, width, height)`` tuple or
|
||||
as a three-digit subplot position code (e.g., "121").
|
||||
nrows_ncols : (int, int)
|
||||
Number of rows and columns in the grid.
|
||||
ngrids : int or None, default: None
|
||||
If not None, only the first *ngrids* axes in the grid are created.
|
||||
direction : {"row", "column"}, default: "row"
|
||||
Whether axes are created in row-major ("row by row") or
|
||||
column-major order ("column by column").
|
||||
axes_pad : float or (float, float), default: 0.02
|
||||
Padding or (horizontal padding, vertical padding) between axes, in
|
||||
inches.
|
||||
add_all : bool, default: True
|
||||
Whether to add the axes to the figure using `.Figure.add_axes`.
|
||||
This parameter is deprecated.
|
||||
share_all : bool, default: False
|
||||
Whether all axes share their x- and y-axis. Overrides *share_x*
|
||||
and *share_y*.
|
||||
share_x : bool, default: True
|
||||
Whether all axes of a column share their x-axis.
|
||||
share_y : bool, default: True
|
||||
Whether all axes of a row share their y-axis.
|
||||
label_mode : {"L", "1", "all"}, default: "L"
|
||||
Determines which axes will get tick labels:
|
||||
|
||||
- "L": All axes on the left column get vertical tick labels;
|
||||
all axes on the bottom row get horizontal tick labels.
|
||||
- "1": Only the bottom left axes is labelled.
|
||||
- "all": all axes are labelled.
|
||||
|
||||
axes_class : subclass of `matplotlib.axes.Axes`, default: None
|
||||
aspect : bool, default: False
|
||||
Whether the axes aspect ratio follows the aspect ratio of the data
|
||||
limits.
|
||||
"""
|
||||
self._nrows, self._ncols = nrows_ncols
|
||||
|
||||
if ngrids is None:
|
||||
ngrids = self._nrows * self._ncols
|
||||
else:
|
||||
if not 0 < ngrids <= self._nrows * self._ncols:
|
||||
raise Exception("")
|
||||
|
||||
self.ngrids = ngrids
|
||||
|
||||
self._horiz_pad_size, self._vert_pad_size = map(
|
||||
Size.Fixed, np.broadcast_to(axes_pad, 2))
|
||||
|
||||
cbook._check_in_list(["column", "row"], direction=direction)
|
||||
self._direction = direction
|
||||
|
||||
if axes_class is None:
|
||||
axes_class = self._defaultAxesClass
|
||||
elif isinstance(axes_class, (list, tuple)):
|
||||
cls, kwargs = axes_class
|
||||
axes_class = functools.partial(cls, **kwargs)
|
||||
|
||||
kw = dict(horizontal=[], vertical=[], aspect=aspect)
|
||||
if isinstance(rect, (str, Number, SubplotSpec)):
|
||||
self._divider = SubplotDivider(fig, rect, **kw)
|
||||
elif len(rect) == 3:
|
||||
self._divider = SubplotDivider(fig, *rect, **kw)
|
||||
elif len(rect) == 4:
|
||||
self._divider = Divider(fig, rect, **kw)
|
||||
else:
|
||||
raise Exception("")
|
||||
|
||||
rect = self._divider.get_position()
|
||||
|
||||
axes_array = np.full((self._nrows, self._ncols), None, dtype=object)
|
||||
for i in range(self.ngrids):
|
||||
col, row = self._get_col_row(i)
|
||||
if share_all:
|
||||
sharex = sharey = axes_array[0, 0]
|
||||
else:
|
||||
sharex = axes_array[0, col] if share_x else None
|
||||
sharey = axes_array[row, 0] if share_y else None
|
||||
axes_array[row, col] = axes_class(
|
||||
fig, rect, sharex=sharex, sharey=sharey)
|
||||
self.axes_all = axes_array.ravel().tolist()
|
||||
self.axes_column = axes_array.T.tolist()
|
||||
self.axes_row = axes_array.tolist()
|
||||
self.axes_llc = self.axes_column[0][-1]
|
||||
|
||||
self._init_locators()
|
||||
|
||||
if add_all:
|
||||
for ax in self.axes_all:
|
||||
fig.add_axes(ax)
|
||||
|
||||
self.set_label_mode(label_mode)
|
||||
|
||||
def _init_locators(self):
|
||||
|
||||
h = []
|
||||
h_ax_pos = []
|
||||
for _ in range(self._ncols):
|
||||
if h:
|
||||
h.append(self._horiz_pad_size)
|
||||
h_ax_pos.append(len(h))
|
||||
sz = Size.Scaled(1)
|
||||
h.append(sz)
|
||||
|
||||
v = []
|
||||
v_ax_pos = []
|
||||
for _ in range(self._nrows):
|
||||
if v:
|
||||
v.append(self._vert_pad_size)
|
||||
v_ax_pos.append(len(v))
|
||||
sz = Size.Scaled(1)
|
||||
v.append(sz)
|
||||
|
||||
for i in range(self.ngrids):
|
||||
col, row = self._get_col_row(i)
|
||||
locator = self._divider.new_locator(
|
||||
nx=h_ax_pos[col], ny=v_ax_pos[self._nrows - 1 - row])
|
||||
self.axes_all[i].set_axes_locator(locator)
|
||||
|
||||
self._divider.set_horizontal(h)
|
||||
self._divider.set_vertical(v)
|
||||
|
||||
def _get_col_row(self, n):
|
||||
if self._direction == "column":
|
||||
col, row = divmod(n, self._nrows)
|
||||
else:
|
||||
row, col = divmod(n, self._ncols)
|
||||
|
||||
return col, row
|
||||
|
||||
# Good to propagate __len__ if we have __getitem__
|
||||
def __len__(self):
|
||||
return len(self.axes_all)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.axes_all[i]
|
||||
|
||||
def get_geometry(self):
|
||||
"""
|
||||
Return the number of rows and columns of the grid as (nrows, ncols).
|
||||
"""
|
||||
return self._nrows, self._ncols
|
||||
|
||||
def set_axes_pad(self, axes_pad):
|
||||
"""
|
||||
Set the padding between the axes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
axes_pad : (float, float)
|
||||
The padding (horizontal pad, vertical pad) in inches.
|
||||
"""
|
||||
self._horiz_pad_size.fixed_size = axes_pad[0]
|
||||
self._vert_pad_size.fixed_size = axes_pad[1]
|
||||
|
||||
def get_axes_pad(self):
|
||||
"""
|
||||
Return the axes padding.
|
||||
|
||||
Returns
|
||||
-------
|
||||
hpad, vpad
|
||||
Padding (horizontal pad, vertical pad) in inches.
|
||||
"""
|
||||
return (self._horiz_pad_size.fixed_size,
|
||||
self._vert_pad_size.fixed_size)
|
||||
|
||||
def set_aspect(self, aspect):
|
||||
"""Set the aspect of the SubplotDivider."""
|
||||
self._divider.set_aspect(aspect)
|
||||
|
||||
def get_aspect(self):
|
||||
"""Return the aspect of the SubplotDivider."""
|
||||
return self._divider.get_aspect()
|
||||
|
||||
def set_label_mode(self, mode):
|
||||
"""
|
||||
Define which axes have tick labels.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mode : {"L", "1", "all"}
|
||||
The label mode:
|
||||
|
||||
- "L": All axes on the left column get vertical tick labels;
|
||||
all axes on the bottom row get horizontal tick labels.
|
||||
- "1": Only the bottom left axes is labelled.
|
||||
- "all": all axes are labelled.
|
||||
"""
|
||||
if mode == "all":
|
||||
for ax in self.axes_all:
|
||||
_tick_only(ax, False, False)
|
||||
elif mode == "L":
|
||||
# left-most axes
|
||||
for ax in self.axes_column[0][:-1]:
|
||||
_tick_only(ax, bottom_on=True, left_on=False)
|
||||
# lower-left axes
|
||||
ax = self.axes_column[0][-1]
|
||||
_tick_only(ax, bottom_on=False, left_on=False)
|
||||
|
||||
for col in self.axes_column[1:]:
|
||||
# axes with no labels
|
||||
for ax in col[:-1]:
|
||||
_tick_only(ax, bottom_on=True, left_on=True)
|
||||
|
||||
# bottom
|
||||
ax = col[-1]
|
||||
_tick_only(ax, bottom_on=False, left_on=True)
|
||||
|
||||
elif mode == "1":
|
||||
for ax in self.axes_all:
|
||||
_tick_only(ax, bottom_on=True, left_on=True)
|
||||
|
||||
ax = self.axes_llc
|
||||
_tick_only(ax, bottom_on=False, left_on=False)
|
||||
|
||||
def get_divider(self):
|
||||
return self._divider
|
||||
|
||||
def set_axes_locator(self, locator):
|
||||
self._divider.set_locator(locator)
|
||||
|
||||
def get_axes_locator(self):
|
||||
return self._divider.get_locator()
|
||||
|
||||
def get_vsize_hsize(self):
|
||||
return self._divider.get_vsize_hsize()
|
||||
|
||||
|
||||
class ImageGrid(Grid):
|
||||
# docstring inherited
|
||||
|
||||
_defaultCbarAxesClass = CbarAxes
|
||||
|
||||
@cbook._delete_parameter("3.3", "add_all")
|
||||
def __init__(self, fig,
|
||||
rect,
|
||||
nrows_ncols,
|
||||
ngrids=None,
|
||||
direction="row",
|
||||
axes_pad=0.02,
|
||||
add_all=True,
|
||||
share_all=False,
|
||||
aspect=True,
|
||||
label_mode="L",
|
||||
cbar_mode=None,
|
||||
cbar_location="right",
|
||||
cbar_pad=None,
|
||||
cbar_size="5%",
|
||||
cbar_set_cax=True,
|
||||
axes_class=None,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fig : `.Figure`
|
||||
The parent figure.
|
||||
rect : (float, float, float, float) or int
|
||||
The axes position, as a ``(left, bottom, width, height)`` tuple or
|
||||
as a three-digit subplot position code (e.g., "121").
|
||||
nrows_ncols : (int, int)
|
||||
Number of rows and columns in the grid.
|
||||
ngrids : int or None, default: None
|
||||
If not None, only the first *ngrids* axes in the grid are created.
|
||||
direction : {"row", "column"}, default: "row"
|
||||
Whether axes are created in row-major ("row by row") or
|
||||
column-major order ("column by column"). This also affects the
|
||||
order in which axes are accessed using indexing (``grid[index]``).
|
||||
axes_pad : float or (float, float), default: 0.02in
|
||||
Padding or (horizontal padding, vertical padding) between axes, in
|
||||
inches.
|
||||
add_all : bool, default: True
|
||||
Whether to add the axes to the figure using `.Figure.add_axes`.
|
||||
This parameter is deprecated.
|
||||
share_all : bool, default: False
|
||||
Whether all axes share their x- and y-axis.
|
||||
aspect : bool, default: True
|
||||
Whether the axes aspect ratio follows the aspect ratio of the data
|
||||
limits.
|
||||
label_mode : {"L", "1", "all"}, default: "L"
|
||||
Determines which axes will get tick labels:
|
||||
|
||||
- "L": All axes on the left column get vertical tick labels;
|
||||
all axes on the bottom row get horizontal tick labels.
|
||||
- "1": Only the bottom left axes is labelled.
|
||||
- "all": all axes are labelled.
|
||||
|
||||
cbar_mode : {"each", "single", "edge", None}, default: None
|
||||
Whether to create a colorbar for "each" axes, a "single" colorbar
|
||||
for the entire grid, colorbars only for axes on the "edge"
|
||||
determined by *cbar_location*, or no colorbars. The colorbars are
|
||||
stored in the :attr:`cbar_axes` attribute.
|
||||
cbar_location : {"left", "right", "bottom", "top"}, default: "right"
|
||||
cbar_pad : float, default: None
|
||||
Padding between the image axes and the colorbar axes.
|
||||
cbar_size : size specification (see `.Size.from_any`), default: "5%"
|
||||
Colorbar size.
|
||||
cbar_set_cax : bool, default: True
|
||||
If True, each axes in the grid has a *cax* attribute that is bound
|
||||
to associated *cbar_axes*.
|
||||
axes_class : subclass of `matplotlib.axes.Axes`, default: None
|
||||
"""
|
||||
self._colorbar_mode = cbar_mode
|
||||
self._colorbar_location = cbar_location
|
||||
self._colorbar_pad = cbar_pad
|
||||
self._colorbar_size = cbar_size
|
||||
# The colorbar axes are created in _init_locators().
|
||||
|
||||
if add_all:
|
||||
super().__init__(
|
||||
fig, rect, nrows_ncols, ngrids,
|
||||
direction=direction, axes_pad=axes_pad,
|
||||
share_all=share_all, share_x=True, share_y=True, aspect=aspect,
|
||||
label_mode=label_mode, axes_class=axes_class)
|
||||
else: # Only show deprecation in that case.
|
||||
super().__init__(
|
||||
fig, rect, nrows_ncols, ngrids,
|
||||
direction=direction, axes_pad=axes_pad, add_all=add_all,
|
||||
share_all=share_all, share_x=True, share_y=True, aspect=aspect,
|
||||
label_mode=label_mode, axes_class=axes_class)
|
||||
|
||||
if add_all:
|
||||
for ax in self.cbar_axes:
|
||||
fig.add_axes(ax)
|
||||
|
||||
if cbar_set_cax:
|
||||
if self._colorbar_mode == "single":
|
||||
for ax in self.axes_all:
|
||||
ax.cax = self.cbar_axes[0]
|
||||
elif self._colorbar_mode == "edge":
|
||||
for index, ax in enumerate(self.axes_all):
|
||||
col, row = self._get_col_row(index)
|
||||
if self._colorbar_location in ("left", "right"):
|
||||
ax.cax = self.cbar_axes[row]
|
||||
else:
|
||||
ax.cax = self.cbar_axes[col]
|
||||
else:
|
||||
for ax, cax in zip(self.axes_all, self.cbar_axes):
|
||||
ax.cax = cax
|
||||
|
||||
def _init_locators(self):
|
||||
# Slightly abusing this method to inject colorbar creation into init.
|
||||
|
||||
if self._colorbar_pad is None:
|
||||
# horizontal or vertical arrangement?
|
||||
if self._colorbar_location in ("left", "right"):
|
||||
self._colorbar_pad = self._horiz_pad_size.fixed_size
|
||||
else:
|
||||
self._colorbar_pad = self._vert_pad_size.fixed_size
|
||||
self.cbar_axes = [
|
||||
self._defaultCbarAxesClass(
|
||||
self.axes_all[0].figure, self._divider.get_position(),
|
||||
orientation=self._colorbar_location)
|
||||
for _ in range(self.ngrids)]
|
||||
|
||||
cb_mode = self._colorbar_mode
|
||||
cb_location = self._colorbar_location
|
||||
|
||||
h = []
|
||||
v = []
|
||||
|
||||
h_ax_pos = []
|
||||
h_cb_pos = []
|
||||
if cb_mode == "single" and cb_location in ("left", "bottom"):
|
||||
if cb_location == "left":
|
||||
sz = self._nrows * Size.AxesX(self.axes_llc)
|
||||
h.append(Size.from_any(self._colorbar_size, sz))
|
||||
h.append(Size.from_any(self._colorbar_pad, sz))
|
||||
locator = self._divider.new_locator(nx=0, ny=0, ny1=-1)
|
||||
elif cb_location == "bottom":
|
||||
sz = self._ncols * Size.AxesY(self.axes_llc)
|
||||
v.append(Size.from_any(self._colorbar_size, sz))
|
||||
v.append(Size.from_any(self._colorbar_pad, sz))
|
||||
locator = self._divider.new_locator(nx=0, nx1=-1, ny=0)
|
||||
for i in range(self.ngrids):
|
||||
self.cbar_axes[i].set_visible(False)
|
||||
self.cbar_axes[0].set_axes_locator(locator)
|
||||
self.cbar_axes[0].set_visible(True)
|
||||
|
||||
for col, ax in enumerate(self.axes_row[0]):
|
||||
if h:
|
||||
h.append(self._horiz_pad_size)
|
||||
|
||||
if ax:
|
||||
sz = Size.AxesX(ax, aspect="axes", ref_ax=self.axes_all[0])
|
||||
else:
|
||||
sz = Size.AxesX(self.axes_all[0],
|
||||
aspect="axes", ref_ax=self.axes_all[0])
|
||||
|
||||
if (cb_location == "left"
|
||||
and (cb_mode == "each"
|
||||
or (cb_mode == "edge" and col == 0))):
|
||||
h_cb_pos.append(len(h))
|
||||
h.append(Size.from_any(self._colorbar_size, sz))
|
||||
h.append(Size.from_any(self._colorbar_pad, sz))
|
||||
|
||||
h_ax_pos.append(len(h))
|
||||
h.append(sz)
|
||||
|
||||
if (cb_location == "right"
|
||||
and (cb_mode == "each"
|
||||
or (cb_mode == "edge" and col == self._ncols - 1))):
|
||||
h.append(Size.from_any(self._colorbar_pad, sz))
|
||||
h_cb_pos.append(len(h))
|
||||
h.append(Size.from_any(self._colorbar_size, sz))
|
||||
|
||||
v_ax_pos = []
|
||||
v_cb_pos = []
|
||||
for row, ax in enumerate(self.axes_column[0][::-1]):
|
||||
if v:
|
||||
v.append(self._vert_pad_size)
|
||||
|
||||
if ax:
|
||||
sz = Size.AxesY(ax, aspect="axes", ref_ax=self.axes_all[0])
|
||||
else:
|
||||
sz = Size.AxesY(self.axes_all[0],
|
||||
aspect="axes", ref_ax=self.axes_all[0])
|
||||
|
||||
if (cb_location == "bottom"
|
||||
and (cb_mode == "each"
|
||||
or (cb_mode == "edge" and row == 0))):
|
||||
v_cb_pos.append(len(v))
|
||||
v.append(Size.from_any(self._colorbar_size, sz))
|
||||
v.append(Size.from_any(self._colorbar_pad, sz))
|
||||
|
||||
v_ax_pos.append(len(v))
|
||||
v.append(sz)
|
||||
|
||||
if (cb_location == "top"
|
||||
and (cb_mode == "each"
|
||||
or (cb_mode == "edge" and row == self._nrows - 1))):
|
||||
v.append(Size.from_any(self._colorbar_pad, sz))
|
||||
v_cb_pos.append(len(v))
|
||||
v.append(Size.from_any(self._colorbar_size, sz))
|
||||
|
||||
for i in range(self.ngrids):
|
||||
col, row = self._get_col_row(i)
|
||||
locator = self._divider.new_locator(nx=h_ax_pos[col],
|
||||
ny=v_ax_pos[self._nrows-1-row])
|
||||
self.axes_all[i].set_axes_locator(locator)
|
||||
|
||||
if cb_mode == "each":
|
||||
if cb_location in ("right", "left"):
|
||||
locator = self._divider.new_locator(
|
||||
nx=h_cb_pos[col], ny=v_ax_pos[self._nrows - 1 - row])
|
||||
|
||||
elif cb_location in ("top", "bottom"):
|
||||
locator = self._divider.new_locator(
|
||||
nx=h_ax_pos[col], ny=v_cb_pos[self._nrows - 1 - row])
|
||||
|
||||
self.cbar_axes[i].set_axes_locator(locator)
|
||||
elif cb_mode == "edge":
|
||||
if (cb_location == "left" and col == 0
|
||||
or cb_location == "right" and col == self._ncols - 1):
|
||||
locator = self._divider.new_locator(
|
||||
nx=h_cb_pos[0], ny=v_ax_pos[self._nrows - 1 - row])
|
||||
self.cbar_axes[row].set_axes_locator(locator)
|
||||
elif (cb_location == "bottom" and row == self._nrows - 1
|
||||
or cb_location == "top" and row == 0):
|
||||
locator = self._divider.new_locator(nx=h_ax_pos[col],
|
||||
ny=v_cb_pos[0])
|
||||
self.cbar_axes[col].set_axes_locator(locator)
|
||||
|
||||
if cb_mode == "single":
|
||||
if cb_location == "right":
|
||||
sz = self._nrows * Size.AxesX(self.axes_llc)
|
||||
h.append(Size.from_any(self._colorbar_pad, sz))
|
||||
h.append(Size.from_any(self._colorbar_size, sz))
|
||||
locator = self._divider.new_locator(nx=-2, ny=0, ny1=-1)
|
||||
elif cb_location == "top":
|
||||
sz = self._ncols * Size.AxesY(self.axes_llc)
|
||||
v.append(Size.from_any(self._colorbar_pad, sz))
|
||||
v.append(Size.from_any(self._colorbar_size, sz))
|
||||
locator = self._divider.new_locator(nx=0, nx1=-1, ny=-2)
|
||||
if cb_location in ("right", "top"):
|
||||
for i in range(self.ngrids):
|
||||
self.cbar_axes[i].set_visible(False)
|
||||
self.cbar_axes[0].set_axes_locator(locator)
|
||||
self.cbar_axes[0].set_visible(True)
|
||||
elif cb_mode == "each":
|
||||
for i in range(self.ngrids):
|
||||
self.cbar_axes[i].set_visible(True)
|
||||
elif cb_mode == "edge":
|
||||
if cb_location in ("right", "left"):
|
||||
count = self._nrows
|
||||
else:
|
||||
count = self._ncols
|
||||
for i in range(count):
|
||||
self.cbar_axes[i].set_visible(True)
|
||||
for j in range(i + 1, self.ngrids):
|
||||
self.cbar_axes[j].set_visible(False)
|
||||
else:
|
||||
for i in range(self.ngrids):
|
||||
self.cbar_axes[i].set_visible(False)
|
||||
self.cbar_axes[i].set_position([1., 1., 0.001, 0.001],
|
||||
which="active")
|
||||
|
||||
self._divider.set_horizontal(h)
|
||||
self._divider.set_vertical(v)
|
||||
|
||||
|
||||
AxesGrid = ImageGrid
|
171
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_rgb.py
Normal file
171
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_rgb.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
import numpy as np
|
||||
|
||||
from matplotlib import cbook
|
||||
from .axes_divider import make_axes_locatable, Size
|
||||
from .mpl_axes import Axes
|
||||
|
||||
|
||||
@cbook._delete_parameter("3.3", "add_all")
|
||||
def make_rgb_axes(ax, pad=0.01, axes_class=None, add_all=True, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
pad : float
|
||||
Fraction of the axes height.
|
||||
"""
|
||||
|
||||
divider = make_axes_locatable(ax)
|
||||
|
||||
pad_size = pad * Size.AxesY(ax)
|
||||
|
||||
xsize = ((1-2*pad)/3) * Size.AxesX(ax)
|
||||
ysize = ((1-2*pad)/3) * Size.AxesY(ax)
|
||||
|
||||
divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
|
||||
divider.set_vertical([ysize, pad_size, ysize, pad_size, ysize])
|
||||
|
||||
ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
|
||||
|
||||
ax_rgb = []
|
||||
if axes_class is None:
|
||||
try:
|
||||
axes_class = ax._axes_class
|
||||
except AttributeError:
|
||||
axes_class = type(ax)
|
||||
|
||||
for ny in [4, 2, 0]:
|
||||
ax1 = axes_class(ax.get_figure(), ax.get_position(original=True),
|
||||
sharex=ax, sharey=ax, **kwargs)
|
||||
locator = divider.new_locator(nx=2, ny=ny)
|
||||
ax1.set_axes_locator(locator)
|
||||
for t in ax1.yaxis.get_ticklabels() + ax1.xaxis.get_ticklabels():
|
||||
t.set_visible(False)
|
||||
try:
|
||||
for axis in ax1.axis.values():
|
||||
axis.major_ticklabels.set_visible(False)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
ax_rgb.append(ax1)
|
||||
|
||||
if add_all:
|
||||
fig = ax.get_figure()
|
||||
for ax1 in ax_rgb:
|
||||
fig.add_axes(ax1)
|
||||
|
||||
return ax_rgb
|
||||
|
||||
|
||||
@cbook.deprecated("3.3", alternative="ax.imshow(np.dstack([r, g, b]))")
|
||||
def imshow_rgb(ax, r, g, b, **kwargs):
|
||||
return ax.imshow(np.dstack([r, g, b]), **kwargs)
|
||||
|
||||
|
||||
class RGBAxes:
|
||||
"""
|
||||
4-panel imshow (RGB, R, G, B).
|
||||
|
||||
Layout:
|
||||
+---------------+-----+
|
||||
| | R |
|
||||
+ +-----+
|
||||
| RGB | G |
|
||||
+ +-----+
|
||||
| | B |
|
||||
+---------------+-----+
|
||||
|
||||
Subclasses can override the ``_defaultAxesClass`` attribute.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
RGB : ``_defaultAxesClass``
|
||||
The axes object for the three-channel imshow.
|
||||
R : ``_defaultAxesClass``
|
||||
The axes object for the red channel imshow.
|
||||
G : ``_defaultAxesClass``
|
||||
The axes object for the green channel imshow.
|
||||
B : ``_defaultAxesClass``
|
||||
The axes object for the blue channel imshow.
|
||||
"""
|
||||
|
||||
_defaultAxesClass = Axes
|
||||
|
||||
@cbook._delete_parameter("3.3", "add_all")
|
||||
def __init__(self, *args, pad=0, add_all=True, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
pad : float, default: 0
|
||||
fraction of the axes height to put as padding.
|
||||
add_all : bool, default: True
|
||||
Whether to add the {rgb, r, g, b} axes to the figure.
|
||||
This parameter is deprecated.
|
||||
axes_class : matplotlib.axes.Axes
|
||||
|
||||
*args
|
||||
Unpacked into axes_class() init for RGB
|
||||
**kwargs
|
||||
Unpacked into axes_class() init for RGB, R, G, B axes
|
||||
"""
|
||||
axes_class = kwargs.pop("axes_class", self._defaultAxesClass)
|
||||
self.RGB = ax = axes_class(*args, **kwargs)
|
||||
if add_all:
|
||||
ax.get_figure().add_axes(ax)
|
||||
else:
|
||||
kwargs["add_all"] = add_all # only show deprecation in that case
|
||||
self.R, self.G, self.B = make_rgb_axes(
|
||||
ax, pad=pad, axes_class=axes_class, **kwargs)
|
||||
# Set the line color and ticks for the axes.
|
||||
for ax1 in [self.RGB, self.R, self.G, self.B]:
|
||||
ax1.axis[:].line.set_color("w")
|
||||
ax1.axis[:].major_ticks.set_markeredgecolor("w")
|
||||
|
||||
@cbook.deprecated("3.3")
|
||||
def add_RGB_to_figure(self):
|
||||
"""Add red, green and blue axes to the RGB composite's axes figure."""
|
||||
self.RGB.get_figure().add_axes(self.R)
|
||||
self.RGB.get_figure().add_axes(self.G)
|
||||
self.RGB.get_figure().add_axes(self.B)
|
||||
|
||||
def imshow_rgb(self, r, g, b, **kwargs):
|
||||
"""
|
||||
Create the four images {rgb, r, g, b}.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
r : array-like
|
||||
The red array
|
||||
g : array-like
|
||||
The green array
|
||||
b : array-like
|
||||
The blue array
|
||||
kwargs : imshow kwargs
|
||||
kwargs get unpacked into the imshow calls for the four images
|
||||
|
||||
Returns
|
||||
-------
|
||||
rgb : matplotlib.image.AxesImage
|
||||
r : matplotlib.image.AxesImage
|
||||
g : matplotlib.image.AxesImage
|
||||
b : matplotlib.image.AxesImage
|
||||
"""
|
||||
if not (r.shape == g.shape == b.shape):
|
||||
raise ValueError(
|
||||
f'Input shapes ({r.shape}, {g.shape}, {b.shape}) do not match')
|
||||
RGB = np.dstack([r, g, b])
|
||||
R = np.zeros_like(RGB)
|
||||
R[:, :, 0] = r
|
||||
G = np.zeros_like(RGB)
|
||||
G[:, :, 1] = g
|
||||
B = np.zeros_like(RGB)
|
||||
B[:, :, 2] = b
|
||||
im_rgb = self.RGB.imshow(RGB, **kwargs)
|
||||
im_r = self.R.imshow(R, **kwargs)
|
||||
im_g = self.G.imshow(G, **kwargs)
|
||||
im_b = self.B.imshow(B, **kwargs)
|
||||
return im_rgb, im_r, im_g, im_b
|
||||
|
||||
|
||||
@cbook.deprecated("3.3", alternative="RGBAxes")
|
||||
class RGBAxesBase(RGBAxes):
|
||||
pass
|
272
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_size.py
Normal file
272
venv/Lib/site-packages/mpl_toolkits/axes_grid1/axes_size.py
Normal file
|
@ -0,0 +1,272 @@
|
|||
"""
|
||||
Provides classes of simple units that will be used with AxesDivider
|
||||
class (or others) to determine the size of each axes. The unit
|
||||
classes define `get_size` method that returns a tuple of two floats,
|
||||
meaning relative and absolute sizes, respectively.
|
||||
|
||||
Note that this class is nothing more than a simple tuple of two
|
||||
floats. Take a look at the Divider class to see how these two
|
||||
values are used.
|
||||
"""
|
||||
|
||||
from numbers import Number
|
||||
|
||||
from matplotlib import cbook
|
||||
from matplotlib.axes import Axes
|
||||
|
||||
|
||||
class _Base:
|
||||
|
||||
def __rmul__(self, other):
|
||||
return Fraction(other, self)
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, _Base):
|
||||
return Add(self, other)
|
||||
else:
|
||||
return Add(self, Fixed(other))
|
||||
|
||||
|
||||
class Add(_Base):
|
||||
def __init__(self, a, b):
|
||||
self._a = a
|
||||
self._b = b
|
||||
|
||||
def get_size(self, renderer):
|
||||
a_rel_size, a_abs_size = self._a.get_size(renderer)
|
||||
b_rel_size, b_abs_size = self._b.get_size(renderer)
|
||||
return a_rel_size + b_rel_size, a_abs_size + b_abs_size
|
||||
|
||||
|
||||
class AddList(_Base):
|
||||
def __init__(self, add_list):
|
||||
self._list = add_list
|
||||
|
||||
def get_size(self, renderer):
|
||||
sum_rel_size = sum([a.get_size(renderer)[0] for a in self._list])
|
||||
sum_abs_size = sum([a.get_size(renderer)[1] for a in self._list])
|
||||
return sum_rel_size, sum_abs_size
|
||||
|
||||
|
||||
class Fixed(_Base):
|
||||
"""
|
||||
Simple fixed size with absolute part = *fixed_size* and relative part = 0.
|
||||
"""
|
||||
|
||||
def __init__(self, fixed_size):
|
||||
cbook._check_isinstance(Number, fixed_size=fixed_size)
|
||||
self.fixed_size = fixed_size
|
||||
|
||||
def get_size(self, renderer):
|
||||
rel_size = 0.
|
||||
abs_size = self.fixed_size
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class Scaled(_Base):
|
||||
"""
|
||||
Simple scaled(?) size with absolute part = 0 and
|
||||
relative part = *scalable_size*.
|
||||
"""
|
||||
|
||||
def __init__(self, scalable_size):
|
||||
self._scalable_size = scalable_size
|
||||
|
||||
def get_size(self, renderer):
|
||||
rel_size = self._scalable_size
|
||||
abs_size = 0.
|
||||
return rel_size, abs_size
|
||||
|
||||
Scalable = Scaled
|
||||
|
||||
|
||||
def _get_axes_aspect(ax):
|
||||
aspect = ax.get_aspect()
|
||||
if aspect == "auto":
|
||||
aspect = 1.
|
||||
return aspect
|
||||
|
||||
|
||||
class AxesX(_Base):
|
||||
"""
|
||||
Scaled size whose relative part corresponds to the data width
|
||||
of the *axes* multiplied by the *aspect*.
|
||||
"""
|
||||
|
||||
def __init__(self, axes, aspect=1., ref_ax=None):
|
||||
self._axes = axes
|
||||
self._aspect = aspect
|
||||
if aspect == "axes" and ref_ax is None:
|
||||
raise ValueError("ref_ax must be set when aspect='axes'")
|
||||
self._ref_ax = ref_ax
|
||||
|
||||
def get_size(self, renderer):
|
||||
l1, l2 = self._axes.get_xlim()
|
||||
if self._aspect == "axes":
|
||||
ref_aspect = _get_axes_aspect(self._ref_ax)
|
||||
aspect = ref_aspect / _get_axes_aspect(self._axes)
|
||||
else:
|
||||
aspect = self._aspect
|
||||
|
||||
rel_size = abs(l2-l1)*aspect
|
||||
abs_size = 0.
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class AxesY(_Base):
|
||||
"""
|
||||
Scaled size whose relative part corresponds to the data height
|
||||
of the *axes* multiplied by the *aspect*.
|
||||
"""
|
||||
|
||||
def __init__(self, axes, aspect=1., ref_ax=None):
|
||||
self._axes = axes
|
||||
self._aspect = aspect
|
||||
if aspect == "axes" and ref_ax is None:
|
||||
raise ValueError("ref_ax must be set when aspect='axes'")
|
||||
self._ref_ax = ref_ax
|
||||
|
||||
def get_size(self, renderer):
|
||||
l1, l2 = self._axes.get_ylim()
|
||||
|
||||
if self._aspect == "axes":
|
||||
ref_aspect = _get_axes_aspect(self._ref_ax)
|
||||
aspect = _get_axes_aspect(self._axes)
|
||||
else:
|
||||
aspect = self._aspect
|
||||
|
||||
rel_size = abs(l2-l1)*aspect
|
||||
abs_size = 0.
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class MaxExtent(_Base):
|
||||
"""
|
||||
Size whose absolute part is either the largest width or the largest height
|
||||
of the given *artist_list*.
|
||||
"""
|
||||
|
||||
def __init__(self, artist_list, w_or_h):
|
||||
self._artist_list = artist_list
|
||||
cbook._check_in_list(["width", "height"], w_or_h=w_or_h)
|
||||
self._w_or_h = w_or_h
|
||||
|
||||
def add_artist(self, a):
|
||||
self._artist_list.append(a)
|
||||
|
||||
def get_size(self, renderer):
|
||||
rel_size = 0.
|
||||
extent_list = [
|
||||
getattr(a.get_window_extent(renderer), self._w_or_h) / a.figure.dpi
|
||||
for a in self._artist_list]
|
||||
abs_size = max(extent_list, default=0)
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class MaxWidth(MaxExtent):
|
||||
"""
|
||||
Size whose absolute part is the largest width of the given *artist_list*.
|
||||
"""
|
||||
|
||||
def __init__(self, artist_list):
|
||||
super().__init__(artist_list, "width")
|
||||
|
||||
|
||||
class MaxHeight(MaxExtent):
|
||||
"""
|
||||
Size whose absolute part is the largest height of the given *artist_list*.
|
||||
"""
|
||||
|
||||
def __init__(self, artist_list):
|
||||
super().__init__(artist_list, "height")
|
||||
|
||||
|
||||
class Fraction(_Base):
|
||||
"""
|
||||
An instance whose size is a *fraction* of the *ref_size*.
|
||||
|
||||
>>> s = Fraction(0.3, AxesX(ax))
|
||||
"""
|
||||
|
||||
def __init__(self, fraction, ref_size):
|
||||
cbook._check_isinstance(Number, fraction=fraction)
|
||||
self._fraction_ref = ref_size
|
||||
self._fraction = fraction
|
||||
|
||||
def get_size(self, renderer):
|
||||
if self._fraction_ref is None:
|
||||
return self._fraction, 0.
|
||||
else:
|
||||
r, a = self._fraction_ref.get_size(renderer)
|
||||
rel_size = r*self._fraction
|
||||
abs_size = a*self._fraction
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class Padded(_Base):
|
||||
"""
|
||||
Return a instance where the absolute part of *size* is
|
||||
increase by the amount of *pad*.
|
||||
"""
|
||||
|
||||
def __init__(self, size, pad):
|
||||
self._size = size
|
||||
self._pad = pad
|
||||
|
||||
def get_size(self, renderer):
|
||||
r, a = self._size.get_size(renderer)
|
||||
rel_size = r
|
||||
abs_size = a + self._pad
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
def from_any(size, fraction_ref=None):
|
||||
"""
|
||||
Create a Fixed unit when the first argument is a float, or a
|
||||
Fraction unit if that is a string that ends with %. The second
|
||||
argument is only meaningful when Fraction unit is created.
|
||||
|
||||
>>> a = Size.from_any(1.2) # => Size.Fixed(1.2)
|
||||
>>> Size.from_any("50%", a) # => Size.Fraction(0.5, a)
|
||||
"""
|
||||
if isinstance(size, Number):
|
||||
return Fixed(size)
|
||||
elif isinstance(size, str):
|
||||
if size[-1] == "%":
|
||||
return Fraction(float(size[:-1]) / 100, fraction_ref)
|
||||
raise ValueError("Unknown format")
|
||||
|
||||
|
||||
class SizeFromFunc(_Base):
|
||||
def __init__(self, func):
|
||||
self._func = func
|
||||
|
||||
def get_size(self, renderer):
|
||||
rel_size = 0.
|
||||
|
||||
bb = self._func(renderer)
|
||||
dpi = renderer.points_to_pixels(72.)
|
||||
abs_size = bb/dpi
|
||||
|
||||
return rel_size, abs_size
|
||||
|
||||
|
||||
class GetExtentHelper:
|
||||
_get_func_map = {
|
||||
"left": lambda self, axes_bbox: axes_bbox.xmin - self.xmin,
|
||||
"right": lambda self, axes_bbox: self.xmax - axes_bbox.xmax,
|
||||
"bottom": lambda self, axes_bbox: axes_bbox.ymin - self.ymin,
|
||||
"top": lambda self, axes_bbox: self.ymax - axes_bbox.ymax,
|
||||
}
|
||||
|
||||
def __init__(self, ax, direction):
|
||||
cbook._check_in_list(self._get_func_map, direction=direction)
|
||||
self._ax_list = [ax] if isinstance(ax, Axes) else ax
|
||||
self._direction = direction
|
||||
|
||||
def __call__(self, renderer):
|
||||
get_func = self._get_func_map[self._direction]
|
||||
vl = [get_func(ax.get_tightbbox(renderer, call_axes_locator=False),
|
||||
ax.bbox)
|
||||
for ax in self._ax_list]
|
||||
return max(vl)
|
804
venv/Lib/site-packages/mpl_toolkits/axes_grid1/colorbar.py
Normal file
804
venv/Lib/site-packages/mpl_toolkits/axes_grid1/colorbar.py
Normal file
|
@ -0,0 +1,804 @@
|
|||
"""
|
||||
Colorbar toolkit with two classes and a function:
|
||||
|
||||
:class:`ColorbarBase`
|
||||
the base class with full colorbar drawing functionality.
|
||||
It can be used as-is to make a colorbar for a given colormap;
|
||||
a mappable object (e.g., image) is not needed.
|
||||
|
||||
:class:`Colorbar`
|
||||
the derived class for use with images or contour plots.
|
||||
|
||||
:func:`make_axes`
|
||||
a function for resizing an axes and adding a second axes
|
||||
suitable for a colorbar
|
||||
|
||||
The :meth:`~matplotlib.figure.Figure.colorbar` method uses :func:`make_axes`
|
||||
and :class:`Colorbar`; the :func:`~matplotlib.pyplot.colorbar` function
|
||||
is a thin wrapper over :meth:`~matplotlib.figure.Figure.colorbar`.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib as mpl
|
||||
from matplotlib import cbook
|
||||
import matplotlib.colors as colors
|
||||
import matplotlib.cm as cm
|
||||
from matplotlib import docstring
|
||||
import matplotlib.ticker as ticker
|
||||
import matplotlib.collections as collections
|
||||
import matplotlib.contour as contour
|
||||
from matplotlib.path import Path
|
||||
from matplotlib.patches import PathPatch
|
||||
from matplotlib.transforms import Bbox
|
||||
|
||||
|
||||
cbook.warn_deprecated(
|
||||
"3.2", name=__name__, obj_type="module", alternative="matplotlib.colorbar")
|
||||
|
||||
|
||||
make_axes_kw_doc = '''
|
||||
|
||||
============= ====================================================
|
||||
Property Description
|
||||
============= ====================================================
|
||||
*orientation* vertical or horizontal
|
||||
*fraction* 0.15; fraction of original axes to use for colorbar
|
||||
*pad* 0.05 if vertical, 0.15 if horizontal; fraction
|
||||
of original axes between colorbar and new image axes
|
||||
*shrink* 1.0; fraction by which to shrink the colorbar
|
||||
*aspect* 20; ratio of long to short dimensions
|
||||
============= ====================================================
|
||||
|
||||
'''
|
||||
|
||||
colormap_kw_doc = '''
|
||||
|
||||
=========== ====================================================
|
||||
Property Description
|
||||
=========== ====================================================
|
||||
*extend* [ 'neither' | 'both' | 'min' | 'max' ]
|
||||
If not 'neither', make pointed end(s) for out-of-
|
||||
range values. These are set for a given colormap
|
||||
using the colormap set_under and set_over methods.
|
||||
*spacing* [ 'uniform' | 'proportional' ]
|
||||
Uniform spacing gives each discrete color the same
|
||||
space; proportional makes the space proportional to
|
||||
the data interval.
|
||||
*ticks* [ None | list of ticks | Locator object ]
|
||||
If None, ticks are determined automatically from the
|
||||
input.
|
||||
*format* [ None | format string | Formatter object ]
|
||||
If None, the
|
||||
:class:`~matplotlib.ticker.ScalarFormatter` is used.
|
||||
If a format string is given, e.g., '%.3f', that is
|
||||
used. An alternative
|
||||
:class:`~matplotlib.ticker.Formatter` object may be
|
||||
given instead.
|
||||
*drawedges* bool
|
||||
Whether to draw lines at color boundaries.
|
||||
=========== ====================================================
|
||||
|
||||
The following will probably be useful only in the context of
|
||||
indexed colors (that is, when the mappable has norm=NoNorm()),
|
||||
or other unusual circumstances.
|
||||
|
||||
============ ===================================================
|
||||
Property Description
|
||||
============ ===================================================
|
||||
*boundaries* None or a sequence
|
||||
*values* None or a sequence which must be of length 1 less
|
||||
than the sequence of *boundaries*. For each region
|
||||
delimited by adjacent entries in *boundaries*, the
|
||||
color mapped to the corresponding value in values
|
||||
will be used.
|
||||
============ ===================================================
|
||||
|
||||
'''
|
||||
|
||||
colorbar_doc = '''
|
||||
|
||||
Add a colorbar to a plot.
|
||||
|
||||
Function signatures for the :mod:`~matplotlib.pyplot` interface; all
|
||||
but the first are also method signatures for the
|
||||
:meth:`~matplotlib.figure.Figure.colorbar` method::
|
||||
|
||||
colorbar(**kwargs)
|
||||
colorbar(mappable, **kwargs)
|
||||
colorbar(mappable, cax=cax, **kwargs)
|
||||
colorbar(mappable, ax=ax, **kwargs)
|
||||
|
||||
arguments:
|
||||
|
||||
*mappable*
|
||||
the :class:`~matplotlib.image.Image`,
|
||||
:class:`~matplotlib.contour.ContourSet`, etc. to
|
||||
which the colorbar applies; this argument is mandatory for the
|
||||
:meth:`~matplotlib.figure.Figure.colorbar` method but optional for the
|
||||
:func:`~matplotlib.pyplot.colorbar` function, which sets the
|
||||
default to the current image.
|
||||
|
||||
keyword arguments:
|
||||
|
||||
*cax*
|
||||
None | axes object into which the colorbar will be drawn
|
||||
*ax*
|
||||
None | parent axes object from which space for a new
|
||||
colorbar axes will be stolen
|
||||
|
||||
|
||||
Additional keyword arguments are of two kinds:
|
||||
|
||||
axes properties:
|
||||
%s
|
||||
colorbar properties:
|
||||
%s
|
||||
|
||||
If *mappable* is a :class:`~matplotlib.contours.ContourSet`, its *extend*
|
||||
kwarg is included automatically.
|
||||
|
||||
Note that the *shrink* kwarg provides a simple way to keep a vertical
|
||||
colorbar, for example, from being taller than the axes of the mappable
|
||||
to which the colorbar is attached; but it is a manual method requiring
|
||||
some trial and error. If the colorbar is too tall (or a horizontal
|
||||
colorbar is too wide) use a smaller value of *shrink*.
|
||||
|
||||
For more precise control, you can manually specify the positions of
|
||||
the axes objects in which the mappable and the colorbar are drawn. In
|
||||
this case, do not use any of the axes properties kwargs.
|
||||
|
||||
It is known that some vector graphics viewer (svg and pdf) renders white gaps
|
||||
between segments of the colorbar. This is due to bugs in the viewers not
|
||||
matplotlib. As a workaround the colorbar can be rendered with overlapping
|
||||
segments::
|
||||
|
||||
cbar = colorbar()
|
||||
cbar.solids.set_edgecolor("face")
|
||||
draw()
|
||||
|
||||
However this has negative consequences in other circumstances. Particularly
|
||||
with semi transparent images (alpha < 1) and colorbar extensions and is not
|
||||
enabled by default see (issue #1188).
|
||||
|
||||
returns:
|
||||
:class:`~matplotlib.colorbar.Colorbar` instance; see also its base class,
|
||||
:class:`~matplotlib.colorbar.ColorbarBase`. Call the
|
||||
:meth:`~matplotlib.colorbar.ColorbarBase.set_label` method
|
||||
to label the colorbar.
|
||||
|
||||
|
||||
The transData of the *cax* is adjusted so that the limits in the
|
||||
longest axis actually corresponds to the limits in colorbar range. On
|
||||
the other hand, the shortest axis has a data limits of [1,2], whose
|
||||
unconventional value is to prevent underflow when log scale is used.
|
||||
''' % (make_axes_kw_doc, colormap_kw_doc)
|
||||
|
||||
#docstring.interpd.update(colorbar_doc=colorbar_doc)
|
||||
|
||||
|
||||
class CbarAxesLocator:
|
||||
"""
|
||||
CbarAxesLocator is a axes_locator for colorbar axes. It adjust the
|
||||
position of the axes to make a room for extended ends, i.e., the
|
||||
extended ends are located outside the axes area.
|
||||
"""
|
||||
|
||||
def __init__(self, locator=None, extend="neither", orientation="vertical"):
|
||||
"""
|
||||
*locator* : the bbox returned from the locator is used as a
|
||||
initial axes location. If None, axes.bbox is used.
|
||||
|
||||
*extend* : same as in ColorbarBase
|
||||
*orientation* : same as in ColorbarBase
|
||||
|
||||
"""
|
||||
self._locator = locator
|
||||
self.extesion_fraction = 0.05
|
||||
self.extend = extend
|
||||
self.orientation = orientation
|
||||
|
||||
def get_original_position(self, axes, renderer):
|
||||
"""Return the original position of the axes."""
|
||||
if self._locator is None:
|
||||
bbox = axes.get_position(original=True)
|
||||
else:
|
||||
bbox = self._locator(axes, renderer)
|
||||
return bbox
|
||||
|
||||
def get_end_vertices(self):
|
||||
"""
|
||||
Return a tuple of two vertices for the colorbar extended ends.
|
||||
|
||||
The first vertices is for the minimum end, and the second is for
|
||||
the maximum end.
|
||||
"""
|
||||
# Note that concatenating two vertices needs to make a
|
||||
# vertices for the frame.
|
||||
extesion_fraction = self.extesion_fraction
|
||||
|
||||
corx = extesion_fraction*2.
|
||||
cory = 1./(1. - corx)
|
||||
x1, y1, w, h = 0, 0, 1, 1
|
||||
x2, y2 = x1 + w, y1 + h
|
||||
dw, dh = w*extesion_fraction, h*extesion_fraction*cory
|
||||
|
||||
if self.extend in ["min", "both"]:
|
||||
bottom = [(x1, y1),
|
||||
(x1+w/2., y1-dh),
|
||||
(x2, y1)]
|
||||
else:
|
||||
bottom = [(x1, y1),
|
||||
(x2, y1)]
|
||||
|
||||
if self.extend in ["max", "both"]:
|
||||
top = [(x2, y2),
|
||||
(x1+w/2., y2+dh),
|
||||
(x1, y2)]
|
||||
else:
|
||||
top = [(x2, y2),
|
||||
(x1, y2)]
|
||||
|
||||
if self.orientation == "horizontal":
|
||||
bottom = [(y, x) for (x, y) in bottom]
|
||||
top = [(y, x) for (x, y) in top]
|
||||
|
||||
return bottom, top
|
||||
|
||||
def get_path_patch(self):
|
||||
"""Return the path for axes patch."""
|
||||
end1, end2 = self.get_end_vertices()
|
||||
verts = [] + end1 + end2 + end1[:1]
|
||||
return Path(verts)
|
||||
|
||||
def get_path_ends(self):
|
||||
"""Return the paths for extended ends."""
|
||||
end1, end2 = self.get_end_vertices()
|
||||
return Path(end1), Path(end2)
|
||||
|
||||
def __call__(self, axes, renderer):
|
||||
"""Return the adjusted position of the axes."""
|
||||
bbox0 = self.get_original_position(axes, renderer)
|
||||
bbox = bbox0
|
||||
|
||||
x1, y1, w, h = bbox.bounds
|
||||
extesion_fraction = self.extesion_fraction
|
||||
dw, dh = w*extesion_fraction, h*extesion_fraction
|
||||
|
||||
if self.extend in ["min", "both"]:
|
||||
if self.orientation == "horizontal":
|
||||
x1 = x1 + dw
|
||||
else:
|
||||
y1 = y1+dh
|
||||
|
||||
if self.extend in ["max", "both"]:
|
||||
if self.orientation == "horizontal":
|
||||
w = w-2*dw
|
||||
else:
|
||||
h = h-2*dh
|
||||
|
||||
return Bbox.from_bounds(x1, y1, w, h)
|
||||
|
||||
|
||||
class ColorbarBase(cm.ScalarMappable):
|
||||
"""
|
||||
Draw a colorbar in an existing axes.
|
||||
|
||||
This is a base class for the :class:`Colorbar` class, which is the
|
||||
basis for the :func:`~matplotlib.pyplot.colorbar` method and pyplot
|
||||
function.
|
||||
|
||||
It is also useful by itself for showing a colormap. If the *cmap*
|
||||
kwarg is given but *boundaries* and *values* are left as None,
|
||||
then the colormap will be displayed on a 0-1 scale. To show the
|
||||
under- and over-value colors, specify the *norm* as::
|
||||
|
||||
colors.Normalize(clip=False)
|
||||
|
||||
To show the colors versus index instead of on the 0-1 scale,
|
||||
use::
|
||||
|
||||
norm=colors.NoNorm.
|
||||
|
||||
Useful attributes:
|
||||
|
||||
:attr:`ax`
|
||||
the Axes instance in which the colorbar is drawn
|
||||
|
||||
:attr:`lines`
|
||||
a LineCollection if lines were drawn, otherwise None
|
||||
|
||||
:attr:`dividers`
|
||||
a LineCollection if *drawedges* is True, otherwise None
|
||||
|
||||
Useful public methods are :meth:`set_label` and :meth:`add_lines`.
|
||||
"""
|
||||
|
||||
def __init__(self, ax,
|
||||
cmap=None,
|
||||
norm=None,
|
||||
alpha=1.0,
|
||||
values=None,
|
||||
boundaries=None,
|
||||
orientation='vertical',
|
||||
extend='neither',
|
||||
spacing='uniform', # uniform or proportional
|
||||
ticks=None,
|
||||
format=None,
|
||||
drawedges=False,
|
||||
filled=True,
|
||||
):
|
||||
self.ax = ax
|
||||
|
||||
if cmap is None:
|
||||
cmap = cm.get_cmap()
|
||||
if norm is None:
|
||||
norm = colors.Normalize()
|
||||
self.alpha = alpha
|
||||
cm.ScalarMappable.__init__(self, cmap=cmap, norm=norm)
|
||||
self.values = values
|
||||
self.boundaries = boundaries
|
||||
self.extend = extend
|
||||
self.spacing = spacing
|
||||
self.orientation = orientation
|
||||
self.drawedges = drawedges
|
||||
self.filled = filled
|
||||
|
||||
# artists
|
||||
self.solids = None
|
||||
self.lines = None
|
||||
self.dividers = None
|
||||
self.extension_patch1 = None
|
||||
self.extension_patch2 = None
|
||||
|
||||
if orientation == "vertical":
|
||||
self.cbar_axis = self.ax.yaxis
|
||||
else:
|
||||
self.cbar_axis = self.ax.xaxis
|
||||
|
||||
if format is None:
|
||||
if isinstance(self.norm, colors.LogNorm):
|
||||
# change both axis for proper aspect
|
||||
self.ax.set_xscale("log")
|
||||
self.ax.set_yscale("log")
|
||||
self.cbar_axis.set_minor_locator(ticker.NullLocator())
|
||||
formatter = ticker.LogFormatter()
|
||||
else:
|
||||
formatter = None
|
||||
elif isinstance(format, str):
|
||||
formatter = ticker.FormatStrFormatter(format)
|
||||
else:
|
||||
formatter = format # Assume it is a Formatter
|
||||
|
||||
if formatter is None:
|
||||
formatter = self.cbar_axis.get_major_formatter()
|
||||
else:
|
||||
self.cbar_axis.set_major_formatter(formatter)
|
||||
|
||||
if np.iterable(ticks):
|
||||
self.cbar_axis.set_ticks(ticks)
|
||||
elif ticks is not None:
|
||||
self.cbar_axis.set_major_locator(ticks)
|
||||
else:
|
||||
self._select_locator()
|
||||
|
||||
self._config_axes()
|
||||
|
||||
self.update_artists()
|
||||
|
||||
self.set_label_text('')
|
||||
|
||||
def _get_colorbar_limits(self):
|
||||
"""
|
||||
initial limits for colorbar range. The returned min, max values
|
||||
will be used to create colorbar solid(?) and etc.
|
||||
"""
|
||||
if self.boundaries is not None:
|
||||
C = self.boundaries
|
||||
if self.extend in ["min", "both"]:
|
||||
C = C[1:]
|
||||
|
||||
if self.extend in ["max", "both"]:
|
||||
C = C[:-1]
|
||||
return min(C), max(C)
|
||||
else:
|
||||
return self.get_clim()
|
||||
|
||||
def _config_axes(self):
|
||||
"""
|
||||
Adjust the properties of the axes to be adequate for colorbar display.
|
||||
"""
|
||||
ax = self.ax
|
||||
|
||||
axes_locator = CbarAxesLocator(ax.get_axes_locator(),
|
||||
extend=self.extend,
|
||||
orientation=self.orientation)
|
||||
ax.set_axes_locator(axes_locator)
|
||||
|
||||
# override the get_data_ratio for the aspect works.
|
||||
def _f():
|
||||
return 1.
|
||||
ax.get_data_ratio = _f
|
||||
ax.get_data_ratio_log = _f
|
||||
|
||||
ax.set_frame_on(True)
|
||||
ax.set_navigate(False)
|
||||
|
||||
self.ax.set_autoscalex_on(False)
|
||||
self.ax.set_autoscaley_on(False)
|
||||
|
||||
if self.orientation == 'horizontal':
|
||||
ax.xaxis.set_label_position('bottom')
|
||||
ax.set_yticks([])
|
||||
else:
|
||||
ax.set_xticks([])
|
||||
ax.yaxis.set_label_position('right')
|
||||
ax.yaxis.set_ticks_position('right')
|
||||
|
||||
def update_artists(self):
|
||||
"""
|
||||
Update the colorbar associated artists, *filled* and
|
||||
*ends*. Note that *lines* are not updated. This needs to be
|
||||
called whenever clim of associated image changes.
|
||||
"""
|
||||
self._process_values()
|
||||
self._add_ends()
|
||||
|
||||
X, Y = self._mesh()
|
||||
if self.filled:
|
||||
C = self._values[:, np.newaxis]
|
||||
self._add_solids(X, Y, C)
|
||||
|
||||
ax = self.ax
|
||||
vmin, vmax = self._get_colorbar_limits()
|
||||
if self.orientation == 'horizontal':
|
||||
ax.set_ylim(1, 2)
|
||||
ax.set_xlim(vmin, vmax)
|
||||
else:
|
||||
ax.set_xlim(1, 2)
|
||||
ax.set_ylim(vmin, vmax)
|
||||
|
||||
def _add_ends(self):
|
||||
"""
|
||||
Create patches from extended ends and add them to the axes.
|
||||
"""
|
||||
|
||||
del self.extension_patch1
|
||||
del self.extension_patch2
|
||||
|
||||
path1, path2 = self.ax.get_axes_locator().get_path_ends()
|
||||
fc = mpl.rcParams['axes.facecolor']
|
||||
ec = mpl.rcParams['axes.edgecolor']
|
||||
linewidths = 0.5 * mpl.rcParams['axes.linewidth']
|
||||
self.extension_patch1 = PathPatch(path1,
|
||||
fc=fc, ec=ec, lw=linewidths,
|
||||
zorder=2.,
|
||||
transform=self.ax.transAxes,
|
||||
clip_on=False)
|
||||
self.extension_patch2 = PathPatch(path2,
|
||||
fc=fc, ec=ec, lw=linewidths,
|
||||
zorder=2.,
|
||||
transform=self.ax.transAxes,
|
||||
clip_on=False)
|
||||
self.ax.add_artist(self.extension_patch1)
|
||||
self.ax.add_artist(self.extension_patch2)
|
||||
|
||||
def _set_label_text(self):
|
||||
"""Set the colorbar label."""
|
||||
self.cbar_axis.set_label_text(self._label, **self._labelkw)
|
||||
|
||||
def set_label_text(self, label, **kw):
|
||||
"""Label the long axis of the colorbar."""
|
||||
self._label = label
|
||||
self._labelkw = kw
|
||||
self._set_label_text()
|
||||
|
||||
def _edges(self, X, Y):
|
||||
"""Return the separator line segments; helper for _add_solids."""
|
||||
N = X.shape[0]
|
||||
# Using the non-array form of these line segments is much
|
||||
# simpler than making them into arrays.
|
||||
if self.orientation == 'vertical':
|
||||
return [list(zip(X[i], Y[i])) for i in range(1, N-1)]
|
||||
else:
|
||||
return [list(zip(Y[i], X[i])) for i in range(1, N-1)]
|
||||
|
||||
def _add_solids(self, X, Y, C):
|
||||
"""
|
||||
Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`;
|
||||
optionally add separators.
|
||||
"""
|
||||
## Change to pcolorfast after fixing bugs in some backends...
|
||||
|
||||
if self.extend in ["min", "both"]:
|
||||
cc = self.to_rgba([C[0][0]])
|
||||
self.extension_patch1.set_facecolor(cc[0])
|
||||
X, Y, C = X[1:], Y[1:], C[1:]
|
||||
|
||||
if self.extend in ["max", "both"]:
|
||||
cc = self.to_rgba([C[-1][0]])
|
||||
self.extension_patch2.set_facecolor(cc[0])
|
||||
X, Y, C = X[:-1], Y[:-1], C[:-1]
|
||||
|
||||
if self.orientation == 'vertical':
|
||||
args = (X, Y, C)
|
||||
else:
|
||||
args = (np.transpose(Y), np.transpose(X), np.transpose(C))
|
||||
|
||||
del self.solids
|
||||
del self.dividers
|
||||
|
||||
col = self.ax.pcolormesh(
|
||||
*args,
|
||||
cmap=self.cmap, norm=self.norm, shading='flat', alpha=self.alpha)
|
||||
|
||||
self.solids = col
|
||||
if self.drawedges:
|
||||
self.dividers = collections.LineCollection(
|
||||
self._edges(X, Y),
|
||||
colors=(mpl.rcParams['axes.edgecolor'],),
|
||||
linewidths=(0.5*mpl.rcParams['axes.linewidth'],),
|
||||
)
|
||||
self.ax.add_collection(self.dividers)
|
||||
else:
|
||||
self.dividers = None
|
||||
|
||||
def add_lines(self, levels, colors, linewidths):
|
||||
"""Draw lines on the colorbar. It deletes preexisting lines."""
|
||||
X, Y = np.meshgrid([1, 2], levels)
|
||||
if self.orientation == 'vertical':
|
||||
xy = np.stack([X, Y], axis=-1)
|
||||
else:
|
||||
xy = np.stack([Y, X], axis=-1)
|
||||
col = collections.LineCollection(xy, linewidths=linewidths)
|
||||
self.lines = col
|
||||
col.set_color(colors)
|
||||
self.ax.add_collection(col)
|
||||
|
||||
def _select_locator(self):
|
||||
"""Select a suitable locator."""
|
||||
if self.boundaries is None:
|
||||
if isinstance(self.norm, colors.NoNorm):
|
||||
nv = len(self._values)
|
||||
base = 1 + int(nv/10)
|
||||
locator = ticker.IndexLocator(base=base, offset=0)
|
||||
elif isinstance(self.norm, colors.BoundaryNorm):
|
||||
b = self.norm.boundaries
|
||||
locator = ticker.FixedLocator(b, nbins=10)
|
||||
elif isinstance(self.norm, colors.LogNorm):
|
||||
locator = ticker.LogLocator()
|
||||
else:
|
||||
locator = ticker.MaxNLocator(nbins=5)
|
||||
else:
|
||||
b = self._boundaries[self._inside]
|
||||
locator = ticker.FixedLocator(b)
|
||||
|
||||
self.cbar_axis.set_major_locator(locator)
|
||||
|
||||
def _process_values(self, b=None):
|
||||
"""
|
||||
Set the :attr:`_boundaries` and :attr:`_values` attributes
|
||||
based on the input boundaries and values. Input boundaries
|
||||
can be *self.boundaries* or the argument *b*.
|
||||
"""
|
||||
if b is None:
|
||||
b = self.boundaries
|
||||
if b is not None:
|
||||
self._boundaries = np.asarray(b, dtype=float)
|
||||
if self.values is None:
|
||||
self._values = (self._boundaries[:-1]
|
||||
+ self._boundaries[1:]) / 2
|
||||
if isinstance(self.norm, colors.NoNorm):
|
||||
self._values = (self._values + 0.00001).astype(np.int16)
|
||||
return
|
||||
self._values = np.array(self.values)
|
||||
return
|
||||
if self.values is not None:
|
||||
self._values = np.array(self.values)
|
||||
if self.boundaries is None:
|
||||
b = np.zeros(len(self.values) + 1)
|
||||
b[1:-1] = 0.5*(self._values[:-1] - self._values[1:])
|
||||
b[0] = 2.0*b[1] - b[2]
|
||||
b[-1] = 2.0*b[-2] - b[-3]
|
||||
self._boundaries = b
|
||||
return
|
||||
self._boundaries = np.array(self.boundaries)
|
||||
return
|
||||
# Neither boundaries nor values are specified;
|
||||
# make reasonable ones based on cmap and norm.
|
||||
if isinstance(self.norm, colors.NoNorm):
|
||||
self._boundaries = (
|
||||
self._uniform_y(self.cmap.N + 1) * self.cmap.N - 0.5)
|
||||
self._values = np.arange(self.cmap.N, dtype=np.int16)
|
||||
return
|
||||
elif isinstance(self.norm, colors.BoundaryNorm):
|
||||
self._boundaries = np.array(self.norm.boundaries)
|
||||
self._values = (self._boundaries[:-1] + self._boundaries[1:]) / 2
|
||||
return
|
||||
else:
|
||||
b = self._uniform_y(self.cmap.N + 1)
|
||||
|
||||
self._process_values(b)
|
||||
|
||||
def _uniform_y(self, N):
|
||||
"""
|
||||
Return colorbar data coordinates for *N* uniformly spaced boundaries.
|
||||
"""
|
||||
vmin, vmax = self._get_colorbar_limits()
|
||||
if isinstance(self.norm, colors.LogNorm):
|
||||
y = np.geomspace(vmin, vmax, N)
|
||||
else:
|
||||
y = np.linspace(vmin, vmax, N)
|
||||
return y
|
||||
|
||||
def _mesh(self):
|
||||
"""
|
||||
Return X,Y, the coordinate arrays for the colorbar pcolormesh.
|
||||
These are suitable for a vertical colorbar; swapping and
|
||||
transposition for a horizontal colorbar are done outside
|
||||
this function.
|
||||
"""
|
||||
x = np.array([1.0, 2.0])
|
||||
if self.spacing == 'uniform':
|
||||
y = self._uniform_y(len(self._boundaries))
|
||||
else:
|
||||
y = self._boundaries
|
||||
self._y = y
|
||||
|
||||
X, Y = np.meshgrid(x, y)
|
||||
return X, Y
|
||||
|
||||
def set_alpha(self, alpha):
|
||||
"""Set the alpha value for transparency."""
|
||||
self.alpha = alpha
|
||||
|
||||
|
||||
class Colorbar(ColorbarBase):
|
||||
def __init__(self, ax, mappable, **kw):
|
||||
# Ensure mappable.norm.vmin, vmax are set when colorbar is called, even
|
||||
# if mappable.draw has not yet been called. This will not change vmin,
|
||||
# vmax if they are already set.
|
||||
mappable.autoscale_None()
|
||||
|
||||
self.mappable = mappable
|
||||
kw['cmap'] = mappable.cmap
|
||||
kw['norm'] = mappable.norm
|
||||
kw['alpha'] = mappable.get_alpha()
|
||||
if isinstance(mappable, contour.ContourSet):
|
||||
CS = mappable
|
||||
kw['boundaries'] = CS._levels
|
||||
kw['values'] = CS.cvalues
|
||||
kw['extend'] = CS.extend
|
||||
#kw['ticks'] = CS._levels
|
||||
kw.setdefault('ticks', ticker.FixedLocator(CS.levels, nbins=10))
|
||||
kw['filled'] = CS.filled
|
||||
ColorbarBase.__init__(self, ax, **kw)
|
||||
if not CS.filled:
|
||||
self.add_lines(CS)
|
||||
else:
|
||||
ColorbarBase.__init__(self, ax, **kw)
|
||||
|
||||
def add_lines(self, CS):
|
||||
"""Add the lines from a non-filled `.ContourSet` to the colorbar."""
|
||||
if not isinstance(CS, contour.ContourSet) or CS.filled:
|
||||
raise ValueError('add_lines is only for a ContourSet of lines')
|
||||
tcolors = [c[0] for c in CS.tcolors]
|
||||
tlinewidths = [t[0] for t in CS.tlinewidths]
|
||||
# The following was an attempt to get the colorbar lines
|
||||
# to follow subsequent changes in the contour lines,
|
||||
# but more work is needed: specifically, a careful
|
||||
# look at event sequences, and at how
|
||||
# to make one object track another automatically.
|
||||
#tcolors = [col.get_colors()[0] for col in CS.collections]
|
||||
#tlinewidths = [col.get_linewidth()[0] for lw in CS.collections]
|
||||
ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths)
|
||||
|
||||
def update_normal(self, mappable):
|
||||
"""
|
||||
Update solid patches, lines, etc.
|
||||
|
||||
This is meant to be called when the norm of the image or contour plot
|
||||
to which this colorbar belongs changes.
|
||||
|
||||
If the norm on the mappable is different than before, this resets the
|
||||
locator and formatter for the axis, so if these have been customized,
|
||||
they will need to be customized again. However, if the norm only
|
||||
changes values of *vmin*, *vmax* or *cmap* then the old formatter
|
||||
and locator will be preserved.
|
||||
"""
|
||||
self.mappable = mappable
|
||||
self.set_alpha(mappable.get_alpha())
|
||||
self.cmap = mappable.cmap
|
||||
if mappable.norm != self.norm:
|
||||
self.norm = mappable.norm
|
||||
self._reset_locator_formatter_scale()
|
||||
|
||||
self.draw_all()
|
||||
if isinstance(self.mappable, contour.ContourSet):
|
||||
CS = self.mappable
|
||||
if not CS.filled:
|
||||
self.add_lines(CS)
|
||||
self.stale = True
|
||||
|
||||
def update_bruteforce(self, mappable):
|
||||
"""
|
||||
Update the colorbar artists to reflect the change of the
|
||||
associated mappable.
|
||||
"""
|
||||
self.update_artists()
|
||||
|
||||
if isinstance(mappable, contour.ContourSet):
|
||||
if not mappable.filled:
|
||||
self.add_lines(mappable)
|
||||
|
||||
|
||||
@docstring.Substitution(make_axes_kw_doc)
|
||||
def make_axes(parent, *, fraction=0.15, shrink=1.0, aspect=20, **kw):
|
||||
"""
|
||||
Resize and reposition a parent axes, and return a child
|
||||
axes suitable for a colorbar
|
||||
|
||||
::
|
||||
|
||||
cax, kw = make_axes(parent, **kw)
|
||||
|
||||
Keyword arguments may include the following (with defaults):
|
||||
|
||||
*orientation*
|
||||
'vertical' or 'horizontal'
|
||||
|
||||
%s
|
||||
|
||||
All but the first of these are stripped from the input kw set.
|
||||
|
||||
Returns (cax, kw), the child axes and the reduced kw dictionary.
|
||||
"""
|
||||
orientation = kw.setdefault('orientation', 'vertical')
|
||||
#pb = transforms.PBox(parent.get_position())
|
||||
pb = parent.get_position(original=True).frozen()
|
||||
if orientation == 'vertical':
|
||||
pad = kw.pop('pad', 0.05)
|
||||
x1 = 1.0-fraction
|
||||
pb1, pbx, pbcb = pb.splitx(x1-pad, x1)
|
||||
pbcb = pbcb.shrunk(1.0, shrink).anchored('C', pbcb)
|
||||
anchor = (0.0, 0.5)
|
||||
panchor = (1.0, 0.5)
|
||||
else:
|
||||
pad = kw.pop('pad', 0.15)
|
||||
pbcb, pbx, pb1 = pb.splity(fraction, fraction+pad)
|
||||
pbcb = pbcb.shrunk(shrink, 1.0).anchored('C', pbcb)
|
||||
aspect = 1.0/aspect
|
||||
anchor = (0.5, 1.0)
|
||||
panchor = (0.5, 0.0)
|
||||
parent.set_position(pb1)
|
||||
parent.set_anchor(panchor)
|
||||
fig = parent.get_figure()
|
||||
cax = fig.add_axes(pbcb)
|
||||
cax.set_aspect(aspect, anchor=anchor, adjustable='box')
|
||||
return cax, kw
|
||||
|
||||
|
||||
@docstring.Substitution(colorbar_doc)
|
||||
def colorbar(mappable, cax=None, ax=None, **kw):
|
||||
"""
|
||||
Create a colorbar for a ScalarMappable instance.
|
||||
|
||||
Documentation for the pyplot thin wrapper:
|
||||
|
||||
%s
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
if cax is None:
|
||||
cax, kw = make_axes(ax, **kw)
|
||||
cb = Colorbar(cax, mappable, **kw)
|
||||
|
||||
def on_changed(m):
|
||||
cb.set_cmap(m.get_cmap())
|
||||
cb.set_clim(m.get_clim())
|
||||
cb.update_bruteforce(m)
|
||||
|
||||
mappable.callbacksSM.connect('changed', on_changed)
|
||||
mappable.colorbar = cb
|
||||
ax.figure.sca(ax)
|
||||
return cb
|
653
venv/Lib/site-packages/mpl_toolkits/axes_grid1/inset_locator.py
Normal file
653
venv/Lib/site-packages/mpl_toolkits/axes_grid1/inset_locator.py
Normal file
|
@ -0,0 +1,653 @@
|
|||
"""
|
||||
A collection of functions and objects for creating or placing inset axes.
|
||||
"""
|
||||
|
||||
from matplotlib import cbook, docstring
|
||||
from matplotlib.offsetbox import AnchoredOffsetbox
|
||||
from matplotlib.patches import Patch, Rectangle
|
||||
from matplotlib.path import Path
|
||||
from matplotlib.transforms import Bbox, BboxTransformTo
|
||||
from matplotlib.transforms import IdentityTransform, TransformedBbox
|
||||
|
||||
from . import axes_size as Size
|
||||
from .parasite_axes import HostAxes
|
||||
|
||||
|
||||
class InsetPosition:
|
||||
@docstring.dedent_interpd
|
||||
def __init__(self, parent, lbwh):
|
||||
"""
|
||||
An object for positioning an inset axes.
|
||||
|
||||
This is created by specifying the normalized coordinates in the axes,
|
||||
instead of the figure.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parent : `matplotlib.axes.Axes`
|
||||
Axes to use for normalizing coordinates.
|
||||
|
||||
lbwh : iterable of four floats
|
||||
The left edge, bottom edge, width, and height of the inset axes, in
|
||||
units of the normalized coordinate of the *parent* axes.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:meth:`matplotlib.axes.Axes.set_axes_locator`
|
||||
|
||||
Examples
|
||||
--------
|
||||
The following bounds the inset axes to a box with 20%% of the parent
|
||||
axes's height and 40%% of the width. The size of the axes specified
|
||||
([0, 0, 1, 1]) ensures that the axes completely fills the bounding box:
|
||||
|
||||
>>> parent_axes = plt.gca()
|
||||
>>> ax_ins = plt.axes([0, 0, 1, 1])
|
||||
>>> ip = InsetPosition(ax, [0.5, 0.1, 0.4, 0.2])
|
||||
>>> ax_ins.set_axes_locator(ip)
|
||||
"""
|
||||
self.parent = parent
|
||||
self.lbwh = lbwh
|
||||
|
||||
def __call__(self, ax, renderer):
|
||||
bbox_parent = self.parent.get_position(original=False)
|
||||
trans = BboxTransformTo(bbox_parent)
|
||||
bbox_inset = Bbox.from_bounds(*self.lbwh)
|
||||
bb = TransformedBbox(bbox_inset, trans)
|
||||
return bb
|
||||
|
||||
|
||||
class AnchoredLocatorBase(AnchoredOffsetbox):
|
||||
def __init__(self, bbox_to_anchor, offsetbox, loc,
|
||||
borderpad=0.5, bbox_transform=None):
|
||||
super().__init__(
|
||||
loc, pad=0., child=None, borderpad=borderpad,
|
||||
bbox_to_anchor=bbox_to_anchor, bbox_transform=bbox_transform
|
||||
)
|
||||
|
||||
def draw(self, renderer):
|
||||
raise RuntimeError("No draw method should be called")
|
||||
|
||||
def __call__(self, ax, renderer):
|
||||
self.axes = ax
|
||||
|
||||
fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
|
||||
self._update_offset_func(renderer, fontsize)
|
||||
|
||||
width, height, xdescent, ydescent = self.get_extent(renderer)
|
||||
|
||||
px, py = self.get_offset(width, height, 0, 0, renderer)
|
||||
bbox_canvas = Bbox.from_bounds(px, py, width, height)
|
||||
tr = ax.figure.transFigure.inverted()
|
||||
bb = TransformedBbox(bbox_canvas, tr)
|
||||
|
||||
return bb
|
||||
|
||||
|
||||
class AnchoredSizeLocator(AnchoredLocatorBase):
|
||||
def __init__(self, bbox_to_anchor, x_size, y_size, loc,
|
||||
borderpad=0.5, bbox_transform=None):
|
||||
super().__init__(
|
||||
bbox_to_anchor, None, loc,
|
||||
borderpad=borderpad, bbox_transform=bbox_transform
|
||||
)
|
||||
|
||||
self.x_size = Size.from_any(x_size)
|
||||
self.y_size = Size.from_any(y_size)
|
||||
|
||||
def get_extent(self, renderer):
|
||||
bbox = self.get_bbox_to_anchor()
|
||||
dpi = renderer.points_to_pixels(72.)
|
||||
|
||||
r, a = self.x_size.get_size(renderer)
|
||||
width = bbox.width * r + a * dpi
|
||||
r, a = self.y_size.get_size(renderer)
|
||||
height = bbox.height * r + a * dpi
|
||||
|
||||
xd, yd = 0, 0
|
||||
|
||||
fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
|
||||
pad = self.pad * fontsize
|
||||
|
||||
return width + 2 * pad, height + 2 * pad, xd + pad, yd + pad
|
||||
|
||||
|
||||
class AnchoredZoomLocator(AnchoredLocatorBase):
|
||||
def __init__(self, parent_axes, zoom, loc,
|
||||
borderpad=0.5,
|
||||
bbox_to_anchor=None,
|
||||
bbox_transform=None):
|
||||
self.parent_axes = parent_axes
|
||||
self.zoom = zoom
|
||||
if bbox_to_anchor is None:
|
||||
bbox_to_anchor = parent_axes.bbox
|
||||
super().__init__(
|
||||
bbox_to_anchor, None, loc, borderpad=borderpad,
|
||||
bbox_transform=bbox_transform)
|
||||
|
||||
def get_extent(self, renderer):
|
||||
bb = TransformedBbox(self.axes.viewLim, self.parent_axes.transData)
|
||||
fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
|
||||
pad = self.pad * fontsize
|
||||
return (abs(bb.width * self.zoom) + 2 * pad,
|
||||
abs(bb.height * self.zoom) + 2 * pad,
|
||||
pad, pad)
|
||||
|
||||
|
||||
class BboxPatch(Patch):
|
||||
@docstring.dedent_interpd
|
||||
def __init__(self, bbox, **kwargs):
|
||||
"""
|
||||
Patch showing the shape bounded by a Bbox.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox : `matplotlib.transforms.Bbox`
|
||||
Bbox to use for the extents of this patch.
|
||||
|
||||
**kwargs
|
||||
Patch properties. Valid arguments include:
|
||||
|
||||
%(Patch)s
|
||||
"""
|
||||
if "transform" in kwargs:
|
||||
raise ValueError("transform should not be set")
|
||||
|
||||
kwargs["transform"] = IdentityTransform()
|
||||
Patch.__init__(self, **kwargs)
|
||||
self.bbox = bbox
|
||||
|
||||
def get_path(self):
|
||||
# docstring inherited
|
||||
x0, y0, x1, y1 = self.bbox.extents
|
||||
return Path([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)],
|
||||
closed=True)
|
||||
|
||||
|
||||
class BboxConnector(Patch):
|
||||
@staticmethod
|
||||
def get_bbox_edge_pos(bbox, loc):
|
||||
"""
|
||||
Helper function to obtain the location of a corner of a bbox
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox : `matplotlib.transforms.Bbox`
|
||||
|
||||
loc : {1, 2, 3, 4}
|
||||
Corner of *bbox*. Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
Returns
|
||||
-------
|
||||
x, y : float
|
||||
Coordinates of the corner specified by *loc*.
|
||||
"""
|
||||
x0, y0, x1, y1 = bbox.extents
|
||||
if loc == 1:
|
||||
return x1, y1
|
||||
elif loc == 2:
|
||||
return x0, y1
|
||||
elif loc == 3:
|
||||
return x0, y0
|
||||
elif loc == 4:
|
||||
return x1, y0
|
||||
|
||||
@staticmethod
|
||||
def connect_bbox(bbox1, bbox2, loc1, loc2=None):
|
||||
"""
|
||||
Helper function to obtain a Path from one bbox to another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox1, bbox2 : `matplotlib.transforms.Bbox`
|
||||
Bounding boxes to connect.
|
||||
|
||||
loc1 : {1, 2, 3, 4}
|
||||
Corner of *bbox1* to use. Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
loc2 : {1, 2, 3, 4}, optional
|
||||
Corner of *bbox2* to use. If None, defaults to *loc1*.
|
||||
Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
Returns
|
||||
-------
|
||||
path : `matplotlib.path.Path`
|
||||
A line segment from the *loc1* corner of *bbox1* to the *loc2*
|
||||
corner of *bbox2*.
|
||||
"""
|
||||
if isinstance(bbox1, Rectangle):
|
||||
bbox1 = TransformedBbox(Bbox.unit(), bbox1.get_transform())
|
||||
if isinstance(bbox2, Rectangle):
|
||||
bbox2 = TransformedBbox(Bbox.unit(), bbox2.get_transform())
|
||||
if loc2 is None:
|
||||
loc2 = loc1
|
||||
x1, y1 = BboxConnector.get_bbox_edge_pos(bbox1, loc1)
|
||||
x2, y2 = BboxConnector.get_bbox_edge_pos(bbox2, loc2)
|
||||
return Path([[x1, y1], [x2, y2]])
|
||||
|
||||
@docstring.dedent_interpd
|
||||
def __init__(self, bbox1, bbox2, loc1, loc2=None, **kwargs):
|
||||
"""
|
||||
Connect two bboxes with a straight line.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox1, bbox2 : `matplotlib.transforms.Bbox`
|
||||
Bounding boxes to connect.
|
||||
|
||||
loc1 : {1, 2, 3, 4}
|
||||
Corner of *bbox1* to draw the line. Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
loc2 : {1, 2, 3, 4}, optional
|
||||
Corner of *bbox2* to draw the line. If None, defaults to *loc1*.
|
||||
Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
**kwargs
|
||||
Patch properties for the line drawn. Valid arguments include:
|
||||
|
||||
%(Patch)s
|
||||
"""
|
||||
if "transform" in kwargs:
|
||||
raise ValueError("transform should not be set")
|
||||
|
||||
kwargs["transform"] = IdentityTransform()
|
||||
if 'fill' in kwargs:
|
||||
Patch.__init__(self, **kwargs)
|
||||
else:
|
||||
fill = bool({'fc', 'facecolor', 'color'}.intersection(kwargs))
|
||||
Patch.__init__(self, fill=fill, **kwargs)
|
||||
self.bbox1 = bbox1
|
||||
self.bbox2 = bbox2
|
||||
self.loc1 = loc1
|
||||
self.loc2 = loc2
|
||||
|
||||
def get_path(self):
|
||||
# docstring inherited
|
||||
return self.connect_bbox(self.bbox1, self.bbox2,
|
||||
self.loc1, self.loc2)
|
||||
|
||||
|
||||
class BboxConnectorPatch(BboxConnector):
|
||||
@docstring.dedent_interpd
|
||||
def __init__(self, bbox1, bbox2, loc1a, loc2a, loc1b, loc2b, **kwargs):
|
||||
"""
|
||||
Connect two bboxes with a quadrilateral.
|
||||
|
||||
The quadrilateral is specified by two lines that start and end at
|
||||
corners of the bboxes. The four sides of the quadrilateral are defined
|
||||
by the two lines given, the line between the two corners specified in
|
||||
*bbox1* and the line between the two corners specified in *bbox2*.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox1, bbox2 : `matplotlib.transforms.Bbox`
|
||||
Bounding boxes to connect.
|
||||
|
||||
loc1a, loc2a : {1, 2, 3, 4}
|
||||
Corners of *bbox1* and *bbox2* to draw the first line.
|
||||
Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
loc1b, loc2b : {1, 2, 3, 4}
|
||||
Corners of *bbox1* and *bbox2* to draw the second line.
|
||||
Valid values are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4
|
||||
|
||||
**kwargs
|
||||
Patch properties for the line drawn:
|
||||
|
||||
%(Patch)s
|
||||
"""
|
||||
if "transform" in kwargs:
|
||||
raise ValueError("transform should not be set")
|
||||
BboxConnector.__init__(self, bbox1, bbox2, loc1a, loc2a, **kwargs)
|
||||
self.loc1b = loc1b
|
||||
self.loc2b = loc2b
|
||||
|
||||
def get_path(self):
|
||||
# docstring inherited
|
||||
path1 = self.connect_bbox(self.bbox1, self.bbox2, self.loc1, self.loc2)
|
||||
path2 = self.connect_bbox(self.bbox2, self.bbox1,
|
||||
self.loc2b, self.loc1b)
|
||||
path_merged = [*path1.vertices, *path2.vertices, path1.vertices[0]]
|
||||
return Path(path_merged)
|
||||
|
||||
|
||||
def _add_inset_axes(parent_axes, inset_axes):
|
||||
"""Helper function to add an inset axes and disable navigation in it"""
|
||||
parent_axes.figure.add_axes(inset_axes)
|
||||
inset_axes.set_navigate(False)
|
||||
|
||||
|
||||
@docstring.dedent_interpd
|
||||
def inset_axes(parent_axes, width, height, loc='upper right',
|
||||
bbox_to_anchor=None, bbox_transform=None,
|
||||
axes_class=None,
|
||||
axes_kwargs=None,
|
||||
borderpad=0.5):
|
||||
"""
|
||||
Create an inset axes with a given width and height.
|
||||
|
||||
Both sizes used can be specified either in inches or percentage.
|
||||
For example,::
|
||||
|
||||
inset_axes(parent_axes, width='40%%', height='30%%', loc=3)
|
||||
|
||||
creates in inset axes in the lower left corner of *parent_axes* which spans
|
||||
over 30%% in height and 40%% in width of the *parent_axes*. Since the usage
|
||||
of `.inset_axes` may become slightly tricky when exceeding such standard
|
||||
cases, it is recommended to read :doc:`the examples
|
||||
</gallery/axes_grid1/inset_locator_demo>`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The meaning of *bbox_to_anchor* and *bbox_to_transform* is interpreted
|
||||
differently from that of legend. The value of bbox_to_anchor
|
||||
(or the return value of its get_points method; the default is
|
||||
*parent_axes.bbox*) is transformed by the bbox_transform (the default
|
||||
is Identity transform) and then interpreted as points in the pixel
|
||||
coordinate (which is dpi dependent).
|
||||
|
||||
Thus, following three calls are identical and creates an inset axes
|
||||
with respect to the *parent_axes*::
|
||||
|
||||
axins = inset_axes(parent_axes, "30%%", "40%%")
|
||||
axins = inset_axes(parent_axes, "30%%", "40%%",
|
||||
bbox_to_anchor=parent_axes.bbox)
|
||||
axins = inset_axes(parent_axes, "30%%", "40%%",
|
||||
bbox_to_anchor=(0, 0, 1, 1),
|
||||
bbox_transform=parent_axes.transAxes)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parent_axes : `matplotlib.axes.Axes`
|
||||
Axes to place the inset axes.
|
||||
|
||||
width, height : float or str
|
||||
Size of the inset axes to create. If a float is provided, it is
|
||||
the size in inches, e.g. *width=1.3*. If a string is provided, it is
|
||||
the size in relative units, e.g. *width='40%%'*. By default, i.e. if
|
||||
neither *bbox_to_anchor* nor *bbox_transform* are specified, those
|
||||
are relative to the parent_axes. Otherwise they are to be understood
|
||||
relative to the bounding box provided via *bbox_to_anchor*.
|
||||
|
||||
loc : int or str, default: 1
|
||||
Location to place the inset axes. The valid locations are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional
|
||||
Bbox that the inset axes will be anchored to. If None,
|
||||
a tuple of (0, 0, 1, 1) is used if *bbox_transform* is set
|
||||
to *parent_axes.transAxes* or *parent_axes.figure.transFigure*.
|
||||
Otherwise, *parent_axes.bbox* is used. If a tuple, can be either
|
||||
[left, bottom, width, height], or [left, bottom].
|
||||
If the kwargs *width* and/or *height* are specified in relative units,
|
||||
the 2-tuple [left, bottom] cannot be used. Note that,
|
||||
unless *bbox_transform* is set, the units of the bounding box
|
||||
are interpreted in the pixel coordinate. When using *bbox_to_anchor*
|
||||
with tuple, it almost always makes sense to also specify
|
||||
a *bbox_transform*. This might often be the axes transform
|
||||
*parent_axes.transAxes*.
|
||||
|
||||
bbox_transform : `matplotlib.transforms.Transform`, optional
|
||||
Transformation for the bbox that contains the inset axes.
|
||||
If None, a `.transforms.IdentityTransform` is used. The value
|
||||
of *bbox_to_anchor* (or the return value of its get_points method)
|
||||
is transformed by the *bbox_transform* and then interpreted
|
||||
as points in the pixel coordinate (which is dpi dependent).
|
||||
You may provide *bbox_to_anchor* in some normalized coordinate,
|
||||
and give an appropriate transform (e.g., *parent_axes.transAxes*).
|
||||
|
||||
axes_class : `matplotlib.axes.Axes` type, optional
|
||||
If specified, the inset axes created will be created with this class's
|
||||
constructor.
|
||||
|
||||
axes_kwargs : dict, optional
|
||||
Keyworded arguments to pass to the constructor of the inset axes.
|
||||
Valid arguments include:
|
||||
|
||||
%(Axes)s
|
||||
|
||||
borderpad : float, default: 0.5
|
||||
Padding between inset axes and the bbox_to_anchor.
|
||||
The units are axes font size, i.e. for a default font size of 10 points
|
||||
*borderpad = 0.5* is equivalent to a padding of 5 points.
|
||||
|
||||
Returns
|
||||
-------
|
||||
inset_axes : *axes_class*
|
||||
Inset axes object created.
|
||||
"""
|
||||
|
||||
if axes_class is None:
|
||||
axes_class = HostAxes
|
||||
|
||||
if axes_kwargs is None:
|
||||
inset_axes = axes_class(parent_axes.figure, parent_axes.get_position())
|
||||
else:
|
||||
inset_axes = axes_class(parent_axes.figure, parent_axes.get_position(),
|
||||
**axes_kwargs)
|
||||
|
||||
if bbox_transform in [parent_axes.transAxes,
|
||||
parent_axes.figure.transFigure]:
|
||||
if bbox_to_anchor is None:
|
||||
cbook._warn_external("Using the axes or figure transform "
|
||||
"requires a bounding box in the respective "
|
||||
"coordinates. "
|
||||
"Using bbox_to_anchor=(0, 0, 1, 1) now.")
|
||||
bbox_to_anchor = (0, 0, 1, 1)
|
||||
|
||||
if bbox_to_anchor is None:
|
||||
bbox_to_anchor = parent_axes.bbox
|
||||
|
||||
if (isinstance(bbox_to_anchor, tuple) and
|
||||
(isinstance(width, str) or isinstance(height, str))):
|
||||
if len(bbox_to_anchor) != 4:
|
||||
raise ValueError("Using relative units for width or height "
|
||||
"requires to provide a 4-tuple or a "
|
||||
"`Bbox` instance to `bbox_to_anchor.")
|
||||
|
||||
axes_locator = AnchoredSizeLocator(bbox_to_anchor,
|
||||
width, height,
|
||||
loc=loc,
|
||||
bbox_transform=bbox_transform,
|
||||
borderpad=borderpad)
|
||||
|
||||
inset_axes.set_axes_locator(axes_locator)
|
||||
|
||||
_add_inset_axes(parent_axes, inset_axes)
|
||||
|
||||
return inset_axes
|
||||
|
||||
|
||||
@docstring.dedent_interpd
|
||||
def zoomed_inset_axes(parent_axes, zoom, loc='upper right',
|
||||
bbox_to_anchor=None, bbox_transform=None,
|
||||
axes_class=None,
|
||||
axes_kwargs=None,
|
||||
borderpad=0.5):
|
||||
"""
|
||||
Create an anchored inset axes by scaling a parent axes. For usage, also see
|
||||
:doc:`the examples </gallery/axes_grid1/inset_locator_demo2>`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parent_axes : `matplotlib.axes.Axes`
|
||||
Axes to place the inset axes.
|
||||
|
||||
zoom : float
|
||||
Scaling factor of the data axes. *zoom* > 1 will enlargen the
|
||||
coordinates (i.e., "zoomed in"), while *zoom* < 1 will shrink the
|
||||
coordinates (i.e., "zoomed out").
|
||||
|
||||
loc : int or str, default: 'upper right'
|
||||
Location to place the inset axes. The valid locations are::
|
||||
|
||||
'upper right' : 1,
|
||||
'upper left' : 2,
|
||||
'lower left' : 3,
|
||||
'lower right' : 4,
|
||||
'right' : 5,
|
||||
'center left' : 6,
|
||||
'center right' : 7,
|
||||
'lower center' : 8,
|
||||
'upper center' : 9,
|
||||
'center' : 10
|
||||
|
||||
bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional
|
||||
Bbox that the inset axes will be anchored to. If None,
|
||||
*parent_axes.bbox* is used. If a tuple, can be either
|
||||
[left, bottom, width, height], or [left, bottom].
|
||||
If the kwargs *width* and/or *height* are specified in relative units,
|
||||
the 2-tuple [left, bottom] cannot be used. Note that
|
||||
the units of the bounding box are determined through the transform
|
||||
in use. When using *bbox_to_anchor* it almost always makes sense to
|
||||
also specify a *bbox_transform*. This might often be the axes transform
|
||||
*parent_axes.transAxes*.
|
||||
|
||||
bbox_transform : `matplotlib.transforms.Transform`, optional
|
||||
Transformation for the bbox that contains the inset axes.
|
||||
If None, a `.transforms.IdentityTransform` is used (i.e. pixel
|
||||
coordinates). This is useful when not providing any argument to
|
||||
*bbox_to_anchor*. When using *bbox_to_anchor* it almost always makes
|
||||
sense to also specify a *bbox_transform*. This might often be the
|
||||
axes transform *parent_axes.transAxes*. Inversely, when specifying
|
||||
the axes- or figure-transform here, be aware that not specifying
|
||||
*bbox_to_anchor* will use *parent_axes.bbox*, the units of which are
|
||||
in display (pixel) coordinates.
|
||||
|
||||
axes_class : `matplotlib.axes.Axes` type, optional
|
||||
If specified, the inset axes created will be created with this class's
|
||||
constructor.
|
||||
|
||||
axes_kwargs : dict, optional
|
||||
Keyworded arguments to pass to the constructor of the inset axes.
|
||||
Valid arguments include:
|
||||
|
||||
%(Axes)s
|
||||
|
||||
borderpad : float, default: 0.5
|
||||
Padding between inset axes and the bbox_to_anchor.
|
||||
The units are axes font size, i.e. for a default font size of 10 points
|
||||
*borderpad = 0.5* is equivalent to a padding of 5 points.
|
||||
|
||||
Returns
|
||||
-------
|
||||
inset_axes : *axes_class*
|
||||
Inset axes object created.
|
||||
"""
|
||||
|
||||
if axes_class is None:
|
||||
axes_class = HostAxes
|
||||
|
||||
if axes_kwargs is None:
|
||||
inset_axes = axes_class(parent_axes.figure, parent_axes.get_position())
|
||||
else:
|
||||
inset_axes = axes_class(parent_axes.figure, parent_axes.get_position(),
|
||||
**axes_kwargs)
|
||||
|
||||
axes_locator = AnchoredZoomLocator(parent_axes, zoom=zoom, loc=loc,
|
||||
bbox_to_anchor=bbox_to_anchor,
|
||||
bbox_transform=bbox_transform,
|
||||
borderpad=borderpad)
|
||||
inset_axes.set_axes_locator(axes_locator)
|
||||
|
||||
_add_inset_axes(parent_axes, inset_axes)
|
||||
|
||||
return inset_axes
|
||||
|
||||
|
||||
@docstring.dedent_interpd
|
||||
def mark_inset(parent_axes, inset_axes, loc1, loc2, **kwargs):
|
||||
"""
|
||||
Draw a box to mark the location of an area represented by an inset axes.
|
||||
|
||||
This function draws a box in *parent_axes* at the bounding box of
|
||||
*inset_axes*, and shows a connection with the inset axes by drawing lines
|
||||
at the corners, giving a "zoomed in" effect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parent_axes : `matplotlib.axes.Axes`
|
||||
Axes which contains the area of the inset axes.
|
||||
|
||||
inset_axes : `matplotlib.axes.Axes`
|
||||
The inset axes.
|
||||
|
||||
loc1, loc2 : {1, 2, 3, 4}
|
||||
Corners to use for connecting the inset axes and the area in the
|
||||
parent axes.
|
||||
|
||||
**kwargs
|
||||
Patch properties for the lines and box drawn:
|
||||
|
||||
%(Patch)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
pp : `matplotlib.patches.Patch`
|
||||
The patch drawn to represent the area of the inset axes.
|
||||
|
||||
p1, p2 : `matplotlib.patches.Patch`
|
||||
The patches connecting two corners of the inset axes and its area.
|
||||
"""
|
||||
rect = TransformedBbox(inset_axes.viewLim, parent_axes.transData)
|
||||
|
||||
if 'fill' in kwargs:
|
||||
pp = BboxPatch(rect, **kwargs)
|
||||
else:
|
||||
fill = bool({'fc', 'facecolor', 'color'}.intersection(kwargs))
|
||||
pp = BboxPatch(rect, fill=fill, **kwargs)
|
||||
parent_axes.add_patch(pp)
|
||||
|
||||
p1 = BboxConnector(inset_axes.bbox, rect, loc1=loc1, **kwargs)
|
||||
inset_axes.add_patch(p1)
|
||||
p1.set_clip_on(False)
|
||||
p2 = BboxConnector(inset_axes.bbox, rect, loc1=loc2, **kwargs)
|
||||
inset_axes.add_patch(p2)
|
||||
p2.set_clip_on(False)
|
||||
|
||||
return pp, p1, p2
|
134
venv/Lib/site-packages/mpl_toolkits/axes_grid1/mpl_axes.py
Normal file
134
venv/Lib/site-packages/mpl_toolkits/axes_grid1/mpl_axes.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
import matplotlib.axes as maxes
|
||||
from matplotlib.artist import Artist
|
||||
from matplotlib.axis import XAxis, YAxis
|
||||
|
||||
|
||||
class SimpleChainedObjects:
|
||||
def __init__(self, objects):
|
||||
self._objects = objects
|
||||
|
||||
def __getattr__(self, k):
|
||||
_a = SimpleChainedObjects([getattr(a, k) for a in self._objects])
|
||||
return _a
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
for m in self._objects:
|
||||
m(*args, **kwargs)
|
||||
|
||||
|
||||
class Axes(maxes.Axes):
|
||||
|
||||
class AxisDict(dict):
|
||||
def __init__(self, axes):
|
||||
self.axes = axes
|
||||
super().__init__()
|
||||
|
||||
def __getitem__(self, k):
|
||||
if isinstance(k, tuple):
|
||||
r = SimpleChainedObjects(
|
||||
# super() within a list comprehension needs explicit args.
|
||||
[super(Axes.AxisDict, self).__getitem__(k1) for k1 in k])
|
||||
return r
|
||||
elif isinstance(k, slice):
|
||||
if k.start is None and k.stop is None and k.step is None:
|
||||
return SimpleChainedObjects(list(self.values()))
|
||||
else:
|
||||
raise ValueError("Unsupported slice")
|
||||
else:
|
||||
return dict.__getitem__(self, k)
|
||||
|
||||
def __call__(self, *v, **kwargs):
|
||||
return maxes.Axes.axis(self.axes, *v, **kwargs)
|
||||
|
||||
def _init_axis_artists(self, axes=None):
|
||||
if axes is None:
|
||||
axes = self
|
||||
self._axislines = self.AxisDict(self)
|
||||
self._axislines.update(
|
||||
bottom=SimpleAxisArtist(self.xaxis, 1, self.spines["bottom"]),
|
||||
top=SimpleAxisArtist(self.xaxis, 2, self.spines["top"]),
|
||||
left=SimpleAxisArtist(self.yaxis, 1, self.spines["left"]),
|
||||
right=SimpleAxisArtist(self.yaxis, 2, self.spines["right"]))
|
||||
|
||||
@property
|
||||
def axis(self):
|
||||
return self._axislines
|
||||
|
||||
def cla(self):
|
||||
super().cla()
|
||||
self._init_axis_artists()
|
||||
|
||||
|
||||
class SimpleAxisArtist(Artist):
|
||||
def __init__(self, axis, axisnum, spine):
|
||||
self._axis = axis
|
||||
self._axisnum = axisnum
|
||||
self.line = spine
|
||||
|
||||
if isinstance(axis, XAxis):
|
||||
self._axis_direction = ["bottom", "top"][axisnum-1]
|
||||
elif isinstance(axis, YAxis):
|
||||
self._axis_direction = ["left", "right"][axisnum-1]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"axis must be instance of XAxis or YAxis, but got {axis}")
|
||||
Artist.__init__(self)
|
||||
|
||||
@property
|
||||
def major_ticks(self):
|
||||
tickline = "tick%dline" % self._axisnum
|
||||
return SimpleChainedObjects([getattr(tick, tickline)
|
||||
for tick in self._axis.get_major_ticks()])
|
||||
|
||||
@property
|
||||
def major_ticklabels(self):
|
||||
label = "label%d" % self._axisnum
|
||||
return SimpleChainedObjects([getattr(tick, label)
|
||||
for tick in self._axis.get_major_ticks()])
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self._axis.label
|
||||
|
||||
def set_visible(self, b):
|
||||
self.toggle(all=b)
|
||||
self.line.set_visible(b)
|
||||
self._axis.set_visible(True)
|
||||
Artist.set_visible(self, b)
|
||||
|
||||
def set_label(self, txt):
|
||||
self._axis.set_label_text(txt)
|
||||
|
||||
def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
|
||||
|
||||
if all:
|
||||
_ticks, _ticklabels, _label = True, True, True
|
||||
elif all is not None:
|
||||
_ticks, _ticklabels, _label = False, False, False
|
||||
else:
|
||||
_ticks, _ticklabels, _label = None, None, None
|
||||
|
||||
if ticks is not None:
|
||||
_ticks = ticks
|
||||
if ticklabels is not None:
|
||||
_ticklabels = ticklabels
|
||||
if label is not None:
|
||||
_label = label
|
||||
|
||||
tickOn = "tick%dOn" % self._axisnum
|
||||
labelOn = "label%dOn" % self._axisnum
|
||||
|
||||
if _ticks is not None:
|
||||
tickparam = {tickOn: _ticks}
|
||||
self._axis.set_tick_params(**tickparam)
|
||||
if _ticklabels is not None:
|
||||
tickparam = {labelOn: _ticklabels}
|
||||
self._axis.set_tick_params(**tickparam)
|
||||
|
||||
if _label is not None:
|
||||
pos = self._axis.get_label_position()
|
||||
if (pos == self._axis_direction) and not _label:
|
||||
self._axis.label.set_visible(False)
|
||||
elif _label:
|
||||
self._axis.label.set_visible(True)
|
||||
self._axis.set_label_position(self._axis_direction)
|
433
venv/Lib/site-packages/mpl_toolkits/axes_grid1/parasite_axes.py
Normal file
433
venv/Lib/site-packages/mpl_toolkits/axes_grid1/parasite_axes.py
Normal file
|
@ -0,0 +1,433 @@
|
|||
import functools
|
||||
|
||||
from matplotlib import artist as martist, cbook, transforms as mtransforms
|
||||
from matplotlib.axes import subplot_class_factory
|
||||
from matplotlib.transforms import Bbox
|
||||
from .mpl_axes import Axes
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class ParasiteAxesBase:
|
||||
|
||||
def get_images_artists(self):
|
||||
artists = {a for a in self.get_children() if a.get_visible()}
|
||||
images = {a for a in self.images if a.get_visible()}
|
||||
|
||||
return list(images), list(artists - images)
|
||||
|
||||
def __init__(self, parent_axes, **kwargs):
|
||||
self._parent_axes = parent_axes
|
||||
kwargs["frameon"] = False
|
||||
super().__init__(parent_axes.figure, parent_axes._position, **kwargs)
|
||||
|
||||
def cla(self):
|
||||
super().cla()
|
||||
|
||||
martist.setp(self.get_children(), visible=False)
|
||||
self._get_lines = self._parent_axes._get_lines
|
||||
|
||||
# In mpl's Axes, zorders of x- and y-axis are originally set
|
||||
# within Axes.draw().
|
||||
if self._axisbelow:
|
||||
self.xaxis.set_zorder(0.5)
|
||||
self.yaxis.set_zorder(0.5)
|
||||
else:
|
||||
self.xaxis.set_zorder(2.5)
|
||||
self.yaxis.set_zorder(2.5)
|
||||
|
||||
def pick(self, mouseevent):
|
||||
# This most likely goes to Artist.pick (depending on axes_class given
|
||||
# to the factory), which only handles pick events registered on the
|
||||
# axes associated with each child:
|
||||
super().pick(mouseevent)
|
||||
# But parasite axes are additionally given pick events from their host
|
||||
# axes (cf. HostAxesBase.pick), which we handle here:
|
||||
for a in self.get_children():
|
||||
if (hasattr(mouseevent.inaxes, "parasites")
|
||||
and self in mouseevent.inaxes.parasites):
|
||||
a.pick(mouseevent)
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def parasite_axes_class_factory(axes_class=None):
|
||||
if axes_class is None:
|
||||
cbook.warn_deprecated(
|
||||
"3.3", message="Support for passing None to "
|
||||
"parasite_axes_class_factory is deprecated since %(since)s and "
|
||||
"will be removed %(removal)s; explicitly pass the default Axes "
|
||||
"class instead.")
|
||||
axes_class = Axes
|
||||
|
||||
return type("%sParasite" % axes_class.__name__,
|
||||
(ParasiteAxesBase, axes_class), {})
|
||||
|
||||
|
||||
ParasiteAxes = parasite_axes_class_factory(Axes)
|
||||
|
||||
|
||||
class ParasiteAxesAuxTransBase:
|
||||
def __init__(self, parent_axes, aux_transform, viewlim_mode=None,
|
||||
**kwargs):
|
||||
self.transAux = aux_transform
|
||||
self.set_viewlim_mode(viewlim_mode)
|
||||
super().__init__(parent_axes, **kwargs)
|
||||
|
||||
def _set_lim_and_transforms(self):
|
||||
|
||||
self.transAxes = self._parent_axes.transAxes
|
||||
|
||||
self.transData = \
|
||||
self.transAux + \
|
||||
self._parent_axes.transData
|
||||
|
||||
self._xaxis_transform = mtransforms.blended_transform_factory(
|
||||
self.transData, self.transAxes)
|
||||
self._yaxis_transform = mtransforms.blended_transform_factory(
|
||||
self.transAxes, self.transData)
|
||||
|
||||
def set_viewlim_mode(self, mode):
|
||||
cbook._check_in_list([None, "equal", "transform"], mode=mode)
|
||||
self._viewlim_mode = mode
|
||||
|
||||
def get_viewlim_mode(self):
|
||||
return self._viewlim_mode
|
||||
|
||||
def update_viewlim(self):
|
||||
viewlim = self._parent_axes.viewLim.frozen()
|
||||
mode = self.get_viewlim_mode()
|
||||
if mode is None:
|
||||
pass
|
||||
elif mode == "equal":
|
||||
self.axes.viewLim.set(viewlim)
|
||||
elif mode == "transform":
|
||||
self.axes.viewLim.set(
|
||||
viewlim.transformed(self.transAux.inverted()))
|
||||
else:
|
||||
cbook._check_in_list([None, "equal", "transform"], mode=mode)
|
||||
|
||||
def _pcolor(self, super_pcolor, *XYC, **kwargs):
|
||||
if len(XYC) == 1:
|
||||
C = XYC[0]
|
||||
ny, nx = C.shape
|
||||
|
||||
gx = np.arange(-0.5, nx)
|
||||
gy = np.arange(-0.5, ny)
|
||||
|
||||
X, Y = np.meshgrid(gx, gy)
|
||||
else:
|
||||
X, Y, C = XYC
|
||||
|
||||
if "transform" in kwargs:
|
||||
mesh = super_pcolor(X, Y, C, **kwargs)
|
||||
else:
|
||||
orig_shape = X.shape
|
||||
xyt = np.column_stack([X.flat, Y.flat])
|
||||
wxy = self.transAux.transform(xyt)
|
||||
gx = wxy[:, 0].reshape(orig_shape)
|
||||
gy = wxy[:, 1].reshape(orig_shape)
|
||||
mesh = super_pcolor(gx, gy, C, **kwargs)
|
||||
mesh.set_transform(self._parent_axes.transData)
|
||||
|
||||
return mesh
|
||||
|
||||
def pcolormesh(self, *XYC, **kwargs):
|
||||
return self._pcolor(super().pcolormesh, *XYC, **kwargs)
|
||||
|
||||
def pcolor(self, *XYC, **kwargs):
|
||||
return self._pcolor(super().pcolor, *XYC, **kwargs)
|
||||
|
||||
def _contour(self, super_contour, *XYCL, **kwargs):
|
||||
|
||||
if len(XYCL) <= 2:
|
||||
C = XYCL[0]
|
||||
ny, nx = C.shape
|
||||
|
||||
gx = np.arange(0., nx)
|
||||
gy = np.arange(0., ny)
|
||||
|
||||
X, Y = np.meshgrid(gx, gy)
|
||||
CL = XYCL
|
||||
else:
|
||||
X, Y = XYCL[:2]
|
||||
CL = XYCL[2:]
|
||||
|
||||
if "transform" in kwargs:
|
||||
cont = super_contour(X, Y, *CL, **kwargs)
|
||||
else:
|
||||
orig_shape = X.shape
|
||||
xyt = np.column_stack([X.flat, Y.flat])
|
||||
wxy = self.transAux.transform(xyt)
|
||||
gx = wxy[:, 0].reshape(orig_shape)
|
||||
gy = wxy[:, 1].reshape(orig_shape)
|
||||
cont = super_contour(gx, gy, *CL, **kwargs)
|
||||
for c in cont.collections:
|
||||
c.set_transform(self._parent_axes.transData)
|
||||
|
||||
return cont
|
||||
|
||||
def contour(self, *XYCL, **kwargs):
|
||||
return self._contour(super().contour, *XYCL, **kwargs)
|
||||
|
||||
def contourf(self, *XYCL, **kwargs):
|
||||
return self._contour(super().contourf, *XYCL, **kwargs)
|
||||
|
||||
def apply_aspect(self, position=None):
|
||||
self.update_viewlim()
|
||||
super().apply_aspect()
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def parasite_axes_auxtrans_class_factory(axes_class=None):
|
||||
if axes_class is None:
|
||||
cbook.warn_deprecated(
|
||||
"3.3", message="Support for passing None to "
|
||||
"parasite_axes_auxtrans_class_factory is deprecated since "
|
||||
"%(since)s and will be removed %(removal)s; explicitly pass the "
|
||||
"default ParasiteAxes class instead.")
|
||||
parasite_axes_class = ParasiteAxes
|
||||
elif not issubclass(axes_class, ParasiteAxesBase):
|
||||
parasite_axes_class = parasite_axes_class_factory(axes_class)
|
||||
else:
|
||||
parasite_axes_class = axes_class
|
||||
return type("%sParasiteAuxTrans" % parasite_axes_class.__name__,
|
||||
(ParasiteAxesAuxTransBase, parasite_axes_class),
|
||||
{'name': 'parasite_axes'})
|
||||
|
||||
|
||||
ParasiteAxesAuxTrans = parasite_axes_auxtrans_class_factory(ParasiteAxes)
|
||||
|
||||
|
||||
class HostAxesBase:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.parasites = []
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_aux_axes(self, tr, viewlim_mode="equal", axes_class=ParasiteAxes):
|
||||
parasite_axes_class = parasite_axes_auxtrans_class_factory(axes_class)
|
||||
ax2 = parasite_axes_class(self, tr, viewlim_mode)
|
||||
# note that ax2.transData == tr + ax1.transData
|
||||
# Anything you draw in ax2 will match the ticks and grids of ax1.
|
||||
self.parasites.append(ax2)
|
||||
ax2._remove_method = self.parasites.remove
|
||||
return ax2
|
||||
|
||||
def _get_legend_handles(self, legend_handler_map=None):
|
||||
all_handles = super()._get_legend_handles()
|
||||
for ax in self.parasites:
|
||||
all_handles.extend(ax._get_legend_handles(legend_handler_map))
|
||||
return all_handles
|
||||
|
||||
def draw(self, renderer):
|
||||
|
||||
orig_artists = list(self.artists)
|
||||
orig_images = list(self.images)
|
||||
|
||||
if hasattr(self, "get_axes_locator"):
|
||||
locator = self.get_axes_locator()
|
||||
if locator:
|
||||
pos = locator(self, renderer)
|
||||
self.set_position(pos, which="active")
|
||||
self.apply_aspect(pos)
|
||||
else:
|
||||
self.apply_aspect()
|
||||
else:
|
||||
self.apply_aspect()
|
||||
|
||||
rect = self.get_position()
|
||||
|
||||
for ax in self.parasites:
|
||||
ax.apply_aspect(rect)
|
||||
images, artists = ax.get_images_artists()
|
||||
self.images.extend(images)
|
||||
self.artists.extend(artists)
|
||||
|
||||
super().draw(renderer)
|
||||
self.artists = orig_artists
|
||||
self.images = orig_images
|
||||
|
||||
def cla(self):
|
||||
for ax in self.parasites:
|
||||
ax.cla()
|
||||
super().cla()
|
||||
|
||||
def pick(self, mouseevent):
|
||||
super().pick(mouseevent)
|
||||
# Also pass pick events on to parasite axes and, in turn, their
|
||||
# children (cf. ParasiteAxesBase.pick)
|
||||
for a in self.parasites:
|
||||
a.pick(mouseevent)
|
||||
|
||||
def twinx(self, axes_class=None):
|
||||
"""
|
||||
Create a twin of Axes with a shared x-axis but independent y-axis.
|
||||
|
||||
The y-axis of self will have ticks on the left and the returned axes
|
||||
will have ticks on the right.
|
||||
"""
|
||||
if axes_class is None:
|
||||
axes_class = self._get_base_axes()
|
||||
|
||||
parasite_axes_class = parasite_axes_class_factory(axes_class)
|
||||
|
||||
ax2 = parasite_axes_class(self, sharex=self, frameon=False)
|
||||
self.parasites.append(ax2)
|
||||
ax2._remove_method = self._remove_twinx
|
||||
|
||||
self.axis["right"].set_visible(False)
|
||||
|
||||
ax2.axis["right"].set_visible(True)
|
||||
ax2.axis["left", "top", "bottom"].set_visible(False)
|
||||
|
||||
return ax2
|
||||
|
||||
def _remove_twinx(self, ax):
|
||||
self.parasites.remove(ax)
|
||||
self.axis["right"].set_visible(True)
|
||||
self.axis["right"].toggle(ticklabels=False, label=False)
|
||||
|
||||
def twiny(self, axes_class=None):
|
||||
"""
|
||||
Create a twin of Axes with a shared y-axis but independent x-axis.
|
||||
|
||||
The x-axis of self will have ticks on the bottom and the returned axes
|
||||
will have ticks on the top.
|
||||
"""
|
||||
if axes_class is None:
|
||||
axes_class = self._get_base_axes()
|
||||
|
||||
parasite_axes_class = parasite_axes_class_factory(axes_class)
|
||||
|
||||
ax2 = parasite_axes_class(self, sharey=self, frameon=False)
|
||||
self.parasites.append(ax2)
|
||||
ax2._remove_method = self._remove_twiny
|
||||
|
||||
self.axis["top"].set_visible(False)
|
||||
|
||||
ax2.axis["top"].set_visible(True)
|
||||
ax2.axis["left", "right", "bottom"].set_visible(False)
|
||||
|
||||
return ax2
|
||||
|
||||
def _remove_twiny(self, ax):
|
||||
self.parasites.remove(ax)
|
||||
self.axis["top"].set_visible(True)
|
||||
self.axis["top"].toggle(ticklabels=False, label=False)
|
||||
|
||||
def twin(self, aux_trans=None, axes_class=None):
|
||||
"""
|
||||
Create a twin of Axes with no shared axis.
|
||||
|
||||
While self will have ticks on the left and bottom axis, the returned
|
||||
axes will have ticks on the top and right axis.
|
||||
"""
|
||||
if axes_class is None:
|
||||
axes_class = self._get_base_axes()
|
||||
|
||||
parasite_axes_auxtrans_class = \
|
||||
parasite_axes_auxtrans_class_factory(axes_class)
|
||||
|
||||
if aux_trans is None:
|
||||
ax2 = parasite_axes_auxtrans_class(
|
||||
self, mtransforms.IdentityTransform(), viewlim_mode="equal")
|
||||
else:
|
||||
ax2 = parasite_axes_auxtrans_class(
|
||||
self, aux_trans, viewlim_mode="transform")
|
||||
self.parasites.append(ax2)
|
||||
ax2._remove_method = self.parasites.remove
|
||||
|
||||
self.axis["top", "right"].set_visible(False)
|
||||
|
||||
ax2.axis["top", "right"].set_visible(True)
|
||||
ax2.axis["left", "bottom"].set_visible(False)
|
||||
|
||||
def _remove_method(h):
|
||||
self.parasites.remove(h)
|
||||
self.axis["top", "right"].set_visible(True)
|
||||
self.axis["top", "right"].toggle(ticklabels=False, label=False)
|
||||
ax2._remove_method = _remove_method
|
||||
|
||||
return ax2
|
||||
|
||||
def get_tightbbox(self, renderer, call_axes_locator=True,
|
||||
bbox_extra_artists=None):
|
||||
bbs = [
|
||||
*[ax.get_tightbbox(renderer, call_axes_locator=call_axes_locator)
|
||||
for ax in self.parasites],
|
||||
super().get_tightbbox(renderer,
|
||||
call_axes_locator=call_axes_locator,
|
||||
bbox_extra_artists=bbox_extra_artists)]
|
||||
return Bbox.union([b for b in bbs if b.width != 0 or b.height != 0])
|
||||
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def host_axes_class_factory(axes_class=None):
|
||||
if axes_class is None:
|
||||
cbook.warn_deprecated(
|
||||
"3.3", message="Support for passing None to host_axes_class is "
|
||||
"deprecated since %(since)s and will be removed %(removed)s; "
|
||||
"explicitly pass the default Axes class instead.")
|
||||
axes_class = Axes
|
||||
|
||||
def _get_base_axes(self):
|
||||
return axes_class
|
||||
|
||||
return type("%sHostAxes" % axes_class.__name__,
|
||||
(HostAxesBase, axes_class),
|
||||
{'_get_base_axes': _get_base_axes})
|
||||
|
||||
|
||||
def host_subplot_class_factory(axes_class):
|
||||
host_axes_class = host_axes_class_factory(axes_class)
|
||||
subplot_host_class = subplot_class_factory(host_axes_class)
|
||||
return subplot_host_class
|
||||
|
||||
|
||||
HostAxes = host_axes_class_factory(Axes)
|
||||
SubplotHost = subplot_class_factory(HostAxes)
|
||||
|
||||
|
||||
def host_axes(*args, axes_class=Axes, figure=None, **kwargs):
|
||||
"""
|
||||
Create axes that can act as a hosts to parasitic axes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
figure : `matplotlib.figure.Figure`
|
||||
Figure to which the axes will be added. Defaults to the current figure
|
||||
`.pyplot.gcf()`.
|
||||
|
||||
*args, **kwargs
|
||||
Will be passed on to the underlying ``Axes`` object creation.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
host_axes_class = host_axes_class_factory(axes_class)
|
||||
if figure is None:
|
||||
figure = plt.gcf()
|
||||
ax = host_axes_class(figure, *args, **kwargs)
|
||||
figure.add_axes(ax)
|
||||
plt.draw_if_interactive()
|
||||
return ax
|
||||
|
||||
|
||||
def host_subplot(*args, axes_class=Axes, figure=None, **kwargs):
|
||||
"""
|
||||
Create a subplot that can act as a host to parasitic axes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
figure : `matplotlib.figure.Figure`
|
||||
Figure to which the subplot will be added. Defaults to the current
|
||||
figure `.pyplot.gcf()`.
|
||||
|
||||
*args, **kwargs
|
||||
Will be passed on to the underlying ``Axes`` object creation.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
host_subplot_class = host_subplot_class_factory(axes_class)
|
||||
if figure is None:
|
||||
figure = plt.gcf()
|
||||
ax = host_subplot_class(figure, *args, **kwargs)
|
||||
figure.add_subplot(ax)
|
||||
plt.draw_if_interactive()
|
||||
return ax
|
Loading…
Add table
Add a link
Reference in a new issue