4

This picture shows what I am trying to achieve:

I am searching for a solution to add a cursor to my plotted line in matplotlib. The cursor should be draggable, but should only move on the plotted line. A label shall display the actual value of the marked point on the trace.

I don't have an idea which object to use as this cursor/marker.

eyllanesc
  • 221,139
  • 17
  • 121
  • 189
MartinaW
  • 149
  • 1
  • 3
  • 7

2 Answers2

5

There is an example on the matplotlib page, which you may adapt to show a point at the position of interest.

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



class SnaptoCursor(object):
    def __init__(self, ax, x, y):
        self.ax = ax
        self.ly = ax.axvline(color='k', alpha=0.2)  # the vert line
        self.marker, = ax.plot([0],[0], marker="o", color="crimson", zorder=3) 
        self.x = x
        self.y = y
        self.txt = ax.text(0.7, 0.9, '')

    def mouse_move(self, event):
        if not event.inaxes: return
        x, y = event.xdata, event.ydata
        indx = np.searchsorted(self.x, [x])[0]
        x = self.x[indx]
        y = self.y[indx]
        self.ly.set_xdata(x)
        self.marker.set_data([x],[y])
        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        self.txt.set_position((x,y))
        self.ax.figure.canvas.draw_idle()

t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*2*np.pi*t)
fig, ax = plt.subplots()

#cursor = Cursor(ax)
cursor = SnaptoCursor(ax, t, s)
cid =  plt.connect('motion_notify_event', cursor.mouse_move)

ax.plot(t, s,)
plt.axis([0, 1, -1, 1])
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 289,005
  • 45
  • 571
  • 615
  • Great, Thank you! But how to add this cursor onto a line which is already plotted? I cannot find a solution to draw the cursor afterwards... – MartinaW Jun 22 '17 at 11:28
  • In the code above the Cursor **is** added afterwards. What exactly do you mean? – ImportanceOfBeingErnest Jun 22 '17 at 11:36
  • You're rigth! Thanks a lot! The solution works fine for me! – MartinaW Jun 22 '17 at 13:22
  • `indx = min(np.searchsorted(self.x, [x])[0], len(self.x) - 1)` prevents an out-of-range error for the last point. For people wishing to use this for other data, `plt.axis([0, 1, -1, 1])` needs to be changed (or removed) depending on the data ranges. – JohanC Sep 14 '20 at 14:17
0

look here : Is there a matplotlib equivalent of MATLAB's datacursormode?

import matplotlib.pyplot as plt

class DataCursor(object):
    text_template = 'x: %0.2f\ny: %0.2f'
    x, y = 0.0, 0.0
    xoffset, yoffset = -20, 20
    text_template = 'x: %0.2f\ny: %0.2f'

    def __init__(self, ax):
        self.ax = ax
        self.annotation = ax.annotate(self.text_template, 
                xy=(self.x, self.y), xytext=(self.xoffset, self.yoffset), 
                textcoords='offset points', ha='right', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')
                )
        self.annotation.set_visible(False)

    def __call__(self, event):
        self.event = event
        # xdata, ydata = event.artist.get_data()
        # self.x, self.y = xdata[event.ind], ydata[event.ind]
        self.x, self.y = event.mouseevent.xdata, event.mouseevent.ydata
        if self.x is not None:
            self.annotation.xy = self.x, self.y
            self.annotation.set_text(self.text_template % (self.x, self.y))
            self.annotation.set_visible(True)
            event.canvas.draw()

fig = plt.figure()
line, = plt.plot(range(10), 'ro-')
fig.canvas.mpl_connect('pick_event', DataCursor(plt.gca()))
line.set_picker(5) # Tolerance in points
plt.show()
ymmx
  • 4,302
  • 5
  • 23
  • 60