115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
"""
|
|
plot_pub skill — 出版级 matplotlib rcParams 一键设置。
|
|
|
|
LLM 通过 `from skills.plot_pub.style import apply_pub_style; apply_pub_style()` 使用。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import warnings
|
|
import matplotlib
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib import font_manager
|
|
|
|
|
|
# 中文字体候选,按平台优先级排
|
|
_CHINESE_FONTS = [
|
|
"SimHei", # Windows 黑体
|
|
"Microsoft YaHei", # Windows 雅黑
|
|
"WenQuanYi Micro Hei", # Linux
|
|
"Heiti TC", # macOS
|
|
"Arial Unicode MS", # macOS 兜底
|
|
]
|
|
|
|
|
|
def _find_chinese_font() -> str | None:
|
|
"""从候选清单里挑系统装了的第一个,都没有返 None。"""
|
|
installed = {f.name for f in font_manager.fontManager.ttflist}
|
|
for name in _CHINESE_FONTS:
|
|
if name in installed:
|
|
return name
|
|
return None
|
|
|
|
|
|
def apply_pub_style(
|
|
chinese: bool = True,
|
|
dpi: int = 150,
|
|
font_size: int = 10,
|
|
linewidth: float = 1.2,
|
|
cmap: str = "viridis",
|
|
) -> None:
|
|
"""
|
|
一键设置出版级 matplotlib rcParams。
|
|
|
|
Args:
|
|
chinese: True 时加载中文字体,失败 warn 但不抛(图继续画,中文显示方块)
|
|
dpi: 屏幕显示 dpi。保存图请用 savefig(..., dpi=300) 单独指定
|
|
font_size: 基础字号,论文双栏图标准 10pt
|
|
linewidth: 线粗,出版图细些更精致
|
|
cmap: 默认 colormap,viridis / plasma / cividis 是色觉友好选择
|
|
"""
|
|
rc = matplotlib.rcParams
|
|
|
|
# ---- 字体 ----
|
|
if chinese:
|
|
font = _find_chinese_font()
|
|
if font:
|
|
# 中文字体放第一位,后面接 Arial / DejaVu 处理英文
|
|
rc["font.sans-serif"] = [font, "Arial", "DejaVu Sans"]
|
|
else:
|
|
warnings.warn(
|
|
"[plot_pub] 未找到中文字体(候选: "
|
|
+ ", ".join(_CHINESE_FONTS)
|
|
+ ")。中文将显示为方块。"
|
|
+ "Windows 控制面板 → 字体,Linux 装 wqy-microhei 包。",
|
|
RuntimeWarning,
|
|
stacklevel=2,
|
|
)
|
|
rc["font.sans-serif"] = ["Arial", "DejaVu Sans"]
|
|
else:
|
|
rc["font.sans-serif"] = ["Arial", "DejaVu Sans"]
|
|
|
|
rc["axes.unicode_minus"] = False # 负号不显示成方块
|
|
rc["font.family"] = "sans-serif"
|
|
rc["font.size"] = font_size
|
|
|
|
# ---- 尺寸 / dpi ----
|
|
rc["figure.dpi"] = dpi
|
|
rc["savefig.dpi"] = 300 # 默认保存高 dpi,临时图用 savefig(..., dpi=...) 覆盖
|
|
rc["savefig.bbox"] = "tight"
|
|
rc["savefig.pad_inches"] = 0.05
|
|
|
|
# ---- 线条 ----
|
|
rc["lines.linewidth"] = linewidth
|
|
rc["lines.markersize"] = 4
|
|
rc["axes.linewidth"] = 0.8
|
|
|
|
# ---- 刻度 ----
|
|
rc["xtick.direction"] = "in" # 期刊偏好刻度朝内
|
|
rc["ytick.direction"] = "in"
|
|
rc["xtick.major.size"] = 4
|
|
rc["ytick.major.size"] = 4
|
|
rc["xtick.minor.size"] = 2
|
|
rc["ytick.minor.size"] = 2
|
|
rc["xtick.minor.visible"] = True
|
|
rc["ytick.minor.visible"] = True
|
|
|
|
# ---- legend ----
|
|
rc["legend.frameon"] = False # 出版图 legend 无框
|
|
rc["legend.fontsize"] = font_size - 1
|
|
|
|
# ---- colormap ----
|
|
rc["image.cmap"] = cmap
|
|
|
|
# ---- 数学字体 ----
|
|
rc["mathtext.fontset"] = "stix" # 跟 Times / Arial 配,公式不突兀
|
|
|
|
# ---- 兜底:防止 PDF 嵌入 Type 3 字体(期刊要求 Type 42) ----
|
|
rc["pdf.fonttype"] = 42
|
|
rc["ps.fonttype"] = 42
|
|
|
|
|
|
def reset_style() -> None:
|
|
"""还原 matplotlib 默认 rcParams(测试 / 切换主题时用)。"""
|
|
matplotlib.rcdefaults()
|