Sam
Sam

Reputation: 1327

Python Matplotlib show the cursor when hovering on graph

I want to plot the close price of INS as below and it works. Then I want to add the cursor when hovering on the graph. I follow the demo from https://matplotlib.org/3.1.0/gallery/misc/cursor_demo_sgskip.html and that is what I want.

enter image description here

But when I added these lines into the code, it shows value error. Initially I use epoch time as x-axis and I thought that is the problem, so I convert epoch time to datetime but it is still not working and plot nothing.

snap_cursor = SnaptoCursor(ax, secs, df['close'])
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.mouse_move)

Traceback (most recent call last): File "c:\Users\Sam.vscode\extensions\ms-python.python-2020.6.89148\pythonFiles\ptvsd_launcher.py", line 48, in main(ptvsdArgs) File "c:\Users\Sam.vscode\extensions\ms-python.python-2020.6.89148\pythonFiles\lib\python\old_ptvsd\ptvsd_main_.py", line 432, in main run() File "c:\Users\Sam.vscode\extensions\ms-python.python-2020.6.89148\pythonFiles\lib\python\old_ptvsd\ptvsd_main_.py", line 316, in run_file runpy.run_path(target, run_name='main') File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\runpy.py", line 263, in run_path pkg_name=pkg_name, script_name=fname) File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\runpy.py", line 96, in _run_module_code mod_name, mod_spec, pkg_name, script_name) File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\runpy.py", line 85, in _run_code exec(code, run_globals) File "c:\Users\Sam\OneDrive\Project\stock\test.py", line 89, in plt.gcf().autofmt_xdate() File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\figure.py", line 632, in autofmt_xdate for label in self.axes[0].get_xticklabels(which=which): File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axes_base.py", line 3355, in get_xticklabels return self.xaxis.get_ticklabels(minor=minor, which=which) File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axis.py", line 1320, in get_ticklabels return self.get_majorticklabels() File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axis.py", line 1276, in get_majorticklabels File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axis.py", line 1431, in get_major_ticks numticks = len(self.get_majorticklocs()) File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axis.py", line 1348, in get_majorticklocs return self.major.locator() File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\dates.py", line 1338, in call self.refresh() File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\dates.py", line 1364, in refresh dmin, dmax = self.viewlim_to_dt() File "C:\Users\Sam\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\dates.py", line 1098, in viewlim_to_dt .format(vmin)) ValueError: view limit minimum -36879.777083333334 is less than 1 and is an invalid Matplotlib date value. This often happens if you pass a non-datetime value to an axis that has datetime units

class SnaptoCursor(object):
    """
    Like Cursor but the crosshair snaps to the nearest x, y point.
    For simplicity, this assumes that *x* is sorted.
    """

    def __init__(self, ax, x, y):
        self.ax = ax
        self.lx = ax.axhline(color='k')  # the horiz line
        self.ly = ax.axvline(color='k')  # the vert line
        self.x = x
        self.y = y
        # text location in axes coords
        self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes)

    def mouse_move(self, event):
        if not event.inaxes:
            return

        x, y = event.xdata, event.ydata
        indx = min(np.searchsorted(self.x, x), len(self.x) - 1)
        x = self.x[indx]
        y = self.y[indx]
        # update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        print('x=%1.2f, y=%1.2f' % (x, y))
        self.ax.figure.canvas.draw()

data = td.pricehistory("INS")
df = pd.DataFrame(data['candles'])
df['datetime'] = df.apply(lambda x: datetime.datetime.fromtimestamp(x['datetime']/1000),axis=1)
secs = df['datetime']
fig, ax = plt.subplots(1, 1)  
ax.plot(secs,df['close']) 

snap_cursor = SnaptoCursor(ax, secs, df['close'])
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.mouse_move)

plt.gcf().autofmt_xdate()
myFmt = mdates.DateFormatter('%d-%m-%y %H:%M:%S')
plt.gca().xaxis.set_major_formatter(myFmt)

plt.show()

Upvotes: 1

Views: 3063

Answers (1)

certiprince
certiprince

Reputation: 68

I'm not familiar with SnaptoCursor, but have you considered using plotly instead?

Saves many lines of code and is very user-friendly:

# install plotly library
!pip install plotly

# import express module
import plotly.express as px

# plot interactive line chart
fig = px.line(data=df, x="datetime", y="close")
fig.show()

It's very flexible too. Here is the documentation: https://plotly.com/python/line-charts/

You can interact with the plots using the cursor too, for example: enter image description here

Hope this helps, though of course it's different to what you were trying to do!

Upvotes: 1

Related Questions