Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Matplotlib data cursor
from matplotlib import cbook
class DataCursor(object):
"""A simple data cursor widget that displays the x,y location of a
matplotlib artist when it is selected."""
def __init__(self, artists, tolerance=5, offsets=(-20, 20),
template='x: %0.2f\ny: %0.2f', display_all=False):
"""Create the data cursor and connect it to the relevant figure.
*artists* is the matplotlib artist or sequence of artists that will be
*tolerance* is the radius (in points) that the mouse click must be
within to select the artist.
*offsets* is a tuple of (x,y) offsets in points from the selected
point to the displayed annotation box
*template* is the format string to be used. Note: For compatibility
with older versions of python, this uses the old-style (%)
formatting specification.
*display_all* controls whether more than one annotation box will
be shown if there are multiple axes. Only one will be shown
per-axis, regardless.
self.template = template
self.offsets = offsets
self.display_all = display_all
if not cbook.iterable(artists):
artists = [artists]
self.artists = artists
self.axes = tuple(set(art.axes for art in self.artists))
self.figures = tuple(set(ax.figure for ax in self.axes))
self.annotations = {}
for ax in self.axes:
self.annotations[ax] = self.annotate(ax)
# Hide the annotation box until clicked...
for artist in self.artists:
for fig in self.figures:
fig.canvas.mpl_connect('pick_event', self)
def annotate(self, ax):
"""Draws the annotation box for the given axis *ax*."""
annotation = ax.annotate(self.template, xy=(0, 0), ha='right',
xytext=self.offsets, textcoords='offset points', va='bottom',
bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')
return annotation
def __call__(self, event):
"""Update and draw the annotation box for the pick event *event*."""
# Rather than trying to interpolate, just display the clicked coords
# This will only be called if it's within "tolerance", anyway.
x, y = event.mouseevent.xdata, event.mouseevent.ydata
annotation = self.annotations[event.artist.axes]
if x is not None:
if not self.display_all:
# Hide any other annotation boxes...
for ann in self.annotations.values():
# Update the annotation in the current axis..
annotation.xy = x, y
annotation.set_text(self.template % (x, y))
if __name__ == '__main__':
import matplotlib.pyplot as plt
line1, = plt.plot(range(10), 'ro-')
line2, = plt.plot(range(10), 'bo-')
DataCursor([line1, line2])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.