Hello everyone,
I need to create plots like the following one in matplotlib
How can I get the rounded corners of the axes the same as the ones marked by red arrows in the screenshot?
Hello everyone,
I need to create plots like the following one in matplotlib
How can I get the rounded corners of the axes the same as the ones marked by red arrows in the screenshot?
Update: I found a way to achieve this by subclassing Axes and FancyBoxPatch in the following manner.
import matplotlib.patches as mpatches
import matplotlib.axes as axes
from matplotlib.projection import register_projection
class StaticColorAxisBBox(mpatches.FancyBboxPatch):
def set_edgecolor(self, color):
if hasattr(self, "_original_edgecolor"):
return
self._original_edgecolor = color
self._set_edgecolor(color)
def set_linewidth(self, w):
super().set_linewidth(1.5)
class FancyAxes(maxes.Axes):
name = "fancy_box_axes"
_edgecolor: str
def __init__(self, *args, **kwargs):
self._edgecolor = kwargs.pop("edgecolor", None)
super().__init__(*args, **kwargs)
def _gen_axes_patch(self):
return StaticColorAxisBBox(
(0, 0),
1.0,
1.0,
boxstyle="round, rounding_size=0.06, pad=0",
edgecolor=self._edgecolor,
linewidth=5,
)
Then I registered it using matplotlib.projections.register_projection function. like in this guide
register_projection(FancyAxes)
And now I can use the following code to add subplots. Parameter edgecolor can be used to choose the frame color.
ax = figure.add_subplot(
111, projection="fancy_box_axes", facecolor=COLOR_PLOT_FACE, edgecolor=COLOR_PLOT
)
ax.spines[["bottom", "left", "right", "top"]].set_visible(False)
The resulting plot frame is here. (Legend is a bit ugly now, I didn’t yet figure out how to make it look good :D)
Ah, I was going to post similar instructions, but you’ve figured out how to do the entire thing already.
My only suggestion is that this is not really a projection, so you don’t need to register it. You can pass axes_class=FancyAxes directly instead.
It seems like the problem was not so hard to solve and I was in vain to rush to ask for solutions haha 
I hope this helps someone who later runs into the same problem.
Thank you very much for sharing your approach, it helped me.
I modified your FancyAxes class so it takes the rectangle’s aspect ratio into account and rounds the corners evenly, as described here: Drawing fancy boxes — Matplotlib 3.9.2 documentation
class FancyAxes(axes.Axes):
name = "fancy_box_axes"
_edgecolor: str
def __init__(self, *args, **kwargs):
self._edgecolor = kwargs.pop("edgecolor", None)
self.aspect_ratio = kwargs.pop("ar", 1.0)
super().__init__(*args, **kwargs)
def _gen_axes_patch(self):
return StaticColorAxisBBox(
(0, 0),
1.0,
1.0,
boxstyle="round, rounding_size=0.06, pad=0",
mutation_aspect=self.aspect_ratio,
edgecolor=self._edgecolor,
linewidth=5
)
It allows to pass width/height ratio as ar parameter
ax = figure.add_subplot(
111, projection="fancy_box_axes",
facecolor=COLOR_PLOT_FACE,
edgecolor=COLOR_PLOT,
ar=width/height
)
This uses a bit of internals, but it should auto-set the aspect ratio:
class FancyAxes(maxes.Axes):
...
def _gen_axes_patch(self):
aspect = self.bbox.width / self.bbox.height
return StaticColorAxisBBox(
(0, 0),
1.0,
1.0,
boxstyle="round, rounding_size=0.06, pad=0",
edgecolor=self._edgecolor,
linewidth=5,
mutation_aspect=aspect,
)
def _set_position(self, *args, **kwargs):
super()._set_position(*args, **kwargs)
aspect = self.bbox.width / self.bbox.height
if hasattr(self, 'patch'): # If called during creation, patch might not be set.
self.patch.set_mutation_aspect(aspect)