Python annotation arrows with same headlength

2019-08-20 19:41发布

问题:

I'm using matplotlib to create annotations with arrows.

from matplotlib import pyplot as plt
plt.figure()
plt.annotate('Text1', xy=(1, 0), xytext=(1, 1), arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10))
plt.annotate('Text2', xy=(3, 0), xytext=(3, 3), arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10))
plt.annotate('Text3', xy=(6, 0), xytext=(6, 6), arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10))
plt.annotate('Text4', xy=(9, 0), xytext=(9, 9), arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10))
plt.xlim((0, 10))
plt.ylim(0, 10)
plt.show()

As can be seen, the length of the arrow heads is affected by the line-length and varies between the arrows.

I'm looking for a way to create arrows with different line-lengths but equal arrow-headlengths. Although many options are listed on the annotations manual page, i was not able to produce the desired result.

As an alternative approach i tried to plot the arrows seperatedely using plt.arrow. In that case the arrow heads looked as desired, but when using transparency, additional lines became visible.

Best regards, zinjaai

回答1:

In the arrowprops you can't specify the length of the head, but the frac arguement specifies the length of the head as a fraction the arrow length. Since you know xy and xytext you can compute the necessary fraction for a desired headlength.

I've don't it quickly for the example you provided (with an head length of 0.5).

from matplotlib import pyplot as plt
plt.figure()
plt.annotate('Text1', xy=(1, 0), xytext=(1, 1), 
        arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10, frac=0.5/1))
plt.annotate('Text2', xy=(3, 0), xytext=(3, 3), 
        arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10, frac=0.5/3))
plt.annotate('Text3', xy=(6, 0), xytext=(6, 6), 
        arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10, frac=0.5/6))
plt.annotate('Text4', xy=(9, 0), xytext=(9, 9), 
        arrowprops=dict(alpha=0.5, fc='r', ec='r', headwidth=10, frac=0.5/9))
plt.xlim((0, 10))
plt.ylim(0, 10)
plt.show()

Result:

You could wrap this in a function, which computes the length of the arrow (given xy and xytext) and then passes that on to plt.annotate.