微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Matplotlib中具有可拖动末端的箭头

如何解决Matplotlib中具有可拖动末端的箭头

如何在Matplotlib中创建一个箭头(其末端(头)可以用鼠标拖动)?

编辑:受Mathematica启发,可能的实现是创建一个“定位器”,然后向其绘制箭头。定位器(而不是箭头)可拖动;移动后,将箭头重新绘制到定位器。

相应的Mathematica代码(请参见Locator的文档):

Manipulate[
 Graphics[Line[{{0,0},p}],PlotRange -> 2],{{p,{1,1}},Locator}]

此类对象的用途:我想创建一个交互式图形,以说明基数变化(在2D模式下)如何影响基矢量沿矢量的分量(将在数学课程中使用)。因此,我想将两个基向量实现为具有可拖动末端的箭头(起点固定在原点)。通过拖动箭头末端(头)可以更改它们。

我不熟悉Matplotlib中的事件处理。我尝试阅读related docs,但仍然感到困惑。欢迎答案中的详细解释。

解决方法

这是两个可移动向量的示例。由于我们只想拾取和移动端点,因此仅出于移动端点的目的而添加了两个不可见的点。

这只是一个简单的例子。 motion_notify_callback可以扩展以更新更多元素。

希望这些函数的名称能帮助您找出正在发生的事情。

from matplotlib import pyplot as plt
from matplotlib.backend_bases import MouseButton
from matplotlib.ticker import MultipleLocator

fig,ax = plt.subplots(figsize=(10,10))

picked_artist = None
vector_x2 = ax.annotate('',(0,0),(2,1),ha="left",va="center",arrowprops=dict(arrowstyle='<|-',fc="k",ec="k",shrinkA=0,shrinkB=0))
vector_y2 = ax.annotate('',(-1,2),shrinkB=0))
point_x2,= ax.plot(2,1,'.',color='none',picker=True)
point_y2,= ax.plot(-1,2,picker=True)

def pick_callback(event):
    'called when an element is picked'
    global picked_artist
    if event.mouseevent.button == MouseButton.LEFT:
        picked_artist = event.artist

def button_release_callback(event):
    'called when a mouse button is released'
    global picked_artist
    if event.button == MouseButton.LEFT:
        picked_artist = None

def motion_notify_callback(event):
    'called when the mouse moves'
    global picked_artist
    if picked_artist is not None and event.inaxes is not None and event.button == MouseButton.LEFT:
        picked_artist.set_data([event.xdata],[event.ydata])
        if picked_artist == point_x2:
            vector_x2.set_position([event.xdata,event.ydata])
        elif picked_artist == point_y2:
            vector_y2.set_position([event.xdata,event.ydata])
        fig.canvas.draw_idle()

ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.xaxis.set_minor_locator(MultipleLocator(0.5))
ax.yaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_minor_locator(MultipleLocator(0.5))

ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')

ax.grid(True,which='major',linestyle='-',lw=1)
ax.grid(True,which='minor',linestyle=':',lw=0.5)

fig.canvas.mpl_connect('button_release_event',button_release_callback)
fig.canvas.mpl_connect('pick_event',pick_callback)
fig.canvas.mpl_connect('motion_notify_event',motion_notify_callback)

plt.show()

starting plot

,

基于@JohanC的代码,我现在直接操作FancyArrow而不是ax.annotate。更新方法是删除箭头并重新绘制。欢迎使用更有效的更新方法。

from matplotlib import pyplot as plt
from matplotlib.backend_bases import MouseButton
from matplotlib.ticker import MultipleLocator
from matplotlib.patches import FancyArrow
import numpy as np

fig,ax = plt.subplots(figsize=(8,8))
text_kw = dict(fontsize=14)
picked_artist = None

ax.set_xlim(-5,5)
ax.set_aspect(1)
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.xaxis.set_minor_locator(MultipleLocator(0.5))
ax.yaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_minor_locator(MultipleLocator(0.5))

ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.tick_params(left=False,bottom=False,labelsize=14)

ax.grid(True,lw=0.5)

# locator of vectors
init_v = np.array([3,2])
## the point indicator can be made invisible by setting `color='none'`
ptv,= ax.plot(*init_v,'+',color='blue',ms=10,picker=20)
# draw vector
arrow_kw = {'color': 'blue','head_width': 0.2,'head_length': 0.3,'width': 0.03,'length_includes_head': True}
v = FancyArrow(0,*init_v,**arrow_kw)
ax.add_artist(v)

def on_pick(event):
    'called when an element is picked'
    global picked_artist
    if event.mouseevent.button == MouseButton.LEFT:
        picked_artist = event.artist

def on_button_release(event):
    'called when a mouse button is released'
    global picked_artist
    if event.button == MouseButton.LEFT:
        picked_artist = None

def on_motion_notify(event):
    'called when the mouse moves'
    global picked_artist
    global init_v,v
    if picked_artist is not None and event.inaxes is not None and event.button == MouseButton.LEFT:
        picked_artist.set_data([event.xdata],[event.ydata])
        if picked_artist == ptv:
            # redraw arrow v
            init_v = np.array([event.xdata,event.ydata])
            v.remove()
            v = FancyArrow(0,**arrow_kw)
            ax.add_artist(v)
        fig.canvas.draw_idle()

fig.canvas.mpl_connect('button_release_event',on_button_release)
fig.canvas.mpl_connect('pick_event',on_pick)
fig.canvas.mpl_connect('motion_notify_event',on_motion_notify)

plt.show()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?