I am attempting to store coordinates from two mouse clicks in an interactive matplotlib graph in JupyterLab. I have managed to interact with the plot and get the x and y coordinates to print upon clicking. However, when I try to store those coordinates by appending to a list or numpy array (tried both), the values are not saving in my global variable coords. Since I can get the coordinates for one click successfully as ix and iy, I suppose I could have duplicate cells each with one click event each, but I'd prefer to have a single cell that can store both clicks' coordinates.
My original code was taken from this question. I have had to make a few adjustments to work with ipympl and ipywidgets.
Specifically, coords prints as empty even though my if statement regarding its length does seem to terminate the click event. Maybe I am missing something obvious (I am relatively new to Python from a MATLAB background). I'd appreciate any advice or alternative approaches to the issue.
%matplotlib ipympl
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy import stats
import os
import ipywidgets as widgets
out = widgets.Output()
coords = []
fig, ax = plt.subplots(figsize =(4, 4))
ax.plot(time,temp,'k-',linewidth = 1.5)
ax.set_xlabel("Time (sec)")
ax.set_ylabel(r"Temperature ($\degree$ C)")
ax.tick_params(axis="both",direction='in',top=True, right=True)
plt.show()
@out.capture()
def onclick(event):
ix, iy = event.xdata, event.ydata
print ('x = ',ix, 'y = ',iy)
global coords
coords.append(ix)
coords.append(iy)
# Disconnect after 2 clicks
if len(coords) == 4:
fig.canvas.mpl_disconnect(cid)
return coords
cid = fig.canvas.mpl_connect('button_press_event', onclick)
display(out)
print(coords)
image of interactive figure and printed coordinates, for reference
Code in Jupyter (and other GUIs) doesn't work as you probably expect - code print(coords)
in last line is execute at start, before you click anything, and system will NOT update this when you append values to coords
.
If you use print(coords)
inside def onclick()
then you should see that there are values on this list. And these values are in global variable.
If you need it use it at once then do it directly in onclick()
.
If you need it use little later then better create some button to execute code which you will click to execute code when you will already have both values.
To displaying text you can also use w = widgets.Label(value="text")
and later you can replace text in this widget using w.value = "new text"
%matplotlib ipympl
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy import stats
import os
import ipywidgets as widgets
out = widgets.Output()
coords = []
time = [1,2,3,4,5]
temp = [1,3,2,5,1]
fig, ax = plt.subplots(figsize =(4, 4))
ax.plot(time,temp,'k-',linewidth = 1.5)
ax.set_xlabel("Time (sec)")
ax.set_ylabel(r"Temperature ($\degree$ C)")
ax.tick_params(axis="both",direction='in',top=True, right=True)
plt.show()
@out.capture()
def onclick(event):
global coords # PEP8: globals always at the beginning of function
ix, iy = event.xdata, event.ydata
print('x = ',ix, 'y = ',iy)
coords.append(ix)
coords.append(iy)
#print(coords) # display new text but it doesn't remove previous text
text.value = f'coord: {coords}' # update existing Label (it needs string) and remove previous text
# Disconnect after 2 clicks
if len(coords) == 4:
fig.canvas.mpl_disconnect(cid)
# ... here you could use coords in calculations ...
#return coords
cid = fig.canvas.mpl_connect('button_press_event', onclick)
text = widgets.Label(value=f'coord: {coords}') # create widget which can be updated later
display(text, out)
#print(coords) # this is executed before you click and later it will not update it.