7

I want to know how to create a popup box in a basemap plot. When I hover my mouse over a location , it should trigger the popup box.

Is this possible?

bmu
  • 35,119
  • 13
  • 91
  • 108
Andy Stow Away
  • 649
  • 1
  • 8
  • 17

1 Answers1

31

Yes it is possible thanks to matplotlib's event handling framework. I couldn't find an already written example which does what you are particularly interested in so I wrote one (which I will put forward for inclusion in the matplotlib source).

I would read http://matplotlib.sourceforge.net/users/event_handling.html thoroughly to best understand what is going on. Please note that although it sounds like the perfect solution "pick_event" is for mouse clicks -not for mouse over- events and doesn't work in this case.

My code, which could be objectified very nicely should one want, looks like:

import matplotlib.pyplot as plt

fig = plt.figure()
ax = plt.axes()


points_with_annotation = []
for i in range(10):
    point, = plt.plot(i, i, 'o', markersize=10)

    annotation = ax.annotate("Mouseover point %s" % i,
        xy=(i, i), xycoords='data',
        xytext=(i + 1, i), textcoords='data',
        horizontalalignment="left",
        arrowprops=dict(arrowstyle="simple",
                        connectionstyle="arc3,rad=-0.2"),
        bbox=dict(boxstyle="round", facecolor="w", 
                  edgecolor="0.5", alpha=0.9)
        )
    # by default, disable the annotation visibility
    annotation.set_visible(False)

    points_with_annotation.append([point, annotation])


def on_move(event):
    visibility_changed = False
    for point, annotation in points_with_annotation:
        should_be_visible = (point.contains(event)[0] == True)

        if should_be_visible != annotation.get_visible():
            visibility_changed = True
            annotation.set_visible(should_be_visible)

    if visibility_changed:        
        plt.draw()

on_move_id = fig.canvas.mpl_connect('motion_notify_event', on_move)

plt.show()

Hopefully everything should be fairly readable. A high level overview of the code goes:

  • Create a list of [point, annotation] pairs, where by default the annotation is not visible
  • Register a function, "on_move", to be called every time there is mouse motion detected
  • The on_move function iterates through each point and annotation, if the mouse is now over one of the points, make its associated annotation visible, if it is not, make it invisible. (the contains method is documented here)

Screenshot of the result

pelson
  • 21,252
  • 4
  • 92
  • 99
  • Ah lovely, thank you @pelson for the code and explanation. So this means that there is no direct event handler for hovering over a location on the plot. It needs to be done indirectly. – Andy Stow Away Jul 19 '12 at 09:35
  • No. You can access the x and y coordinates of the mouse event in the `on_move` function. From that you can do anything. In my case I have looked through all of the artists and identified if any contain the mouse position, but you could equally update a single annotation instance's position given the x and y. – pelson Jul 19 '12 at 10:19
  • Yeah you are right on that point, but I was looking for something like registering a callback to the a particular artist with an event like "hover_event" That is what I mean by a direct event – Andy Stow Away Jul 19 '12 at 10:38
  • Right. No, not that I am aware of. Would be a nice feature to have to be able to turn on (but may have performance impacts if it were always checking...). Hope thats been helpful. – pelson Jul 19 '12 at 11:47
  • 1
    Thanks @pelson for the provided solution. I was just curious if you know why would annotations behave strangely if the ax is a polar graph. I am using your exact solution to add annotations on my polar graph, however, only a subset of the annotations actually show up (even though they all are created and are reacting to the onmove event). I posted a question here - http://stackoverflow.com/questions/15649887/matplotlib-invisible-annotations (simplified out the event handlers). Thanks in advance for any help that you can provide! – Jin Mar 27 '13 at 00:39