Welcome to mplcursors’ documentation!

mplcursors provide interactive, clickable annotation for matplotlib. It is heavily inspired from mpldatacursor (https://github.com/joferkington/mpldatacursor), with a much simplified API.

mplcursors requires Python>=3.5 and matplotlib>=1.5.0.

Installation

Pick one among:

$ pip install mplcursors # from PyPI
$ pip install git+https://github.com/anntzer/mplcursors # from Github

Basic example

Basic examples work similarly to mpldatacursor:

import matplotlib.pyplot as plt
import numpy as np
import mplcursors

data = np.outer(range(10), range(1, 5))

fig, ax = plt.subplots()
lines = ax.plot(data)
ax.set_title("Click somewhere on a line.\nRight-click to deselect.\n"
             "Annotations can be dragged.")

mplcursors.cursor(lines) # or just mplcursors.cursor()

plt.show()
_images/basic.png

The cursor convenience function makes a collection of artists selectable. Specifically, its first argument can either be a list of artists or axes (in which case all artists in each of the axes become selectable); or one can just pass no argument, in which case all artists in all figures become selectable. Other arguments (which are all keyword-only) allow for basic customization of the Cursor’s behavior; please refer to the constructor’s documentation.

Activation by environment variable

It is possible to use mplcursors without modifying any source code: setting the MPLCURSORS environment variable to a JSON-encoded dict will patch plt.show to automatically call cursor before displaying the figure (with the passed keyword arguments, if any). Typical settings include:

$ MPLCURSORS={} python foo.py

and:

$ MPLCURSORS='{"hover": 1}' python foo.py

Note that this will only work if mplcursors has been installed, not if it is simply added to the PYTHONPATH.

Default UI

  • A left click on a line (a point, for plots where the data points are not connected) creates a draggable annotation there. Only one annotation is displayed (per Cursor instance), except if the multiple keyword argument was set.
  • A right click on an existing annotation will remove it.
  • Clicks do not trigger annotations if the zoom or pan tool are active. It is possible to bypass this by double-clicking instead.
  • For annotations pointing to lines or images, Shift-Left and Shift-Right move the cursor “left” or “right” by one data point. For annotations pointing to images, Shift-Up and Shift-Down are likewise available.
  • d toggles the visibility of the existing annotation(s).
  • t toggles whether the Cursor is active at all (if not, no event other than re-activation) is propagated.

These bindings are all customizable via Cursor’s bindings keyword argument.

Customization

Instead of providing a host of keyword arguments in Cursor’s constructor, mplcursors represents selections as Selection objects (essentially, namedtuples) and lets you hook into their addition and removal.

Specifically, a Selection has the following fields:

  • artist: the selected artist,
  • target: the point picked within the artist; if a point is picked on a Line2D, the index of the point is available as the target.index sub-attribute (for more details, see Selection indices).
  • dist: the distance from the point clicked to the target (mostly used to decide which ).
  • annotation: a matplotlib Annotation object.
  • extras: an additional list of artists, that will be removed whenever the main annotation is deselected.

For certain classes of artists, additional information about the picked point is available in the target.index sub-attribute:

  • For Line2Ds, it contains the index of the selected point (see Selection indices for more details, especially regarding step plots).
  • For LineCollections, it contains a pair: the index of the selected line, and the index within the line, as defined above.

Thus, in order to customize, e.g., the annotation text, one can call:

lines = ax.plot(range(3), range(3), "o")
labels = ["a", "b", "c"]
cursor = mplcursors.cursor(lines)
cursor.connect(
    "add", lambda sel: sel.annotation.set_text(labels[sel.target.index]))

Whenever a point is selected (resp. deselected), the "add" (resp. "remove") event is triggered and the registered callbacks are executed, with the Selection as only argument. Here, the only callback updates the text of the annotation to a per-point label. (cursor.connect("add") can also be used as a decorator to register a callback, see below for an example.) For an example using pandasDataFrames, see examples/dataframe.py.

For additional customizations of the position and appearance of the annotation, see examples/bar_example.py and examples/change_popup_color.py.

Callbacks can also be used to make additional changes to the figure when a selection occurs. For example, the following snippet (extracted from examples/multi_highlight_example.py) ensures that whenever an artist is selected, another artist that has been “paired” with it (via the pairs map) also gets selected:

@cursor.connect("add")
def on_add(sel):
    sel.extras.append(cursor.add_highlight(pairs[sel.artist]))

Note that the paired artist will also get de-highlighted when the “first” artist is deselected.

Selection indices

When picking a point on a “normal” line, the target index has an integer part equal to the index of segment it is on, and a fractional part that indicates where the point is within that segment.

Such an approach does not make sense for step plots (i.e., created by plt.step or plt.plot(..., drawstyle="steps-..."). In this case, we return a special Index object, with attributes int (the segment index), x (how far the point has advanced in the x direction) and y (how far the point has advanced in the y direction). See examples/step.py for an example.

Complex plots

Some complex plots, such as contour plots, may be partially supported, or not at all. Typically, it is because they do not subclass Artist, and thus appear to cursor as a collection of independent artists (each contour level, in the case of coutour plots).

It is usually possible, again, to hook the "add" signal to provide additional information in the annotation text. See examples/coutour.py for an example.

Indices and tables