python-3.xmatplotlibplotannotationsmplcursors

Display annotation text of plot simultaneously in matplotlib


I am trying to display annotation text for multiple plots in the same graph(or figure) simultaneously. The below code works perfectly but it displays one annotation for one plot at a time, but my graph has multiple plots. plot1_list, plot2_list, plot3_list are the lists of plots in my graph. The annotation texts are in the form of "labels" in my plots. How can i display the annotation texts for every plots in each list simultaneously?

import mplcursors



cursor1 = mplcursors.cursor(plot1_list, hover=False, bindings={"toggle_visible": "h", "toggle_enabled": "e"})
@cursor1.connect("add")
def on_add(sel):
    sel.annotation.set_text(sel.artist.get_label())

cursor2 = mplcursors.cursor(plot2_list, hover=False, bindings={"toggle_visible": "h", "toggle_enabled": "e"})
@cursor2.connect("add")
def on_add(sel):
    sel.annotation.set_text(sel.artist.get_label())

cursor3 = mplcursors.cursor(plot3_list, hover=False, bindings={"toggle_visible": "h", "toggle_enabled": "e"})
@cursor3.connect("add")
def on_add(sel):
    sel.annotation.set_text(sel.artist.get_label())

plot list in above code are created using below code:

label1 = str("MAXIMUM HEAD=" + str(m_head))
label2 = str("MAXIMUM POWER=" + str(m_power))
label3 = str("MAXIMUM EFFICIENCY=" + str(m_efficiency))

plot1, = host.plot(y0, y, linestyle=styles[0], color=colorstouse[count], label=label1)
plot2, = par1.plot(y0, y1, linestyle=styles[1], color=colorstouse[count], label=label2)
plot3, = par2.plot(y0, y2, linestyle=styles[2], color=colorstouse[count], label=label3)

Attached is the image of a sample plot


Solution

  • In your case, you would need a separate mplcursor for each curve. That way each of them can be individually turned on or off.

    The code looks a bit more complicated than necessary. Here is an attempt to simplify. The example shows 15 curves using 3 axes. Note that it can become very crowded, so 90 curves might be a bit too much.

    from matplotlib import pyplot as plt
    import numpy as np
    import mplcursors
    
    num_devices = 5
    fig, host = plt.subplots(figsize=(12, 4))
    fig.subplots_adjust(right=0.8)
    
    par1 = host.twinx()
    par2 = host.twinx()
    par2.spines["right"].set_position(("axes", 1.1))
    
    x = np.linspace(0, 10)
    y = -x ** 2 + 5 * x + 10
    y1 = 10 + 20 * np.cos(x)
    y2 = 30 + 5 * np.sin(x)
    
    ys = [y + np.random.uniform(10, 30) for i in range(num_devices)]
    y1s = [y1 + np.random.uniform(10, 30) for i in range(num_devices)]
    y2s = [y2 + np.random.uniform(10, 30) for i in range(num_devices)]
    
    colors = plt.cm.tab10.colors
    plots = []
    for dev in range(num_devices):
        plot1 = host.plot(x, ys[dev], c=colors[dev], ls='-', label=f'plot 1 - dev {dev}')
        plot2, = par1.plot(x, y1s[dev], c=colors[dev], ls='--', label=f'plot 2 - dev {dev}')
        plot3, = par2.plot(x, y2s[dev], c=colors[dev], ls=':', label=f'plot 3 - dev {dev}')
        plots += [plot1, plot2, plot3]
    
    print(plots)
    for i, plot in enumerate(plots):
        cursor = mplcursors.cursor(plot, hover=False, bindings={"toggle_visible": "h", "toggle_enabled": "e"})
        cursor.connect("add", lambda sel: sel.annotation.set_text(sel.artist.get_label()))
    plt.show()
    

    sample plot