Store mouse click event coordinates with matplotlib
Solution 1
mpl_connect needs to be called just once to connect the event to event handler. It will start listening to click event until you disconnect. And you can use
fig.canvas.mpl_disconnect(cid)
to disconnect the event hook.
What you want to do is something like:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10)
y = x**2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y)
coords = []
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
print 'x = %d, y = %d'%(
ix, iy)
global coords
coords.append((ix, iy))
if len(coords) == 2:
fig.canvas.mpl_disconnect(cid)
return coords
cid = fig.canvas.mpl_connect('button_press_event', onclick)
Solution 2
Thanks to otterb for providing the answer! I've added in a little function taken from here... Find nearest value in numpy array
In all this code will plot, wait for selection of x points and then return the indices of the x array needed for any integration, summations etc.
Ta,
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import trapz
def find_nearest(array,value):
idx = (np.abs(array-value)).argmin()
return array[idx]
# Simple mouse click function to store coordinates
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
# print 'x = %d, y = %d'%(
# ix, iy)
# assign global variable to access outside of function
global coords
coords.append((ix, iy))
# Disconnect after 2 clicks
if len(coords) == 2:
fig.canvas.mpl_disconnect(cid)
plt.close(1)
return
x = np.arange(-10,10)
y = x**2
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(x,y)
coords = []
# Call click func
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show(1)
# limits for integration
ch1 = np.where(x == (find_nearest(x, coords[0][0])))
ch2 = np.where(x == (find_nearest(x, coords[1][0])))
# Calculate integral
y_int = trapz(y[ch1[0][0]:ch2[0][0]], x = x[ch1[0][0]:ch2[0][0]])
print ''
print 'Integral between '+str(coords[0][0])+ ' & ' +str(coords[1][0])
print y_int
Solution 3
I want to provide a different answer here since I recently tried to do event handling but the soulutions here do not distinguish between zooming, paning and clicking, everything gets messed up in my case. I found an extention for matplotlib called mpl_point_clicker that works really well for me and can be installed with pip (with python 3.X). Here is the basic usage from their documentation:
import numpy as np
import matplotlib.pyplot as plt
from mpl_point_clicker import clicker
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(np.sin(np.arange(200)/(5*np.pi)))
klicker = clicker(ax, ["event"], markers=["x"])
plt.show()
print(klicker.get_positions())
The figure with 3 clicks and the output look like this
Output:
{'event': array([[ 24.22415481, 1.00237796],
[ 74.19892948, -0.99140661],
[123.23078387, 1.00237796]])}
Related videos on Youtube
smashbro
Updated on July 09, 2022Comments
-
smashbro almost 2 years
I am trying to implement a simple mouse click event in matplotlib. I wish to plot a figure then use the mouse to select the lower and upper limits for integration. So far I am able to print the coordinates to screen but not store them for later use in the program. I would also like to exit the connection to the figure after the second mouse click.
Below is the code which currently plots and then prints the coordinates.
My Question(s):
How can I store coordinates from the figure to list? i.e. click = [xpos, ypos]
Is it possible to get two sets of x coordinates in order to do a simple integration over that section of line?
import numpy as np import matplotlib.pyplot as plt x = np.arange(-10,10) y = x**2 fig = plt.figure() ax = fig.add_subplot(111) ax.plot(x,y) def onclick(event): global ix, iy ix, iy = event.xdata, event.ydata print 'x = %d, y = %d'%( ix, iy) global coords coords = [ix, iy] return coords for i in xrange(0,1): cid = fig.canvas.mpl_connect('button_press_event', onclick) plt.show()
-
tacaswell almost 10 yearsYou don't need to return
coords
, the value goes no where. it gets stored via closure over the global variable. -
otterb almost 10 years@tcaswell i just did a minimum change to the code. But, what is closure here?
-
smashbro almost 10 yearsExcellent thanks very much. I'll update the question with my solution which is a slightly tweaked version of yours.
-
G M over 7 yearsIt doesn't work with matplotlib 2.0 it crashes when I clik
-
Sigur over 6 yearsHow to add a button to disconnect, instead of test if coords has len two?
-
eduardosufan almost 6 yearsWhy you use global as attibute types inside onclick function?
-
Nisha over 5 years@otterb I tried to implement this method, but even when I click the close button, it guesses it just like a normal mouse click. I will like it to know when it is clicking inside the graph window, how to do it?
-
TimLanger over 3 years@eduardosufan: This is the principle how global Python variables work: You declare them at the top (global scope, no indent) as a 'normal' variable. If you use them in a method, you have to tell the method that you really want to access the global variable, otherwise it will create a local variable with the same name. In the local case, the coordinates would not be registered correctly. I know, quite other way round than in other languages, definitely caused me some headache - but actually good concept if you think about it twice :D
-
zabop over 2 yearsAwesome answer. A related question: stackoverflow.com/questions/70947912/…