【问题标题】:my matplotlib title gets cropped我的 matplotlib 标题被裁剪
【发布时间】:2012-01-10 11:59:29
【问题描述】:

已解决 - 请参阅下面关于结合 wraptext.wrapplt.tightlayout 的评论。

问题: 代码如下:

import matplotlib.pyplot as plt
plt.bar([1,2],[5,4])
plt.title('this is a very long title and therefore it gets cropped which is an unthinkable behaviour as it loses the information in the title')
plt.show()

这会创建一个看起来像的图形

标题被裁剪,如何让它显示整个标题?

更新:我正在寻找一种使图形大小与标题和轴标签中的文本相匹配的解决方案,而不是一种用换行符剪切标题的解决方案,因为那种解决方案并不总是有帮助:

from textwrap import wrap
import matplotlib.pyplot as plt
title = 'this is a very long title and therefore it gets cropped which is an unthinkable behaviour as it loses the information in the title'*5
plt.bar([1,2],[5,4])
plt.title('\n'.join(wrap(title,60)))
plt.show()` 

查看结果:

【问题讨论】:

    标签: python matplotlib


    【解决方案1】:

    您可以尝试找到here 的解决方案。

    这是相当多的代码,但它似乎可以处理情节上任何类型文本的文本换行。

    这是解决方案中的代码,经过修改以适合您的示例:

    import matplotlib.pyplot as plt
    
    def main():
        fig = plt.figure()
        plt.subplots_adjust(top=0.85) # use a lower number to make more vertical space
        plt.bar([1,2],[5,4])
        fig.canvas.mpl_connect('draw_event', on_draw)
        plt.title('this is a very long title and therefore it gets cropped which is an unthinkable behaviour as it loses the information in the title')
        plt.savefig('./test.png')
    
    def on_draw(event):
        """Auto-wraps all text objects in a figure at draw-time"""
        import matplotlib as mpl
        fig = event.canvas.figure
    
        # Cycle through all artists in all the axes in the figure
        for ax in fig.axes:
            for artist in ax.get_children():
                # If it's a text artist, wrap it...
                if isinstance(artist, mpl.text.Text):
                    autowrap_text(artist, event.renderer)
    
        # Temporarily disconnect any callbacks to the draw event...
        # (To avoid recursion)
        func_handles = fig.canvas.callbacks.callbacks[event.name]
        fig.canvas.callbacks.callbacks[event.name] = {}
        # Re-draw the figure..
        fig.canvas.draw()
        # Reset the draw event callbacks
        fig.canvas.callbacks.callbacks[event.name] = func_handles
    
    def autowrap_text(textobj, renderer):
        """Wraps the given matplotlib text object so that it exceed the boundaries
        of the axis it is plotted in."""
        import textwrap
        # Get the starting position of the text in pixels...
        x0, y0 = textobj.get_transform().transform(textobj.get_position())
        # Get the extents of the current axis in pixels...
        clip = textobj.get_axes().get_window_extent()
        # Set the text to rotate about the left edge (doesn't make sense otherwise)
        textobj.set_rotation_mode('anchor')
    
        # Get the amount of space in the direction of rotation to the left and 
        # right of x0, y0 (left and right are relative to the rotation, as well)
        rotation = textobj.get_rotation()
        right_space = min_dist_inside((x0, y0), rotation, clip)
        left_space = min_dist_inside((x0, y0), rotation - 180, clip)
    
        # Use either the left or right distance depending on the horiz alignment.
        alignment = textobj.get_horizontalalignment()
        if alignment is 'left':
            new_width = right_space 
        elif alignment is 'right':
            new_width = left_space
        else:
            new_width = 2 * min(left_space, right_space)
    
        # Estimate the width of the new size in characters...
        aspect_ratio = 0.5 # This varies with the font!! 
        fontsize = textobj.get_size()
        pixels_per_char = aspect_ratio * renderer.points_to_pixels(fontsize)
    
        # If wrap_width is < 1, just make it 1 character
        wrap_width = max(1, new_width // pixels_per_char)
        try:
            wrapped_text = textwrap.fill(textobj.get_text(), wrap_width)
        except TypeError:
            # This appears to be a single word
            wrapped_text = textobj.get_text()
        textobj.set_text(wrapped_text)
    
    def min_dist_inside(point, rotation, box):
        """Gets the space in a given direction from "point" to the boundaries of
        "box" (where box is an object with x0, y0, x1, & y1 attributes, point is a
        tuple of x,y, and rotation is the angle in degrees)"""
        from math import sin, cos, radians
        x0, y0 = point
        rotation = radians(rotation)
        distances = []
        threshold = 0.0001 
        if cos(rotation) > threshold: 
            # Intersects the right axis
            distances.append((box.x1 - x0) / cos(rotation))
        if cos(rotation) < -threshold: 
            # Intersects the left axis
            distances.append((box.x0 - x0) / cos(rotation))
        if sin(rotation) > threshold: 
            # Intersects the top axis
            distances.append((box.y1 - y0) / sin(rotation))
        if sin(rotation) < -threshold: 
            # Intersects the bottom axis
            distances.append((box.y0 - y0) / sin(rotation))
        return min(distances)
    
    if __name__ == '__main__':
        main()
    

    这会产生以下情节:

    更新

    使用以下行在图形顶部和实际绘图顶部之间创建更多空间:

    plt.subplots_adjust(top=0.85) # use a lower number to make more vertical space
    

    例如,如果您使用:

    plt.subplots_adjust(top=0.5)
    

    输出将如下所示:

    【讨论】:

    • 这个答案实际上引导我找到完整而简单的解决方案,即使用plt.tight_layout(),请参阅Tight Layout guide。我仍然需要使用wraptext.wrap 包装文本,如上面的答案之一所示,但是这个 tightlayout 东西也会调整图形以正确显示 xlabel 等。
    • 那你能接受我的回答吗?这通常比在 stackexchange 网站上将 SOLVED 放在问题的首位要好。
    【解决方案2】:

    您可以使用 textwrap 自动用换行符 (\n) 换行文本:

    >>> longstring = "this is a very long title and therefore it gets cropped which is an unthinkable behaviour as it loses the information in the title"
    >>> "\n".join(textwrap.wrap(longstring, 100))
    'this is a very long title and therefore it gets cropped which is an unthinkable behaviour as it\nloses the information in the title'
    

    在这种情况下,100 是每行的字符数(到最近的空格 - textwrap 尽量不分解单词)


    另一种选择是减小字体的大小:

    matplotlib.rcParams.update({'font.size': 12})
    

    【讨论】:

      【解决方案3】:

      您可以在标题中包含换行符 \n 以将标题分隔多行。

      【讨论】:

        猜你喜欢
        • 2014-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-29
        • 1970-01-01
        • 2010-12-06
        相关资源
        最近更新 更多