import matplotlib.pyplot as plt
plt.rcParams["pdf.fonttype"] = 42
plt.rcParams["svg.fonttype"] = "none"
from typing import List, Union
from matplotlib.axes import Axes
[docs]def make_spec(
figsize: tuple = (8, 3),
height_ratios: Union[List[float], None] = None,
width_ratios: Union[List[float], None] = None,
hspace: float = 0,
wspace: float = 0,
):
"""
Make `GridSpec` allows complex layout of Axes in the figure.
Parameters
----------
figsize : 2-tuple of floats, default: `figure.figsize`
Figure dimension ``(width, height)`` in inches.
``Whole Figure Size``
height_ratios : `array-like`
Defines the relative heights of the rows. Each row gets a
relative height of ``height_ratios[i] / sum(height_ratios)``.
If not given, all rows will have the same height.
width_ratios : `array-like`
if `height_ratios` is setting, this parameter will ignore.
Defines the relative widths of the columns. Each column gets a
relative width of ``width_ratios[i] / sum(width_ratios)``.
If not given, all columns will have the same width.
wspace, hspace : `float`, default: 0
The amount of width/height reserved for space between subfigures,
expressed as a fraction of the average subfigure width/height.
If not given, the values will be inferred from a figure or
rcParams when necessary.
Returns
-------
Figure, list[Axes]
`Figure`: top level `~matplotlib.artist.Artist`, which holds all plot elements.
Many methods are implemented in `FigureBase`.
`list[Axes]`: is a list of the Axes objects. The order of the axes is left-to-right
or top-to-bottom of their position in the total layout.
Example
-------
>>> import trackc as tc
>>> fig, axs = tc.make_spec(figsize=(6,6), height_ratios=[1,1,1])
>>> axs[0].plot([1,2,3])
>>> tc.savefig('test.pdf')
"""
nfig = 0
nrows = 1
ncols = 1
ratios = height_ratios
if height_ratios != None and isinstance(height_ratios, list):
nfig = len(height_ratios)
width_ratios = None
nrows = nfig
if width_ratios != None and isinstance(width_ratios, list):
nfig = len(width_ratios)
height_ratios = None
ncols = nfig
ratios = width_ratios
if nfig <= 0:
fig, ax = plt.subplots(1, 1, figsize=figsize)
return fig, ax
fig = plt.figure(figsize=figsize)
spec = fig.add_gridspec(
nrows=nrows,
ncols=ncols,
left=None,
bottom=None,
right=None,
top=None,
wspace=wspace,
hspace=hspace,
width_ratios=width_ratios,
height_ratios=height_ratios,
)
axs = [None] * nfig
for i, v in enumerate(ratios):
axs[i] = fig.add_subplot(spec[i])
return fig, axs
# fig, axs = make_spec(height_ratios=[1,1.5,2.4], hspace=0.1)
class mortise:
bottom = 0
top = 0
track_height = 0
hspace = 0
ax = None
def __init__(self, ax_ref: Axes, bottom: float, track_height: float, hspace: float):
self.ax = ax_ref.inset_axes([0, bottom, 1, track_height])
self.bottom = bottom
self.top = bottom + track_height
self.track_height = track_height
self.hspace = hspace
[docs]class tenon:
"""
Make a virtual-figure, based on virtual-figure, users can `add` `Axes` to
the `top or bottom` of the total layout. After adding `Axes`, the user can use the ``axs`` method
to select an axes from the added axes list. The order of the axes is top-to-bottom of
the position in the total layout.
Just like Lego blocks, you can add new blocks above (top) or below (bottom)
Example
-------
>>> import trackc as tc
>>> ten = trackc.tenon(figsize=(6,1))
>>> ten.add(pos='bottom', height=1, hspace=0.1)
>>> ten.add(pos='top', height=1, hspace=0.1)
>>> ten.axs(0).plot([1,2,3])
>>> ten.axs(1).plot([1,1])
>>> tc.savefig('test.pdf')
"""
mortises = []
fig = None
ax = None
[docs] def __init__(self, figsize=(7, 1)):
fig, ax = plt.subplots(1, 1, figsize=figsize)
ax.set_axis_off()
self.fig = fig
self.ax = ax
def show(self):
for i, v in enumerate(self.mortises):
# v.ax.set_ylabel('mortises[{0}]'.format(i), rotation=0)
v.ax.text(0.5, 0.5, ".axs({0})".format(i), ha="center", va="center")
v.ax.set_xticklabels([])
v.ax.set_xticks([])
self.fig.show()
def add(self, pos="top", height=1, hspace=0):
"""
Add a `Axes` to the total layout.
Just like Lego blocks, you can add new blocks above (top) or below (bottom)
Parameters
----------
pos
one option of ['top', 'bottom']
Choose whether to add a new Axes at the top or bottom.
If set top, then a `~matplotlib.axes.Axes` object will add to top of the top `~matplotlib.axes.Axes`
height: `float`
the relative height of the newly added `Axes` compared to the virtual-figure.
For example, if the virtual-figure has a height of 1 and the new Axes has a height of 4,
the actual height of the subplot will be 1 * 4.
hspace: `float`
The amount of height reserved for space between the top or bottom subfigures
"""
if len(self.mortises) != 0:
ix = -1
if pos == "top":
ix = 0
ref_lego_ax = self.mortises[ix]
else:
ref_lego_ax = mortise(self.ax, 0, height, 0)
self.mortises = [ref_lego_ax]
return
bottom = ref_lego_ax.bottom - hspace - height
if pos == "top":
bottom = ref_lego_ax.top + hspace
lego_ax_demo = mortise(self.ax, bottom, height, hspace)
if pos == "top":
self.mortises.insert(0, lego_ax_demo)
else:
self.mortises.append(lego_ax_demo)
def _remove(self, pos="top"):
"""
Remove a `Axes` to the total layout.
Just like Lego blocks, you can remove new blocks from above (top) or below (bottom)
Parameters
----------
pos
Option of ['top', 'bottom']
Choose whether to remove a Axes from top or bottom of the whole layout.
"""
if len(self.mortises) >= 1:
if pos == "top":
self.mortises.remove(self.mortises[0])
elif pos == "bottom":
self.mortises.remove(self.mortises[-1])
else:
pass
def axs(self, index):
"""
Get one Axes from the total layout.
Parameters
----------
index
The Axes list element index
Returns
-------
`~matplotlib.axes.Axes` object
"""
return self.mortises[index].ax
[docs]def savefig(outfile, **kwargs):
"""
Save the figure.
Parameters
----------
outfile
out file path
"""
# plt.tight_layout()
kwargs.setdefault("bbox_inches", "tight")
plt.savefig(outfile, **kwargs)
# plt.savefig(outfile)
def show():
"""
show the Axes of the layout, help users resize each axes.
"""
plt.tight_layout()