我尝试使用matplotlib创建一个交互式绘图,该绘图创建一个在端点处有两个手柄的线段。您可以单击并拖动手柄,线条将刷新以匹配以这种方式指定的位置,与此matplotlib示例poly_editor的方式类似:(如果您看到示例,请想象我想要相同的东西但是只有多边形的一个边缘。)
我尝试改变poly_editor代码以仅使用Line2D元素,并且我的程序运行没有任何错误,除了它根本不在轴上绘制任何东西。我认为这可能是变量范围内的错误,也可能与matplotlib的绘制调用有关。任何有关错误的指导都将非常感激。
编辑:我提高了一些,简化了代码,现在我可以让它绘制线并在epsilon距离内打印最近顶点的索引,但该线保持静止且不动画。更新的代码如下:
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import numpy as np
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()`
先谢谢,凯文。
答案 0 :(得分:1)
好的,我解决了这个问题。新代码(上面)实际上有效,其中有一个错误。对动作通知事件的mpl_connect调用具有错误的事件类型,现在它正在按预期工作。
答案 1 :(得分:0)
我是新来的,所以希望我不要通过回答这个self.replied问题来犯很多错误。 :)
首先感谢您发布此代码,它节省了一些时间,对我有很大帮助,我几乎完全需要此代码。我做了一些我在此建议的更新,以便可以操纵两个以上的点,并像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()
这实际上会导致我有一个问题/问题,但是会在另一条消息中出现。
克里斯汀