0

I started from the code in this other answer, and I modified a part of it, in order to always show the labels when hovering towards the center of the plot:

...

annot = ax.annotate("", xy=(0,0), xytext=(0,0),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="w"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
xmid = np.mean(ax.get_xlim())                                                 
ymid = np.mean(ax.get_ylim())
offset = 20

def update_annot(ind):

    pos = sc.get_offsets()[ind["ind"][0]]
    annot.xy = pos
    annot.xytext = (-offset if pos[0] > xmid else offset, 
                    -offset if pos[1] > ymid else offset)
    text = "    {}, {}    ".format(" ".join(list(map(str,ind["ind"]))), 
                           " ".join([names[n] for n in ind["ind"]]))
    annot.set_text(text)
    annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
    annot.get_bbox_patch().set_alpha(0.4)
    annot.set_ha("right" if pos[0] > xmid else "left")
    annot.set_va("top" if pos[1] > ymid else "bottom")

...

which yields the following output:

bad1

and two more examples here and here. Which is not the expected output. The arrow is in the correct direction, and the text nearly the desired one, but it is aligned to the data point instead of being aligned to the end of the arrow.

What I expected to get is what is achieved using the same arguments when calling ax.annotate (without interactively modifying the annotation) which is the following behaviour:

enter image description here

OriolAbril
  • 7,315
  • 4
  • 29
  • 40

1 Answers1

0

Reading the docs more carefully, it turns out that unlike xy, xytext is not an attribute of the Annotation objects. Thus, the line

annot.xytext = (-offset if pos[0] > xmid else offset, 
                -offset if pos[1] > ymid else offset)

was actually creating a useless attribute. Therefore, the text position was not modified from the original (0,0) offset. To actually modify the text position, set_position() must be used:

annot.set_position((-offset if pos[0] > xmid else offset, 
                    -offset if pos[1] > ymid else offset))

I decided to post the question anyways even though I have already solved because I found it quite counter-intuitive and a bit confusing. annot.xy modifies the xy argument which is the head of the arrow position, and annot.set_position modifies the xytext argument which is the arrow end. Moreover, even though xytext is modified with the inherited from Text instances method set_position, it is interpreted in the units defined by textcoords (even though in cases like this one, textcoord units are not compatible with Text instances).

OriolAbril
  • 7,315
  • 4
  • 29
  • 40