matplotlib,pyplot.annotate的自定义箭头样式 [英] Custom arrow style for matplotlib, pyplot.annotate

查看:297
本文介绍了matplotlib,pyplot.annotate的自定义箭头样式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用matplotlib.pyplot.annotate在我的绘图上绘制箭头,如下所示:

I am using matplotlib.pyplot.annotate to draw an arrow on my plot, like so:

import matplotlib.pyplot as plt
plt.annotate("",(x,ybottom),(x,ytop),arrowprops=dict(arrowstyle="->"))

我想使用在一端具有平线而在另一端具有箭头的箭头样式,因此将样式"|-|"组合在一起和->"来制作一些我们可能称为"|->"的图片,但我不知道如何定义自己的样式.

I want to use an arrow style that has a flat line at one end and an arrow at the other, so combining the styles "|-|" and "->" to make something we might call "|->", but I can't figure out how to define my own style.

我认为我可以尝试类似的方法

I thought I might try something like

import matplotlib.patches as patches                                                                                                                                                                              
myarrow = patches.ArrowStyle("Fancy", head_length=0.4,head_width=0.2)

(现在应该与->"相同;稍后可以调整样式),然后如何告诉plt.annotate使用myarrow作为样式?没有plt.annotate的arrowstyle属性,并且arrowprops = dict(arrowstyle = myarrow)也不起作用.

(which should just be the same as "->" for now; I can tweak the style later) but then how do I tell plt.annotate to use myarrow as the style? There is no arrowstyle property for plt.annotate, and arrowprops=dict(arrowstyle=myarrow) doesn't work either.

我也尝试过在arrowprops字典中定义它,例如

I've also tried defining it in the arrowprops dictionary, such as

plt.annotate("",(x,ybottom),(x,ytop),arrowprops=dict(head_length=0.4,head_width=0.2))

但这给我关于没有属性'set_head_width'的错误.

but that gives me errors about no attribute 'set_head_width'.

那么,如何定义自己的样式供pyplot.annotate使用?

So, how can I define my own style for pyplot.annotate to use?

推荐答案

在代码的最后一个示例中,您可以使用headwidthfracwidth自定义箭头,结果为arrow0如下所示.对于高度自定义的箭头,可以使用任意多边形.在下面,您可以看到我用来生成图形的代码.

In the last example in your code you could have used headwidth, frac and width to customize the arrow, the result is arrow0 shown below. For highly customized arrows you can used arbitrary polygons. Below you can see the code I used to produce the figures.

要添加更多的多边形,您必须编辑polygons词典,并且新的多边形必须在原点(0,0)上具有第一个和最后一个点,重新缩放和重新定位是自动完成的.下图说明了多边形的定义方式.

To add more polygons you have to edit polygons dictionary, and the new polygons must have the first and the last point at the origin (0,0), the rescaling and repositioning are done automatically. The figure below illustrates how the polygons are defined.

缩小仍然存在一个问题,即线条与多边形断开连接.您可以使用此自定义轻松地创建您请求的"|-|>"箭头.

There is still an issue with shrinking that disconnects the line with the polygons. The '|-|>' arrow that you requested can be easily created using this customization.

代码如下:

import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib import transforms
import numpy as np
from numpy import cos, sin
plt.close()
plt.plot([1,2],[0,4], 'w')
ax = plt.gcf().axes[0]

def patchesAB(styleA, styleB, orig, target,
                widthA, lengthA, widthB, lengthB,
                kwargsA, kwargsB, shrinkA=0., shrinkB=0.):
    '''
    Select 'styleA' and 'styleB' from the dictionary 'polygons'
    widthA, lengthA, widthB, lenghtB, shrinkA, shrinkB are defined in points
    kwargsA and kwargsB are dictionaries
    '''
    polygons = {\
        '|':np.array([[0,0],[0,1],[0.1,1],[0.1,-1],[0,-1],[0,0]], dtype=float),
        'arrow1':np.array([[0,0],[0,1],[-1,2],[3,0],[-1,-2],[0,-1],[0,0]], dtype=float),
        'arrow2':np.array([[0,0],[-1,1],[0,2],[3,0],[0,-2],[-1,-1],[0,0]], dtype=float),
               }
    xyA = polygons.get( styleA )
    xyB = polygons.get( styleB )
    #
    fig = plt.gcf()
    ax = fig.axes[0]
    trans = ax.transData
    pixPunit = trans.transform([(1,0),(0,1)])-ax.transData.transform((0,0))
    unitPpix = pixPunit
    unitPpix[0,0] = 1/unitPpix[0,0]
    unitPpix[1,1] = 1/unitPpix[1,1]
    #
    orig = np.array(orig)
    target = np.array(target)
    vec = target-orig
    angle = np.arctan2( vec[1], vec[0] )
    #
    lengthA *= unitPpix[0,0]
    lengthB *= unitPpix[0,0]
    widthA  *= unitPpix[1,1]
    widthB  *= unitPpix[1,1]
    orig   += (unitPpix[1,1]*sin(angle)+unitPpix[0,0]*cos(angle))*vec*shrinkA
    target -= (unitPpix[1,1]*sin(angle)+unitPpix[0,0]*cos(angle))*vec*shrinkB
    #TODO improve shrinking... another attempt:
    #orig   +=  unitPpix.dot(vec) * shrinkA
    #target -=  unitPpix.dot(vec) * shrinkB
    # polA
    if xyA != None:
        a = transforms.Affine2D()
        tA = a.rotate_around( orig[0], orig[1], angle+np.pi ) + trans
        xyA = np.float_(xyA)
        xyA[:,0] *= lengthA/(xyA[:,0].max()-xyA[:,0].min())
        xyA[:,1] *=  widthA/(xyA[:,1].max()-xyA[:,1].min())
        xyA += orig
        polA = patches.Polygon( xyA, **kwargsA )
        polA.set_transform( tA )
    else:
        polA = None
    # polB
    if xyB != None:
        a = transforms.Affine2D()
        tB = a.rotate_around( target[0], target[1], angle ) + trans
        xyB = np.float_(xyB)
        xyB[:,0] *= lengthB/(xyB[:,0].max()-xyB[:,0].min())
        xyB[:,1] *=  widthB/(xyB[:,1].max()-xyB[:,1].min())
        xyB += target
        polB = patches.Polygon( xyB, **kwargsB )
        polB.set_transform( tB )
    else:
        polB = None
    return polA, polB

# ARROW 0
plt.annotate('arrow0',xy=(2,1.5),xycoords='data',
             xytext=(1.1,1), textcoords='data',
             arrowprops=dict(frac=0.1,headwidth=10., width=2.))
#
kwargsA = dict( lw=1., ec='k', fc='gray' )
kwargsB = dict( lw=1., ec='k', fc='b' )
# ARROW 1
orig = (1.1,2.)
target = (2.,2.5)
shrinkA = 0.
shrinkB = 0.
polA, polB = patchesAB( '|', 'arrow1', orig, target, 20.,1.,60.,60.,
                        kwargsA, kwargsB, shrinkA, shrinkB )
ax.add_patch(polA)
ax.add_patch(polB)

ax.annotate('arrow1', xy=target, xycoords='data',
             xytext=orig, textcoords='data',
             arrowprops=dict(arrowstyle='-', patchA=polA, patchB=polB,
                 lw=1., shrinkA=shrinkA, shrinkB=shrinkB, relpos=(0.,0.),
                 mutation_scale=1.))
# ARROW 2
orig = (1.1,3.)
target = (2.,3.5)
polA, polB = patchesAB( '|', 'arrow2', orig, target, 20.,1.,60.,60.,
                        kwargsA, kwargsB, shrinkA, shrinkB )
ax.add_patch(polA)
ax.add_patch(polB)

ax.annotate('arrow2', xy=target, xycoords='data',
             xytext=orig, textcoords='data',
             arrowprops=dict(arrowstyle='-', patchA=polA, patchB=polB,
                 lw=1., shrinkA=shrinkA, shrinkB=shrinkB, relpos=(0.,0.),
                 mutation_scale=1.))
plt.autoscale()
plt.xlim(1.,2.2)
plt.ylim(0.5,4)
plt.show()

这篇关于matplotlib,pyplot.annotate的自定义箭头样式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆