1 回答

TA貢獻1824條經驗 獲得超6個贊
嘗試使用 繪制樹狀圖時我遇到了同樣的問題squarify。經過一番搜索,我想出了一個解決方案,它似乎按預期工作。
import matplotlib.patches as mpatches
import matplotlib.text as mtext
# Refrence https://stackoverflow.com/questions/48079364/wrapping-text-not-working-in-matplotlib
# and https://stackoverflow.com/questions/50742503/how-do-i-get-the-height-of-a-wrapped-text-in-matplotlib
class WrapText(mtext.Text):
def __init__(self,
x=0, y=0, text='',
width=0,
**kwargs):
mtext.Text.__init__(self,
x=x, y=y, text=text,
wrap=True,
**kwargs)
self.width = width # in screen pixels. You could do scaling first
def _get_wrap_line_width(self):
return self.width
def get_lines_num(self):
return len(self._get_wrapped_text().split('\n'))
class WrapAnnotation(mtext.Annotation):
def __init__(self,
text, xy,
width, **kwargs):
mtext.Annotation.__init__(self,
text=text,
xy=xy,
wrap=True,
**kwargs)
self.width = width
def _get_wrap_line_width(self):
return self.width
def get_lines_num(self):
return len(self._get_wrapped_text().split('\n'))
def text_with_autofit(self, txt, xy, width, height, *,
transform=None,
ha='center', va='center',
wrap=False, show_rect=False,
min_size=1, adjust=0,
**kwargs):
if transform is None:
if isinstance(self, Axes):
transform = self.transData
if isinstance(self, Figure):
transform = self.transFigure
x_data = {'center': (xy[0] - width/2, xy[0] + width/2),
'left': (xy[0], xy[0] + width),
'right': (xy[0] - width, xy[0])}
y_data = {'center': (xy[1] - height/2, xy[1] + height/2),
'bottom': (xy[1], xy[1] + height),
'top': (xy[1] - height, xy[1])}
(x0, y0) = transform.transform((x_data[ha][0], y_data[va][0]))
(x1, y1) = transform.transform((x_data[ha][1], y_data[va][1]))
# rectange region size to constrain the text
rect_width = x1 - x0
rect_height = y1- y0
fig = self.get_figure() if isinstance(self, Axes) else self
dpi = fig.dpi
rect_height_inch = rect_height / dpi
fontsize = rect_height_inch * 72
if isinstance(self, Figure):
if not wrap:
text = self.text(*xy, txt, ha=ha, va=va, transform=transform,
fontsize=min_size,
**kwargs)
else:
fontsize /= 2
text = WrapText(*xy, txt, width=rect_width, ha=ha, va=va,
transform=transform, fontsize=fontsize,
**kwargs)
self.add_artist(text)
if isinstance(self, Axes):
if not wrap:
text = self.annotate(txt, xy, ha=ha, va=va, xycoords=transform,
fontsize=min_size,
**kwargs)
else:
fontsize /= 2
text = WrapAnnotation(txt, xy, ha=ha, va=va, xycoords=transform,
fontsize=fontsize, width=rect_width,
**kwargs)
self.add_artist(text)
while fontsize > min_size:
text.set_fontsize(fontsize)
bbox = text.get_window_extent(fig.canvas.get_renderer())
bbox_width = bbox.width / text.get_lines_num() if wrap else bbox.width
if bbox_width <= rect_width:
while bbox_width <= rect_width:
fontsize += 1
text.set_fontsize(fontsize)
bbox = text.get_window_extent(fig.canvas.get_renderer())
bbox_width = bbox.width / text.get_lines_num() if wrap else bbox.width
else:
fontsize = fontsize - 1
text.set_fontsize(fontsize)
break;
fontsize /= 2
if fig.get_constrained_layout():
c_fontsize = fontsize + adjust + 0.5
text.set_fontsize(c_fontsize if c_fontsize > min_size else min_size)
if fig.get_tight_layout():
c_fontsize = fontsize + adjust
text.set_fontsize(c_fontsize if c_fontsize > min_size else min_size)
if show_rect and isinstance(self, Axes):
rect = mpatches.Rectangle((x_data[ha][0], y_data[va][0]),
width, height, fill=False, ls='--')
self.add_patch(rect)
return text
此功能支持將文本自動調整到框中。如果wrap是True,則文本將根據框的大小自動換行。
grow=True下圖是自動調整( )和自動換行(wrap=True)的圖
數據是來自treemapify的 G20 ,這是一個用于繪制樹形圖的優秀 R 包。
自動擬合的圖:
自動調整和自動換行的圖形:
自動調整的基本過程是根據框的高度設置字體大小,將文本寬度與框的寬度進行比較,然后減小字體大小,直到文本寬度小于框的寬度。
至于自動換行,底層過程依賴于 matplotlib 中內置的自動換行,通過設置wrap=True
. 自動調整字體大小的過程是相同的。
然而,自動適配的過程有點慢。我希望有人能夠找出一些更有效的自動擬合算法。
希望這個功能可以幫助到您。
添加回答
舉報