pythonmatplotlib

matplotlib path patch outside axes


I want to create a tab for a plot. I have a PathPatch object that I would like to place just above the top spine (i.e. outside the plotting area). Secondly I would like a method that dynamically adjusts the width of the path in accordance with the width of the text.

import matplotlib.pyplot as plt

import matplotlib.patches as mpatches
import matplotlib.path as mpath

fig, ax = plt.subplots()

Path = mpath.Path
path_data = [
    (Path.MOVETO, (0, 0)),
    (Path.CURVE4, (0, 1.0)),
    (Path.CURVE4, (1, 1.0)),
    (Path.CURVE4, (1, 1)),

    (Path.LINETO, (4, 1)),
    (Path.CURVE4, (4, 1)),
    (Path.CURVE4, (5, 1.05)),
    (Path.CURVE4, (5, 0)),
    (Path.CLOSEPOLY, (0, 0)),
    ]
codes, verts = zip(*path_data)
path = mpath.Path(verts, codes)
patch = mpatches.PathPatch(path, facecolor='r', alpha=0.5)

ax.add_patch(patch)

x, y = zip(*path.vertices)
#line, = ax.plot(x, y, 'go-')


text_obj = plt.text(x = 0.75, y = 0.35, s = 'Text', fontsize = 32)

ax.grid()
ax.axis('equal')
plt.show()

enter image description here


Solution

  • 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")
    

    enter image description here

    Note: The layout gets a little funky with this approach. This is why the title is being added manually rather than using ax.title().