I have created an interactive plot using matplotlib
and following this answer. The plot uses sliders in order to allow the user to control the vertical (y-axis) locations of the points and fits a spline to them as they are changed. The code to accomplish this is here:
import matplotlib.animation as animation
from matplotlib.widgets import Slider
import matplotlib as mpl
from matplotlib import pyplot as plt
import scipy.interpolate as inter
import numpy as np
func = lambda x: 0.1*x**2
#get a list of points to fit a spline to as well
N = 10
xmin = 0
xmax = 10
x = np.linspace(xmin,xmax,N)
#spline fit
yvals = func(x)
spline = inter.InterpolatedUnivariateSpline (x, yvals)
#figure.subplot.right
mpl.rcParams['figure.subplot.right'] = 0.8
#set up a plot
fig,axes = plt.subplots(1,1,figsize=(9.0,8.0),sharex=True)
ax1 = axes
interval = 100 # ms, time between animation frames
loop_len = 5.0 # seconds per loop
scale = interval / 1000 / loop_len
def update(val):
# update curve
for i in np.arange(N):
yvals[i] = sliders[i].val
l.set_ydata(yvals)
spline = inter.InterpolatedUnivariateSpline (x, yvals)
m.set_ydata(spline(X))
# redraw canvas while idle
fig.canvas.draw_idle()
X = np.arange(0,xmax+1,0.1)
ax1.plot (X, func(X), 'k--', label='original')
l, = ax1.plot (x,yvals,color='k',linestyle='none',marker='o',markersize=8)
m, = ax1.plot (X, spline(X), 'r-', label='spline')
ax1.set_yscale('linear')
ax1.set_xlim(0, xmax)
ax1.set_ylim(0,xmax)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.grid(True)
ax1.yaxis.grid(True,which='minor',linestyle='--')
ax1.legend(loc=2,prop={'size':22})
sliders = []
for i in np.arange(N):
axamp = plt.axes([0.84, 0.8-(i*0.05), 0.12, 0.02])
# Slider
s = Slider(axamp, 'p{0}'.format(i), 0, 10, valinit=yvals[i])
sliders.append(s)
for i in np.arange(N):
#samp.on_changed(update_slider)
sliders[i].on_changed(update)
plt.show()
I think the user interface would be better if one could directly click on the points and drag them vertically upward or downward. Which features of the matplotlib
interactive code would be most helpful to do this? I have looked at the various widgets examples, but have not found anything that is easy for me to see as directly useful for this. The closest seems to be the rectangle selector, but I am unsure how to adapt it to get what I need.
EDIT:
This answer can probably be adapted to do what I need. The difference between this and exactly what I need is that the example picks a point (based on mouse click and drag) along an existing line and highlights it. I need to click and select which existing point is within a small range (if none, then the dragging should do nothing). Then the code should change that point as it is dragged and refit the spline during that dragging action. I may be able to do this with the above example because it let me understand better how to use mpl_connect
for different actions (and gave some example actions.
The final code should only allow dragging of points vertically, but I think that constraint will be easy to build in (i.e. only use y-coordinate of mouse click/release).
EDIT 2:
The Poly Editor example seems to be very close to what I need, I can develop off of that.