【问题标题】:Custom arrow style for matplotlib, pyplot.annotatematplotlib 的自定义箭头样式,pyplot.annotate
【发布时间】:2013-06-06 17:02:38
【问题描述】:

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

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

我想使用一端为平线,另一端为箭头的箭头样式,因此结合样式“|-|”和“->”来制作我们可能称之为“|->”的东西,但我不知道如何定义自己的风格。

我想我可以试试类似的东西

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) 也不起作用。

我也试过在arrowprops字典中定义,比如

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

但这给了我关于没有属性“set_head_width”的错误。

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

【问题讨论】:

    标签: python matplotlib plot customization annotate


    【解决方案1】:

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

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

    仍然存在与多边形断开线的收缩问题。使用此自定义可以轻松创建您请求的“|-|>”箭头。

    代码如下:

    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 中没有内置这种东西完全是香蕉?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2018-05-30
    • 2021-12-23
    相关资源
    最近更新 更多