pythonmatplotlibtextrectangles

Creating a text inside a rectangle disappearing when zooming out


I want to create an rectangle with a text inside for a gant chart with matplotlib. But if one is zooming in and out the text should never be bigger than the rectangle. So as soon as the text (with a fixed font size) is bigger than the rectangle it should disappear and as sson as it is smaller again (zooming in) it should appear again.

This is an example of a simple text with the problem that the text does not stay inside the rectangle when zooming (is not disappearing as soon as it becoems to big).

from matplotlib import pyplot as plt, patches
plt.rcParams["figure.figsize"] = [7.00, 3.50]
plt.rcParams["figure.autolayout"] = True
fig = plt.figure()
ax = fig.add_subplot(111)
rectangle = patches.Rectangle((0, 0), 3, 3, edgecolor='orange',
facecolor="green", linewidth=7)
ax.add_patch(rectangle)
rx, ry = rectangle.get_xy()
cx = rx + rectangle.get_width()/2.0
cy = ry + rectangle.get_height()/2.0
ax.annotate("Rectangle", (cx, cy), color='black', weight='bold', fontsize=10, ha='center', va='center')
plt.xlim([-5, 5])
plt.ylim([-5, 5])
plt.show()

Text should disappear now

Text should disappear now


Solution

  • To monitor the zoom state of your plot, you can connect to the "xlim_changed" and "ylim_changed" events of the Axes class. Apart from the zoom state, you might also want to factor in changes of the figure size, which you can monitor via the "resize_event" of the figure canvas.

    Below, I adjusted your code so that

    from matplotlib import pyplot as plt, patches
    
    plt.rcParams["figure.figsize"] = [7.00, 3.50]
    plt.rcParams["figure.autolayout"] = True
    fig = plt.figure()
    ax = fig.add_subplot(111)
    rectangle = patches.Rectangle((0, 0), 3, 3, edgecolor="orange",
                                  facecolor="green", linewidth=7)
    ax.add_patch(rectangle)
    rx, ry = rectangle.get_xy()
    cx = rx + rectangle.get_width()/2.0
    cy = ry + rectangle.get_height()/2.0
    annotation = ax.annotate("Rectangle", (cx, cy), color="black", weight="bold",
                             fontsize=10, ha="center", va="center")
    
    def on_size_change(*unused_events):
        xlim, ylim, figsize = ax.get_xlim(), ax.get_ylim(), fig.get_size_inches()
        x_ratio = (xlim[1] - xlim[0]) / figsize[0]  # xlim dist. over fig. width
        y_ratio = (ylim[1] - ylim[0]) / figsize[1]  # ylim dist. over fig. height
        visible = x_ratio <= 20 / 7. and y_ratio <= 20 / 3.5
        annotation.set_visible(visible)
    
    ax.callbacks.connect("xlim_changed", on_size_change)
    ax.callbacks.connect("ylim_changed", on_size_change)
    fig.canvas.mpl_connect("resize_event", on_size_change)
    
    plt.xlim([-5, 5])
    plt.ylim([-5, 5])
    plt.show()
    

    Alternatively, you might want to make the showing and hiding of your text directly depend on the rectangle's screen size (show text as long as both width and height of the rectangle are greater than the width and height of the text). In this case, you might want to adjust the on_size_change() function as follows:

    def on_size_change(*unused_events):
        annotation.set_visible(True)  # Text must be visible to get correct size
        rct_box = rectangle.get_window_extent()
        txt_box = annotation.get_window_extent()
        visible = rct_box.width > txt_box.width and rct_box.height > txt_box.height
        annotation.set_visible(visible)
    

    This might not be perfect, yet, but I hope it gives you an idea on how to proceed.