You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中使用Matplotlib为仪表盘添加沿弧线弯曲的文本

实现matplotlib仪表盘弧形文本的方案

原生matplotlib没有内置弧形文本接口,我们可以通过逐字符拆分文本+分别计算每个字符的弧上坐标和旋转角度的方式实现效果,修改后的完整代码如下:

首先补充缺失的numpy依赖导入,再新增弧形文本绘制函数:

import numpy as np
import os, sys
import matplotlib
import seaborn as sns
from matplotlib import cm
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
from matplotlib.patches import Circle, Wedge, Rectangle

# 新增弧形文本绘制函数
def draw_arc_text(ax, center, radius, start_angle, end_angle, text, fontsize=8, fontweight='bold'):
    # 角度转弧度
    start_rad = np.radians(start_angle)
    end_rad = np.radians(end_angle)
    # 计算每个字符占用的角度
    char_count = len(text)
    total_angle = end_rad - start_rad
    char_angle = total_angle / char_count
    # 逐个绘制字符
    for i, char in enumerate(text):
        # 当前字符的中心角度
        char_rad = start_rad + char_angle * (i + 0.5)
        # 计算字符坐标
        x = center[0] + radius * np.cos(char_rad)
        y = center[1] + radius * np.sin(char_rad)
        # 计算字符旋转角度(和弧线切线垂直)
        rotation = np.degrees(char_rad) - 90
        ax.text(x, y, char,
                horizontalalignment='center',
                verticalalignment='center',
                fontsize=fontsize,
                fontweight=fontweight,
                rotation=rotation,
                rotation_mode='anchor')

# 原有三角函数保持不变
def degree_range(n): 
    start = np.linspace(0,180,n+1, endpoint=True)[0:-1]
    end = np.linspace(0,180,n+1, endpoint=True)[1::]
    mid_points = start + ((end-start)/2.)
    return np.c_[start, end], mid_points

def rot_text(ang): 
    rotation = np.degrees(np.radians(ang) * np.pi / np.pi - np.radians(90))
    return rotation

def gauge(labels=['LOW','MEDIUM','HIGH','VERY HIGH','EXTREME'],
          colors='jet_r', 
          arrow=1, 
          title='',
          size = (6,3), 
          fname=False): 
    
    N = len(labels)
    
    if arrow > N: 
        raise Exception("The category ({}) is greated than the length of the labels ({})".format(arrow, N))
 
    """
    if colors is a string, we assume it's a matplotlib colormap
    and we discretize in N discrete colors 
    """
    
    if isinstance(colors, str):
        cmap = cm.get_cmap(colors, N)
        cmap = cmap(np.arange(N))
        colors = cmap[::-1,:].tolist()
    if isinstance(colors, list): 
        if len(colors) == N:
            colors = colors[::-1]
        else: 
            raise Exception("number of colors {} not equal to number of categories{}".format(len(colors), N))

    """
    begins the plotting
    """
    
    fig, ax = plt.subplots(figsize=size, dpi=100) # figsize=(5,5)

    ang_range, mid_points = degree_range(N)

    labels = labels[::-1]
    
    """
    plots the sectors and the arcs
    """
    patches = []
    for ang, c in zip(ang_range, colors): 
        # sectors
        patches.append(Wedge((0.,0.), .4, *ang, facecolor='w', lw=2))
        # arcs
        patches.append(Wedge((0.,0.), .4, *ang, width=0.10, facecolor=c, lw=2, alpha=0.5))
    
    [ax.add_patch(p) for p in patches]

    
    """
    替换原有整段文本绘制逻辑,改为弧形文本绘制
    """
    for ang, lab in zip(ang_range, labels):
        draw_arc_text(ax, (0,0), 0.35, ang[0], ang[1], lab, fontsize=8)

    """
    set the bottom banner and the title
    """
    r = Rectangle((-0.4,-0.1),0.8,0.1, facecolor='w', lw=2)
    ax.add_patch(r)
    
    ax.text(0, 
            -0.05, 
            title, 
            horizontalalignment='center',
            verticalalignment='center', 
            fontsize=14, 
            fontweight='bold')

    """
    plots the arrow now
    """
    
    pos = mid_points[abs(arrow - N)]
    
    ax.arrow(0, 
             0, 
             0.225 * np.cos(np.radians(pos)), 
             0.225 * np.sin(np.radians(pos)),
             width=0.04, 
             head_width=0.09, 
             head_length=0.1, 
             fc='k', 
             ec='k')
    
    ax.add_patch(Circle((0, 0), radius=0.02, facecolor='k'))
    ax.add_patch(Circle((0, 0), radius=0.01, facecolor='w', zorder=11))

    """
    removes frame and ticks, and makes axis equal and tight
    """
    
    ax.set_frame_on(False)
    ax.axes.set_xticks([])
    ax.axes.set_yticks([])
    ax.axis('equal')
    plt.tight_layout()
    if fname:
        fig.savefig(fname, dpi=200)
        plt.close()

# 测试调用
gauge(labels=['LOW','MEDIUM','HIGH','VERY HIGH','EXTREME','CRITICAL'], 
      colors=["#5F4690","#1D6996","#38A6A5","#0F8554","#73AF48","#EDAD08"], 
      arrow=3, size=(5,3), title='drought severity index') 
plt.show()

你可以调整draw_arc_text函数里的radius参数(当前为0.35)控制文本距离圆心的位置,调整fontsize适配不同长度的标签,避免字符重叠。

内容的提问来源于stack exchange,提问作者Ailurophile

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 23:27:01