Helenr
我找到了该问题的解决方案,但是,它不是很优雅。我发现,在非交互式后端调用的唯一回调函数是子类的draw_path()方法AbstractPathEffect。我创建了一个AbstractPathEffect子类,在其draw_path()方法中更新箭头的顶点。我仍然愿意为我的问题提供其他可能更直接的解决方案。import numpy as npfrom numpy.linalg import normfrom matplotlib.patches import FancyArrowfrom matplotlib.patheffects import AbstractPathEffectclass AdaptiveFancyArrow(FancyArrow): """ A `FancyArrow` with fixed head shape. The length of the head is proportional to the width the head in display coordinates. If the head length is longer than the length of the entire arrow, the head length is limited to the arrow length. """ def __init__(self, x, y, dx, dy, tail_width, head_width, head_ratio, draw_head=True, shape="full", **kwargs): if not draw_head: head_width = tail_width super().__init__( x, y, dx, dy, width=tail_width, head_width=head_width, overhang=0, shape=shape, length_includes_head=True, **kwargs ) self.set_path_effects( [_ArrowHeadCorrect(self, head_ratio, draw_head)] )class _ArrowHeadCorrect(AbstractPathEffect): """ Updates the arrow head length every time the arrow is rendered """ def __init__(self, arrow, head_ratio, draw_head): self._arrow = arrow self._head_ratio = head_ratio self._draw_head = draw_head def draw_path(self, renderer, gc, tpath, affine, rgbFace=None): # Indices to certain vertices in the arrow TIP = 0 HEAD_OUTER_1 = 1 HEAD_INNER_1 = 2 TAIL_1 = 3 TAIL_2 = 4 HEAD_INNER_2 = 5 HEAD_OUTER_2 = 6 transform = self._arrow.axes.transData vert = tpath.vertices # Transform data coordiantes to display coordinates vert = transform.transform(vert) # The direction vector alnog the arrow arrow_vec = vert[TIP] - (vert[TAIL_1] + vert[TAIL_2]) / 2 tail_width = norm(vert[TAIL_2] - vert[TAIL_1]) # Calculate head length from head width head_width = norm(vert[HEAD_OUTER_2] - vert[HEAD_OUTER_1]) head_length = head_width * self._head_ratio if head_length > norm(arrow_vec): # If the head would be longer than the entire arrow, # only draw the arrow head with reduced length head_length = norm(arrow_vec) # The new head start vector; is on the arrow vector if self._draw_head: head_start = \ vert[TIP] - head_length * arrow_vec/norm(arrow_vec) else: head_start = vert[TIP] # vector that is orthogonal to the arrow vector arrow_vec_ortho = vert[TAIL_2] - vert[TAIL_1] # Make unit vector arrow_vec_ortho = arrow_vec_ortho / norm(arrow_vec_ortho) # Adjust vertices of the arrow head vert[HEAD_OUTER_1] = head_start - arrow_vec_ortho * head_width/2 vert[HEAD_OUTER_2] = head_start + arrow_vec_ortho * head_width/2 vert[HEAD_INNER_1] = head_start - arrow_vec_ortho * tail_width/2 vert[HEAD_INNER_2] = head_start + arrow_vec_ortho * tail_width/2 # Transform back to data coordinates # and modify path with manipulated vertices tpath.vertices = transform.inverted().transform(vert) renderer.draw_path(gc, tpath, affine, rgbFace)