Python matplotlib中的交互行
我正在尝试使用matplotlib创建一个交互式绘图,该绘图在端点处创建一个具有两个控制柄的线段。您可以单击并拖动控制柄,直线将刷新以匹配以这种方式指定的位置,方式与此matplotlib示例类似:(如果您看到此示例,请想象我想要相同的东西,但只有多边形的一条边) 我已经尝试修改poly_编辑器代码,使其只处理Line2D元素,我的程序运行时没有任何错误,只是它根本没有在轴上绘制任何东西。我认为这可能是变量范围中的错误,或者与matplotlib的draw调用有关。如能提供有关错误的任何指导,我们将不胜感激 编辑:我进一步改进了一些,简化了代码,现在我可以让它画线并打印epsilon距离内最近顶点的索引,但线保持静止,不动。更新后的代码如下Python matplotlib中的交互行,python,matplotlib,Python,Matplotlib,我正在尝试使用matplotlib创建一个交互式绘图,该绘图在端点处创建一个具有两个控制柄的线段。您可以单击并拖动控制柄,直线将刷新以匹配以这种方式指定的位置,方式与此matplotlib示例类似:(如果您看到此示例,请想象我想要相同的东西,但只有多边形的一条边) 我已经尝试修改poly_编辑器代码,使其只处理Line2D元素,我的程序运行时没有任何错误,只是它根本没有在轴上绘制任何东西。我认为这可能是变量范围中的错误,或者与matplotlib的draw调用有关。如能提供有关错误的任何指导,我
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.lines import Line2D
class LineBuilder(object):
epsilon = 0.5
def __init__(self, line):
canvas = line.figure.canvas
self.canvas = canvas
self.line = line
self.axes = line.axes
self.xs = list(line.get_xdata())
self.ys = list(line.get_ydata())
self.ind = None
canvas.mpl_connect('button_press_event', self.button_press_callback)
canvas.mpl_connect('button_release_event', self.button_release_callback)
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
def get_ind(self, event):
x = np.array(self.line.get_xdata())
y = np.array(self.line.get_ydata())
d = np.sqrt((x-event.xdata)**2 + (y - event.ydata)**2)
if min(d) > self.epsilon:
return None
if d[0] < d[1]:
return 0
else:
return 1
def button_press_callback(self, event):
if event.button != 1:
return
self.ind = self.get_ind(event)
print(self.ind)
self.line.set_animated(True)
self.canvas.draw()
self.background = self.canvas.copy_from_bbox(self.line.axes.bbox)
self.axes.draw_artist(self.line)
self.canvas.blit(self.axes.bbox)
def button_release_callback(self, event):
if event.button != 1:
return
self.ind = None
self.line.set_animated(False)
self.background = None
self.line.figure.canvas.draw()
def motion_notify_callback(self, event):
if event.inaxes != self.line.axes:
return
if event.button != 1:
return
if self.ind is None:
return
self.xs[self.ind] = event.xdata
self.ys[self.ind] = event.ydata
self.line.set_data(self.xs, self.ys)
self.canvas.restore_region(self.background)
self.axes.draw_artist(self.line)
self.canvas.blit(self.axes.bbox)
if __name__ == '__main__':
fig, ax = plt.subplots()
line = Line2D([0,1], [0,1], marker='o', markerfacecolor='red')
ax.add_line(line)
linebuilder = LineBuilder(line)
ax.set_title('click to create lines')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
plt.show()
导入matplotlib.pyplot作为plt
将numpy作为np导入
从matplotlib.lines导入Line2D
类LineBuilder(对象):
ε=0.5
定义初始化(自身,行):
canvas=line.figure.canvas
self.canvas=画布
self.line=line
self.axes=line.axes
self.xs=list(line.get_xdata())
self.ys=list(line.get_ydata())
self.ind=None
canvas.mpl\u connect('button\u press\u event',self.button\u press\u callback)
canvas.mpl\u connect('button\u release\u event',self.button\u release\u回调)
canvas.mpl\u connect('motion\u notify\u event',self.motion\u notify\u回调)
def get_ind(自身、事件):
x=np.array(self.line.get_xdata())
y=np.array(self.line.get_ydata())
d=np.sqrt((x-event.xdata)**2+(y-event.ydata)**2)
如果最小值(d)>self.epsilon:
一无所获
如果d[0]
事先谢谢你,凯文。好的,我解决了这个问题。新的代码(上面)实际上是有效的,其中有一个错误。motion notify事件的mpl_connect调用具有错误的事件类型,现在它正在按预期工作。我是新来的,希望通过回答此自我回答的问题不会犯很多错误。:) 首先感谢你发布这篇文章,它帮了我很多,节省了一些时间,我想要的几乎就是这段代码。我做了一些我在这里提出的更新,这样就可以操纵两个以上的点,并使用键处理事件来创建或删除线中的点,就像PolygonInteractor所做的那样
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import numpy as np
def dist(x, y):
"""
Return the distance between two points.
"""
d = x - y
return np.sqrt(np.dot(d, d))
def dist_point_to_segment(p, s0, s1):
"""
Get the distance of a point to a segment.
*p*, *s0*, *s1* are *xy* sequences
This algorithm from
http://geomalgorithms.com/a02-_lines.html
"""
v = s1 - s0
w = p - s0
c1 = np.dot(w, v)
if c1 <= 0:
return dist(p, s0)
c2 = np.dot(v, v)
if c2 <= c1:
return dist(p, s1)
b = c1 / c2
pb = s0 + b * v
return dist(p, pb)
class LineBuilder(object):
epsilon = 30 #in pixels
def __init__(self, line):
canvas = line.figure.canvas
self.canvas = canvas
self.line = line
self.axes = line.axes
self.xs = list(line.get_xdata())
self.ys = list(line.get_ydata())
self.ind = None
canvas.mpl_connect('button_press_event', self.button_press_callback)
canvas.mpl_connect('button_release_event', self.button_release_callback)
canvas.mpl_connect('key_press_event', self.key_press_callback)
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
def get_ind(self, event):
xy = np.asarray(self.line._xy)
xyt = self.line.get_transform().transform(xy)
x, y = xyt[:, 0], xyt[:, 1]
d = np.sqrt((x-event.x)**2 + (y - event.y)**2)
indseq, = np.nonzero(d == d.min())
ind = indseq[0]
if d[ind] >= self.epsilon:
ind = None
return ind
def button_press_callback(self, event):
if event.button != 1:
return
if event.inaxes is None:
return
self.ind = self.get_ind(event)
print(self.ind)
self.line.set_animated(True)
self.canvas.draw()
self.background = self.canvas.copy_from_bbox(self.line.axes.bbox)
self.axes.draw_artist(self.line)
self.canvas.blit(self.axes.bbox)
def button_release_callback(self, event):
if event.button != 1:
return
self.ind = None
self.line.set_animated(False)
self.background = None
self.line.figure.canvas.draw()
def motion_notify_callback(self, event):
if event.inaxes != self.line.axes:
return
if event.button != 1:
return
if self.ind is None:
return
self.xs[self.ind] = event.xdata
self.ys[self.ind] = event.ydata
self.line.set_data(self.xs, self.ys)
self.canvas.restore_region(self.background)
self.axes.draw_artist(self.line)
self.canvas.blit(self.axes.bbox)
def key_press_callback(self, event):
"""Callback for key presses."""
if not event.inaxes:
return
elif event.key == 'd':
ind = self.get_ind(event)
if ind is not None and len(self.xs) > 2:
self.xs = np.delete(self.xs, ind)
self.ys = np.delete(self.ys, ind)
self.line.set_data(self.xs, self.ys)
self.axes.draw_artist(self.line)
self.canvas.draw_idle()
elif event.key == 'i':
p = np.array([event.x, event.y]) # display coords
xy = np.asarray(self.line._xy)
xyt = self.line.get_transform().transform(xy)
for i in range(len(xyt) - 1):
s0 = xyt[i]
s1 = xyt[i+1]
d = dist_point_to_segment(p, s0, s1)
if d <= self.epsilon:
self.xs = np.insert(self.xs, i+1, event.xdata)
self.ys = np.insert(self.ys, i+1, event.ydata)
self.line.set_data(self.xs, self.ys)
self.axes.draw_artist(self.line)
self.canvas.draw_idle()
break
if __name__ == '__main__':
fig, ax = plt.subplots()
line = Line2D([0,0.5,1], [0,0.5,1], marker = 'o', markerfacecolor = 'red')
ax.add_line(line)
linebuilder = LineBuilder(line)
ax.set_title('click to create lines')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
plt.show()
从matplotlib.lines导入Line2D
将matplotlib.pyplot作为plt导入
将numpy作为np导入
def距离(x,y):
"""
返回两点之间的距离。
"""
d=x-y
返回np.sqrt(np.dot(d,d))
def dist_point_至_段(p、s0、s1):
"""
获取点到线段的距离。
*p*、*s0*、*s1*是*xy*序列
此算法来自
http://geomalgorithms.com/a02-_lines.html
"""
v=s1-s0
w=p-s0
c1=np.点(w,v)
如果c1 2:
self.xs=np.delete(self.xs,ind)
self.ys=np.delete(self.ys,ind)
self.line.set_数据(self.xs、self.ys)
self.axes.draw_艺术家(self.line)
self.canvas.draw_idle()
elif event.key==“i”:
p=np.array([event.x,event.y])#显示坐标
xy=np.asarray(自连线)
xyt=self.line.get_transform().transform(xy)
对于范围内的i(len(xyt)-1):
s0=xyt[i]
s1=xyt[i+1]
d=从点到段的距离(p、s0、s1)
如果d