[英]dragging points in matplotlib interactive plot
我已經使用matplotlib
並按照這個答案創建了一個交互式繪圖。 該圖使用滑塊以允許用戶控制點的垂直(y 軸)位置,並在它們發生變化時為它們擬合樣條曲線。 完成此操作的代碼在這里:
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()
我認為如果可以直接點擊這些點並垂直向上或向下拖動它們,用戶界面會更好。 matplotlib
交互式代碼的哪些功能最有助於做到這一點? 我查看了各種小部件示例,但沒有發現任何對我來說很容易看到的直接有用的東西。 最接近的似乎是矩形選擇器,但我不確定如何調整它以獲得我需要的。
編輯:
這個答案可能適合做我需要的事情。 這與我所需要的完全不同的是,該示例沿現有線選擇一個點(基於鼠標單擊和拖動)並突出顯示它。 我需要點擊並選擇哪個現有點在一個小范圍內(如果沒有,那么拖動應該什么都不做)。 然后代碼應該在拖動時更改該點,並在拖動操作期間重新調整樣條線。 我可以用上面的例子來做到這一點,因為它讓我更好地理解如何將mpl_connect
用於不同的動作(並給出了一些示例動作。
最終的代碼應該只允許垂直拖動點,但我認為這個約束很容易建立(即只使用鼠標單擊/釋放的 y 坐標)。
編輯2:
Poly Editor示例似乎非常接近我的需要,我可以從中開發。
好的,在很大程度上依賴於多邊形編輯器示例,我構建了一個示例,該示例可以使用滑塊更改點值或單擊並拖動點。
代碼如下,並注釋了診斷打印:
import matplotlib.animation as animation
from matplotlib.widgets import Slider, Button
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
pind = None #active point
epsilon = 5 #max pixel distance
def update(val):
global yvals
global spline
# 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()
def reset(event):
global yvals
global spline
#reset the values
yvals = func(x)
for i in np.arange(N):
sliders[i].reset()
spline = inter.InterpolatedUnivariateSpline (x, yvals)
l.set_ydata(yvals)
m.set_ydata(spline(X))
# redraw canvas while idle
fig.canvas.draw_idle()
def button_press_callback(event):
'whenever a mouse button is pressed'
global pind
if event.inaxes is None:
return
if event.button != 1:
return
#print(pind)
pind = get_ind_under_point(event)
def button_release_callback(event):
'whenever a mouse button is released'
global pind
if event.button != 1:
return
pind = None
def get_ind_under_point(event):
'get the index of the vertex under point if within epsilon tolerance'
# display coords
#print('display x is: {0}; display y is: {1}'.format(event.x,event.y))
t = ax1.transData.inverted()
tinv = ax1.transData
xy = t.transform([event.x,event.y])
#print('data x is: {0}; data y is: {1}'.format(xy[0],xy[1]))
xr = np.reshape(x,(np.shape(x)[0],1))
yr = np.reshape(yvals,(np.shape(yvals)[0],1))
xy_vals = np.append(xr,yr,1)
xyt = tinv.transform(xy_vals)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.hypot(xt - event.x, yt - event.y)
indseq, = np.nonzero(d == d.min())
ind = indseq[0]
#print(d[ind])
if d[ind] >= epsilon:
ind = None
#print(ind)
return ind
def motion_notify_callback(event):
'on mouse movement'
global yvals
if pind is None:
return
if event.inaxes is None:
return
if event.button != 1:
return
#update yvals
#print('motion x: {0}; y: {1}'.format(event.xdata,event.ydata))
yvals[pind] = event.ydata
# update curve via sliders and draw
sliders[pind].set_val(yvals[pind])
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)
axres = plt.axes([0.84, 0.8-((N)*0.05), 0.12, 0.02])
bres = Button(axres, 'Reset')
bres.on_clicked(reset)
fig.canvas.mpl_connect('button_press_event', button_press_callback)
fig.canvas.mpl_connect('button_release_event', button_release_callback)
fig.canvas.mpl_connect('motion_notify_event', motion_notify_callback)
plt.show()
我沒有像Poly Editor示例那樣將代碼封裝到一個對象中,但這應該是一個很容易做到的升級。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.