Using @RuthC's comment as a starting point:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
from matplotlib.path import Path as mpPath
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10, 8)
ax.set_xlim(-1, 6)
ax.set_ylim(-1, 2)
ax.grid()
width = ax.get_xlim()[1] - ax.get_xlim()[0]
height = ax.get_ylim()[1] - ax.get_ylim()[0]
ax_ins = ax.inset_axes([0, 1, 1, 0.8])
path_data = [
(mpPath.MOVETO, (0, 0)),
(mpPath.CURVE4, (0, 1)),
(mpPath.CURVE4, (1, 1)),
(mpPath.CURVE4, (1, 1)),
(mpPath.LINETO, (4, 1)),
(mpPath.CURVE4, (4, 1)),
(mpPath.CURVE4, (5, 1)),
(mpPath.CURVE4, (5, 0)),
(mpPath.CLOSEPOLY, (0, 0)),
]
codes, verts = zip(*path_data)
verts = [(v[0] / 5, v[1] / 4) for v in verts]
path = mpPath(verts, codes)
patch = mpatches.PathPatch(path, facecolor="r", alpha=0.5)
bbox = patch.get_extents()
ax_ins.add_patch(patch)
ax_ins.set_anchor("S")
ax_ins.axis("off")
ax_ins.text(
x=bbox.x0 + bbox.width / 2,
y=bbox.y0 + bbox.height / 2,
horizontalalignment="center",
verticalalignment="center",
s="Text",
fontsize=24,
)
ax_ins.text(x=0.5, y=0.45, s="Title", fontsize=24, horizontalalignment="center")
fig.tight_layout(rect=[0, 0, 1, 1.3])
fig.savefig("plot.png")
Note: The layout gets a little funky with this approach. This is why the title is being added manually rather than using ax.title().