Sunday, 11 December 2022

matplotlib mouseover annotation between axes

I am looking to annotate lines that are drawn between two subplots.

At this moment, I am re-instantiating the annotation by accessing ax that fired the 'motion_notify_event'.

Minimal example:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import ConnectionPatch

def update_line_annotation(event, line):
    x, y = event.xdata, event.ydata
    ax = event.inaxes

    global annot # in the real use case, annot is stored as class attribute
    annot = ax.annotate(
        f'{line}',
        xy=(x, y),
        xytext=(5, 5),
        textcoords='offset points',
    )

def hover(event):
    annot.set_visible(False)
    fig.canvas.draw_idle()

    for line in cons:
        cont, ind = line.contains(event)
        if cont:
            update_line_annotation(event, line)
            annot.set_visible(True)
            break
  

if __name__ == '__main__':
    fig, axes = plt.subplots(ncols=2)

    annot = axes[0].annotate(
        f'',
        xy=(0,0),
        xytext=(20, 20),
        textcoords='offset points',
    )
    annot.set_visible(False)

    cons = []
    for a in range(2):
        con = ConnectionPatch(
            xyA=np.random.randint(0,10,2),
            xyB=np.random.randint(0,10,2),
            coordsA='data',
            coordsB='data',
            axesA=axes[0],
            axesB=axes[1],
        )
        cons.append(con)
        axes[1].add_artist(con)

    fig.canvas.mpl_connect('motion_notify_event', hover)
    
    for ax in axes:
        ax.set_ylim(0,9)
        ax.set_xlim(0,9)

    plt.show()

There are two problems with my current approach:

  • if the cursor is located in the left ax, then the annotation is drawn behind the right ax.
  • if the cursor is between the two ax, then errors are thrown, because event.inaxes returns None, and None has no annotation method.

I have two possible solutions, for which I cannot find the necessary functionality in matplotlib:

  • make all annotations with respect to the right ax. This requires getting xdata, ydata coordinates with respect to that ax
  • make all annotations with respect to the ConnectionPatch

Help is greatly appreciated!



from matplotlib mouseover annotation between axes

No comments:

Post a Comment