show label on hover over vertically spanned area in matplotlib - python-3.x

when I hover over spanned region, labels are being showed only along the sides of spanned area but not through out the area.
I want the label to be viewed in the whole area when I hover in it. How can I implement this logic?
import matplotlib.pyplot as plt
import mplcursors
plt.axvspan(2,3,gid='yes',alpha=0.3,label = 'y')
mplcursors.cursor(hover=True).connect(
"add", lambda sel: sel.annotation.set_text(sel.artist.get_label()))
plt.show()

I don't know why mplcursors does not work in the code from the question; but here is how to show an annotation upon hovering an axes span (without mplcursors):
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
span = ax.axvspan(2,3,gid='yes',alpha=0.3,label = 'y')
annot = ax.annotate("Y", xy=(0,0), xytext=(20,20), textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = span.contains(event)
if cont:
annot.xy = (event.xdata, event.ydata)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()

Related

Matplotlib Scatter plot interactivity not working

Until this morning I was able to display labels information when hovering the dots on a scatter plot.
Now, if I run the following code it does not display any error but the interactivity is not working and it looks like mplconnect or mlpcursors are completely ignored.
I've tried the same code under windows and Fedora.
Not understanding what's going on.
from matplotlib.pyplot import figure, show
import numpy as npy
from numpy.random import rand
x, y, c, s = rand(4, 100)
def onpick3(event):
ind = event.ind
print('onpick3 scatter:', ind, npy.take(x, ind), npy.take(y, ind))
fig = figure()
ax1 = fig.add_subplot(111)
col = ax1.scatter(x, y, 100*s, c, picker=True)
#fig.savefig('pscoll.eps')
fig.canvas.mpl_connect('pick_event', onpick3)
show()
Or
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
x = np.random.rand(15)
y = np.random.rand(15)
names = np.array(list("ABCDEFGHIJKLMNO"))
c = np.random.randint(1,5,size=15)
norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn
fig,ax = plt.subplots()
sc = plt.scatter(x,y,c=c, s=100, cmap=cmap, norm=norm)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
pos = sc.get_offsets()[ind["ind"][0]]
annot.xy = pos
text = "{}, {}".format(" ".join(list(map(str,ind["ind"]))),
" ".join([names[n] for n in ind["ind"]]))
annot.set_text(text)
annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
annot.get_bbox_patch().set_alpha(0.4)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = sc.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()
This is not my code, I've copied and pasted it from a website but the behavior is the same.
Plotly express solves the problem
import plotly.express as px
alpha = data[data['Ticker']==focus].V1
gamma = data[data['Ticker']==focus].V2
fig = px.scatter(data, x='V1', y='V2', color=Colors.Market_Cap, hover_data=["Ticker"] )
fig.add_shape(type="circle",
xref="x", yref="y",
x0=int(alpha-3), y0=int(gamma-3), x1=int(alpha+3), y1=int(gamma+3),
line_color="LightSeaGreen",
)
fig.show()

How to connect matplotlib cursor mouse_move object with slider value?

I have a figure where 2 axhlines move with mouse movement. I want to put a slider at the bottom where it will change the range of y-axis values covered by these axhlines.
enter image description here
I tried the following code. Problem is that the value of the slider changes but the mouse event object does not update.
Thanks
%matplotlib notebook
import pandas as pd
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.widgets import Slider
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000,200000,3650),
np.random.normal(43000,100000,3650),
np.random.normal(43500,140000,3650),
np.random.normal(48000,70000,3650)],
index=[1992,1993,1994,1995])
df=df.T
data=df.describe().T
data['error']=df.sem()
data['error_range']=df.sem()*1.96
fig, ax = plt.subplots()
def plot_bar(x,y,error,title,alpha_level=0.7):
ax.bar(x,y, yerr=error,
align='center', alpha=alpha_level,
error_kw=dict(ecolor='black', elinewidth=1, capsize=5, capthick=1))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_title(title)
ax.xaxis.set_major_locator(ticker.FixedLocator(data.index))
ax.yaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
ax.set_ylim([-5000,55000])
ax.set_xlim([1990.5,1995.5])
ax.spines['bottom'].set_position(('data',0))
ax.spines['left'].set_position(('data',1991.4))
plt.tight_layout()
return (ax, ax.get_children()[1:5])
ax, barlist=plot_bar(x=data.index,y=data['mean'],error=data['error_range'],title='Even Harder Option', alpha_level=0.6)
fig.subplots_adjust(bottom=0.1)
axcolor = 'lightgoldenrodyellow'
range_slider = plt.axes([0.2, 0.05, 0.65, 0.03], facecolor=axcolor)
slider = Slider(range_slider, 'Range', 0, 55000, valinit=10000, valstep=100)
def update(val):
slider.val = slider.val
slider.on_changed(update)
class Cursor(object):
_df=None
_bl=None
def __init__(self, ax,data_F, bars, slider):
#global slider
self._df=data_F
self._bl=bars
self.ax = ax
self.lx1 = ax.axhline(color='b')
self.lx2 = ax.axhline(color='b')
self.text1 = ax.text(1990.55, y, '%d' %45,bbox=dict(fc='white',ec='k'), fontsize='x-small')
self.text2 = ax.text(1990.55, y, '%d' %45,bbox=dict(fc='white',ec='k'), fontsize='x-small')
self._sl = slider.val
def mouse_move(self, event):
if not event.inaxes:
return
x, y = event.xdata, event.ydata
r = self._sl
y1 , y2 = y+r/2 , y-r/2
#self.lx1.set_ydata(y)
self.lx1.set_ydata(y+r/2)
self.lx2.set_ydata(y-r/2)
for i in range(4):
#shade = cmap(norm((data['mean'.values[i]-event.ydata)/df_std.values[i]))
prob1=stats.norm.cdf(y1,self._df['mean'].values[i],self._df['error'].values[i])
prob2=stats.norm.cdf(y2,self._df['mean'].values[i],self._df['error'].values[i])
shade = cmap(prob1-prob2)
self._bl[i].set_color(shade)
self.text1.set_text('%d' %y1)
self.text1.set_position((1990.55, y1))
self.text2.set_text('%d' %y2)
self.text2.set_position((1990.55, y2))
plt.draw()
cursor = Cursor(ax, data,barlist, slider)
#plt.connect('range_change', cursor.update)
plt.connect('motion_notify_event', cursor.mouse_move)

Tkinter Not Showing Animated Matplotlib

I want to show this matplotlib real-time graph in tkinter GUI
Real-time Graph
import tkinter as tk
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root= tk.Tk()
style.use('fivethirtyeight')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
graph = open('data.txt','r').read()
lines = graph.split('\n')
xs = []
ys = []
zs = []
for line in lines:
if len(line) > 1:
x, y, z = line.split(',')
xs.append(float(x))
ys.append(float(y))
zs.append(float(z))
ax1.clear()
ax1.plot(xs, ys, zs)
anim = animation.FuncAnimation(fig, animate, interval=1000)
app = (fig, root)
root.mainloop()
I tried the code above, but the GUI isn't show anything. What can I do to show that real-time graph?
Sorry, my bad. I updated my code in this line:
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
With this line:
fig = plt.figure(figsize=(5,4), dpi=100)
ax1 = fig.add_subplot(111)
line2 = FigureCanvasTkAgg(fig, root)
line2.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
I got my answer now.

Add a point to a plot with a mouse event

I want to click on my graph and add a point to it. I am using event handling, but the append command is not working.
My Code:
from matplotlib import pyplot as plt
class LineBuilder:
def __init__(self, line):
self.line = line
self.xs = line.get_xdata()
self.ys = line.get_ydata()
self.cid = line.figure.canvas.mpl_connect('button_press_event', self.click)
def click(self, event):
if event.inaxes != self.line.axes:
return
self.xs.append(event.xdata)
self.ys.append(event.ydata)
self.line.set_data(self.xs, self.ys)
self.line.figure.canvas.draw()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click to add a point')
line, = ax.plot([], [], 'o') # empty point
linebuilder = LineBuilder(line)
plt.show()
My Error:
numpy.ndarray' object has no attribute 'append

How to embed a matplotlib plot with annotation inside Gtk3 window

I trying a lot to place a matplotlib plot (that also uses annotation) to place inside gtk3 window. If there is no annotation, I can place the plot easily inside gtk3. But, I have messed up with how to do it while using annotation. I have tried this without much progress.
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from numpy import arange, pi, random, linspace
import matplotlib.cm as cm
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
#WINDOW to embede matplotlib
x = np.random.rand(15)
y = np.random.rand(15)
p_window = Gtk.Window()
p_window.set_default_size(750,500)
p_header = Gtk.HeaderBar()
p_window.set_titlebar(p_header)
p_header.set_subtitle("Periodic Table")
p_header.set_title("column")
p_header.set_show_close_button(True)
c = np.random.randint(1,50,size=120)
norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn
fig,ax = plt.subplots()
sc = plt.scatter(x,y)
plt.plot(x,y)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
pos = sc.get_offsets()[ind["ind"][0]]
annot.xy = pos
namel = "foo"
vall = "bar"
text = "{}, {}".format(namel[2:-2], vall[1:-1])
annot.set_text(text)
# annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
annot.get_bbox_patch().set_alpha(0.4)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = sc.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()
sw = Gtk.ScrolledWindow()
p_window.add(sw)
canvas = FigureCanvas(fig)
canvas.set_size_request(400,400)
sw.add_with_viewport(canvas)
p_window.show_all()
Gtk.main()
After removing pyplt
I have remove pyplt dependency as suggested, and got this:
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import numpy as np
# import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from numpy import arange, pi, random, linspace
import matplotlib.cm as cm
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
#WINDOW to embede matplotlib
x = np.random.rand(15)
y = np.random.rand(15)
p_window = Gtk.Window()
p_window.set_default_size(750,500)
p_header = Gtk.HeaderBar()
p_window.set_titlebar(p_header)
p_header.set_subtitle("Periodic Table")
p_header.set_title("column")
p_header.set_show_close_button(True)
c = np.random.randint(1,50,size=120)
fig = Figure(figsize=(10,6), dpi=100)
ax = fig.add_subplot(111)
ax.set_ylabel("column")
ax.set_xlabel("Z")
sc=ax.plot(x,y, "r-o")
print(type(fig))
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
pos = sc.get_offsets()[ind["ind"][0]]
annot.xy = pos
namel = "foo"
vall = "bar"
text = "{}, {}".format(namel[2:-2], vall[1:-1])
print(text)
annot.set_text(text)
# annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
annot.get_bbox_patch().set_alpha(0.4)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = sc.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
sw = Gtk.ScrolledWindow()
p_window.add(sw)
canvas = FigureCanvas(fig)
canvas.set_size_request(400,400)
fig.canvas.mpl_connect("motion_notify_event", hover)
sw.add_with_viewport(canvas)
p_window.show_all()
Gtk.main()
which is giving me error:
python3 pop.py
Traceback (most recent call last):
File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_gtk3.py", line 268, in motion_notify_event
FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
File "/usr/lib64/python3.6/site-packages/matplotlib/backend_bases.py", line 1958, in motion_notify_event
self.callbacks.process(s, event)
File "/usr/lib64/python3.6/site-packages/matplotlib/cbook.py", line 549, in process
proxy(*args, **kwargs)
File "/usr/lib64/python3.6/site-packages/matplotlib/cbook.py", line 416, in __call__
return mtd(*args, **kwargs)
File "pop.py", line 51, in hover
cont, ind = sc.contains(event)
AttributeError: 'list' object has no attribute 'contains'

Resources