解决 “findfont: Generic family ‘sans-serif‘ not found because none of the following families were foun

解决 “findfont: Generic family ‘sans-serif’ not found because none of the following families were found: simhei” 错误

这个错误表明您的系统无法找到 simhei(黑体)字体。以下是彻底解决这个问题的完整指南:

完整解决方案步骤

1. 安装 simhei 字体

# 更新软件包索引
sudo apt update

# 安装 simhei 字体(黑体)
sudo apt install -y fonts-arphic-uming fonts-arphic-ukai fonts-wqy-microhei fonts-wqy-zenhei fonts-noto-cjk

# 刷新系统字体缓存
sudo fc-cache -f -v

# 清除 Matplotlib 缓存
rm -rf ~/.cache/matplotlib

2. 确认字体是否安装成功

# 列出所有中文字体
fc-list | grep -i -E 'hei|simhei|cjk|han|msyh|simhei|noto'

# 检查特定字体
fc-match -s SimHei
fc-match -s 'Noto Sans CJK SC'
fc-match -s 'WenQuanYi Zen Hei'

3. 在 Matplotlib 代码中配置字体

在您的 Python 脚本开头添加以下配置:

import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import numpy as np
import os

def setup_chinese_font():
    """配置中文字体支持"""
    # 字体替代方案(按优先级排序)
    font_options = [
        'SimHei',                  # 首选 - SimHei
        'Noto Sans CJK SC',         # 备用1 - Google Noto
        'WenQuanYi Zen Hei',       # 备用2 - 文泉驿正黑
        'WenQuanYi Micro Hei',     # 备用3 - 文泉驿微米黑
        'Microsoft YaHei',         # 备用4 - 微软雅黑
        'DejaVu Sans',             # 最后的回退
    ]
    
    # 尝试设置字体
    for font_name in font_options:
        try:
            # 查找字体文件
            font_path = fm.findfont(fm.FontProperties(family=font_name))
            
            # 配置为系统默认
            plt.rcParams['font.family'] = font_name
            plt.rcParams['font.sans-serif'] = [font_name]
            plt.rcParams['axes.unicode_minus'] = False
            
            print(f"已成功配置字体: {font_name} ({os.path.basename(font_path)})")
            return
        except:
            continue
    
    # 如果所有字体都失败,使用回退方案
    plt.rcParams['font.family'] = 'sans-serif'
    print("警告: 未找到中文字体,将使用系统默认")

# 在脚本开头调用设置
setup_chinese_font()

4. 手动安装 simhei 字体(如果需要)

如果上述安装后仍然找不到 simhei 字体,可以手动安装:

# 创建字体目录
mkdir -p ~/.fonts/simhei

# 下载 simhei 字体
wget -O ~/.fonts/simhei/simhei.ttf "https://github.com/msys2/MSYS2-packages/raw/main/mingw-w64-font-simhei/mingw-w64-font-simhei-1.0.0-1-any.pkg.tar.zst?raw=true"

# 刷新字体缓存
fc-cache -f -v ~/.fonts/

5. 在 Matplotlib 中指定字体路径

在您的代码中,添加以下函数来手动指定字体:

def ensure_simhei_font():
    """确保使用 simhei 字体"""
    try:
        # 尝试多种可能的字体路径
        possible_paths = [
            os.path.expanduser("~/.fonts/simhei/simhei.ttf"),
            "/usr/share/fonts/truetype/arphic/simhei.ttf",
            "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
            "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
        ]
        
        for path in possible_paths:
            if os.path.exists(path):
                # 添加字体到 Matplotlib
                font_prop = fm.FontProperties(fname=path)
                plt.rcParams['font.family'] = font_prop.get_name()
                plt.rcParams['axes.unicode_minus'] = False
                print(f"使用字体文件: {path}")
                return True
        return False
    except Exception as e:
        print(f"字体处理错误: {e}")
        return False

# 在设置函数中调用
if not ensure_simhei_font():
    setup_chinese_font()  # 回退到之前的设置

6. 重建 Matplotlib 字体缓存

在您的代码中添加以下函数来重建字体缓存:

def rebuild_font_cache():
    """重建 Matplotlib 字体缓存"""
    from matplotlib import font_manager
    
    # 删除旧的缓存文件
    cache_dir = os.path.join(fm.get_cachedir(), 'font*.cache')
    if os.path.exists(cache_dir):
        for cache_file in os.listdir(fm.get_cachedir()):
            if cache_file.startswith('font') and cache_file.endswith('.cache'):
                os.remove(os.path.join(fm.get_cachedir(), cache_file))
    
    # 创建新的缓存
    try:
        font_manager._rebuild()
        print("Matplotlib 字体缓存已重建")
        return True
    except Exception as e:
        print(f"重建字体缓存失败: {e}")
        return False

# 在脚本开头调用
rebuild_font_cache()

测试代码

在您的脚本末尾添加以下测试函数:

def test_chinese_font():
    """测试中文字体显示"""
    plt.figure()
    plt.plot([0, 1, 2], [3, 2, 1])
    plt.title("中文标题测试 - 黑体")
    plt.xlabel("横坐标标签")
    plt.ylabel("纵坐标标签")
    
    # 保存测试文件
    test_file = "chinese_font_test.png"
    plt.savefig(test_file, bbox_inches='tight')
    plt.close()
    print(f"测试图表已保存至: {test_file}")

# 执行测试
test_chinese_font()

完整解决方案脚本

以下是一个完整的解决方案脚本:

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os
import shutil

# 1. 设置中文环境支持
def setup_chinese_font():
    """配置中文字体支持"""
    try:
        # 检查可用字体
        font_list = [f.name for f in fm.fontManager.ttflist]
        chinese_fonts = ['SimHei', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 
                         'WenQuanYi Micro Hei', 'Microsoft YaHei']
        
        # 查找第一个可用的中文字体
        for font_name in chinese_fonts:
            if any(font_name in name for name in font_list):
                plt.rcParams['font.family'] = font_name
                plt.rcParams['font.sans-serif'] = [font_name]
                plt.rcParams['axes.unicode_minus'] = False
                print(f"配置字体: {font_name}")
                return
        
        # 没有找到预设字体,使用回退
        plt.rcParams['font.family'] = 'sans-serif'
        print("警告: 未找到预设中文字体,使用系统默认")
    except Exception as e:
        print(f"字体配置错误: {e}")
        plt.rcParams['font.family'] = 'sans-serif'

# 2. 确保使用 simhei 字体
def ensure_simhei_font():
    """尝试使用 simhei 字体"""
    possible_paths = [
        os.path.expanduser("~/.fonts/simhei/simhei.ttf"),
        "/usr/share/fonts/truetype/arphic/simhei.ttf",
        "/usr/share/fonts/truetype/msttcorefonts/simhei.ttf",
        "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
        "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
    ]
    
    for path in possible_paths:
        if os.path.exists(path):
            try:
                # 添加字体到 Matplotlib
                fm.fontManager.addfont(path)
                
                # 设置为新加载的字体
                font_name = fm.FontProperties(fname=path).get_name()
                plt.rcParams['font.family'] = font_name
                plt.rcParams['axes.unicode_minus'] = False
                print(f"成功加载字体: {font_name} ({os.path.basename(path)})")
                return True
            except Exception as e:
                print(f"加载字体 {path} 失败: {e}")
    
    return False

# 3. 重建字体缓存
def rebuild_font_cache():
    """重建 Matplotlib 字体缓存"""
    try:
        # 删除缓存目录
        cache_dir = fm.get_cachedir()
        if os.path.exists(cache_dir):
            shutil.rmtree(cache_dir)
            os.makedirs(cache_dir)
        
        # 重建缓存
        fm._rebuild()
        print("Matplotlib 字体缓存已重建")
        return True
    except Exception as e:
        print(f"重建字体缓存失败: {e}")
        return False

# 4. 测试字体显示
def test_chinese_display():
    """测试中文字体显示"""
    try:
        plt.figure(figsize=(8, 4))
        
        # 创建简单图表
        x = [1, 2, 3]
        y = [3, 2, 1]
        plt.plot(x, y)
        
        # 添加中文文本
        plt.title("中文标题 - 黑体测试")
        plt.xlabel("横坐标 - 测试")
        plt.ylabel("纵坐标 - 测试")
        plt.grid(True)
        
        # 保存测试图像
        test_file = "chinese_font_test.png"
        plt.savefig(test_file, bbox_inches='tight')
        plt.close()
        
        # 检查文件是否创建成功
        if os.path.exists(test_file) and os.path.getsize(test_file) > 0:
            print(f"中文字体测试成功! 图片保存至: {test_file}")
            return True
        else:
            print("测试图表保存失败")
            return False
    except Exception as e:
        print(f"测试中文字体显示时出错: {e}")
        return False

# 主流程
if __name__ == "__main__":
    print("===== 开始解决中文字体问题 =====")
    
    # 步骤1: 重建字体缓存
    print("\n[1/4] 重建字体缓存...")
    rebuild_font_cache()
    
    # 步骤2: 尝试使用 simhei 字体
    print("\n[2/4] 尝试加载 SimHei 字体...")
    if not ensure_simhei_font():
        # 步骤3: 设置备选字体
        print("\n[3/4] 设置备选中文字体...")
        setup_chinese_font()
    
    # 步骤4: 测试显示
    print("\n[4/4] 测试中文字体显示...")
    if not test_chinese_display():
        print("\n⚠️ 警告: 中文字体显示测试失败,可能需要手动安装字体")
        print("请尝试以下命令手动安装中文字体:")
        print("  sudo apt install fonts-arphic-uming fonts-arphic-ukai fonts-wqy-microhei fonts-wqy-zenhei fonts-noto-cjk")
        print("然后重新运行此脚本")

高级解决方案:字体嵌入技术

如果上述方法都不起作用,可以考虑将字体直接嵌入到 Python 脚本中:

import base64
import tempfile
from io import BytesIO

def embed_font_in_plot():
    """在图表中嵌入字体"""
    # 字体文件转换为 base64
    FONT_DATA = b"""
    # 字体文件内容
    ...
    """  # 这里放入字体文件的二进制内容
    
    # 创建临时字体文件
    with tempfile.NamedTemporaryFile(suffix=".ttf", delete=False) as temp_font:
        temp_font.write(FONT_DATA)
        temp_font_path = temp_font.name
    
    # 使用临时字体文件
    font_prop = fm.FontProperties(fname=temp_font_path)
    plt.rcParams['font.family'] = font_prop.get_name()
    
    # 创建图表
    plt.figure()
    plt.title("嵌入字体测试 - 中文")
    plt.xlabel("横坐标")
    plt.ylabel("纵坐标")
    
    # 保存到内存
    buf = BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    
    # 清理临时文件
    os.unlink(temp_font_path)
    
    return buf

其他建议

  1. 使用 Docker 容器

    FROM python:3.10-slim
    
    RUN apt-get update && apt-get install -y \
        fonts-wqy-microhei \
        fonts-wqy-zenhei \
        fonts-noto-cjk \
        fonts-noto-cjk-extra
    
    COPY . /app
    WORKDIR /app
    RUN pip install -r requirements.txt
    
  2. 升级 Matplotlib

    pip install --upgrade matplotlib
    
  3. 使用虚拟环境

    python -m venv .venv
    source .venv/bin/activate
    pip install matplotlib
    
  4. 使用 SVG 后端

    import matplotlib
    matplotlib.use('SVG')  # 使用 SVG 后端
    

通过以上步骤,您应该能够彻底解决 “findfont: Generic family ‘sans-serif’ not found because none of the following families were found: simhei” 错误。如果问题仍然存在,请考虑使用网页截图替代方案或改用英文标签。

你可能感兴趣的:(Python学习,运维,服务器,linux)