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

如何阻止Matplotlib在PyInstaller打包的MacOS应用首次运行时构建字体缓存?

解决PyInstaller打包MacOS应用后Matplotlib字体缓存构建问题

Matplotlib首次运行时扫描系统字体并构建缓存的行为,哪怕指定了自定义字体也会触发,要彻底避免这个问题,需要从阻止系统字体扫描和预生成专属字体缓存两个方向入手,以下是具体可行的方案:

方案一:预生成字体缓存并打包(最可靠)

提前在开发环境生成仅包含你捆绑的DejaVuSans.ttf的字体缓存,然后将缓存文件打包进应用,让Matplotlib直接加载预生成的缓存,跳过扫描步骤:

  1. 生成专属字体缓存
    创建一个临时脚本(比如generate_font_cache.py),运行它生成仅包含目标字体的缓存:
import matplotlib
from matplotlib import font_manager
import os

# 清空默认字体配置,只加载我们的字体
font_manager.fontManager.ttflist.clear()
font_manager.fontManager._ttf_cache.clear()

font_path = "resources/DejaVuSans.ttf"
font_manager.fontManager.addfont(font_path)
font_name = font_manager.FontProperties(fname=font_path).get_name()

# 设置默认字体参数
matplotlib.rcParams.update({
    'font.family': font_name,
    'font.sans-serif': [font_name],
    'font.serif': [font_name],
    'font.monospace': [font_name]
})

# 强制生成并保存缓存
cache_dir = "./matplotlib_cache"
os.makedirs(cache_dir, exist_ok=True)
font_manager.fontManager.save(os.path.join(cache_dir, "fontlist-v330.json"))

注意:fontlist-v330.json中的版本号(v330)可能随Matplotlib版本变化,运行脚本后查看生成的文件名,确保后续引用正确。

  1. 打包缓存文件到应用
  • 如果使用.spec文件打包,在datas字段中添加缓存和字体文件的路径映射:
datas=[
    ('resources/DejaVuSans.ttf', 'resources'),
    ('matplotlib_cache/fontlist-v330.json', 'matplotlib_cache')
],
  • 如果用命令行打包,使用--add-data参数:
pyinstaller --add-data "resources/DejaVuSans.ttf:resources" --add-data "matplotlib_cache/fontlist-v330.json:matplotlib_cache" your_main_script.py
  1. 应用启动时加载预生成缓存
    在你的主脚本开头,先设置Matplotlib加载预打包的缓存,再导入pyplot:
import matplotlib
import os
from matplotlib import font_manager

# 获取应用所在目录
app_dir = os.path.dirname(os.path.abspath(__file__))

# 指定缓存路径并加载预生成的缓存
cache_file = os.path.join(app_dir, "matplotlib_cache/fontlist-v330.json")
matplotlib.rcParams['font.cache_path'] = os.path.dirname(cache_file)
font_manager.fontManager = font_manager._load_fontmanager(cache_file)

# 再次确认字体配置(可选,但能避免意外)
font_path = os.path.join(app_dir, "resources/DejaVuSans.ttf")
font_name = font_manager.FontProperties(fname=font_path).get_name()
matplotlib.rcParams.update({
    'font.family': font_name,
    'font.sans-serif': [font_name],
    'font.serif': [font_name],
    'font.monospace': [font_name]
})

# 最后导入pyplot
import matplotlib.pyplot as plt

方案二:直接阻止Matplotlib扫描系统字体

通过覆盖Matplotlib内部的字体扫描函数,让它完全忽略系统字体,只关注你捆绑的字体:

import matplotlib
from matplotlib import font_manager

# 清空现有字体列表
font_manager.fontManager.ttflist.clear()
font_manager.fontManager._ttf_cache.clear()

# 阻止Matplotlib读取系统fontconfig和扫描系统字体
font_manager._get_fontconfig_fonts = lambda: []
font_manager.findSystemFonts = lambda paths=None, fontext='ttf': []

# 添加并设置我们的捆绑字体
font_path = "resources/DejaVuSans.ttf"
font_manager.fontManager.addfont(font_path)
font_name = font_manager.FontProperties(fname=font_path).get_name()
matplotlib.rcParams.update({
    'font.family': font_name,
    'font.sans-serif': [font_name],
    'font.serif': [font_name],
    'font.monospace': [font_name]
})

# 导入pyplot
import matplotlib.pyplot as plt

注意:这种方法依赖Matplotlib的内部函数名,后续版本更新可能失效,建议优先使用方案一。

内容的提问来源于stack exchange,提问作者Martín Fixman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:12:40