`mpl_utils` 会在导入时自动注册自定义的 Matplotlib 样式（如果存在），并应用默认样式 `zyp-default`。

```python
import numpy as np
import matplotlib.pyplot as plt
from zyplib.vis import mpl_utils

# 假设你的库已经自动应用了 'zyp-default' 样式
# 如果没有，可以手动设置：plt.style.use('zyp-default')
```

### 1. 快速创建子图网格 (`make_grids`)


**场景**：创建一个 2x3 的网格，并在每个子图中绘制一条随机游走曲线。

```python
# 使用 make_grids 迭代每个子图
for i, j, ax in mpl_utils.make_grids(row=2, col=3, figsize=(12, 6)):
    # i, j 是子图的行列索引
    ax.plot(np.random.randn(100).cumsum())
    ax.set_title(f'Plot ({i}, {j})')

# make_grids 默认会在循环结束后调用 plt.show()
```

### 2. 统一图表风格 (`style_ax` & `style_figure`)

* `style_ax`: 精细控制单个子图的样式。
* `style_figure`: 一次性对整个 Figure（包括所有子图）应用统一的样式。

**场景**：创建一个 2x2 的图表，并在所有绘图完成后，应用统一的标题、坐标轴标签和网格样式。

```python
# 1. 创建 Figure 和 Axes 对象
#    注意：这里我们将 fn_finalize 设置为 None，因为我们想在后面手动控制
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 2. 在每个子图上绘图
for ax in axes.flat:
    ax.plot(np.random.randn(50).cumsum(), label='Sample Data')

# 3. 所有绘图完成后，使用 style_figure 进行“一键美化”
mpl_utils.style_figure(
    fig,
    suptitle='Main Title for the Entire Figure',
    suptitle_fontsize=20,
    # 以下参数将传递给每个子图的 style_ax 函数
    xlabel='Time Step',
    ylabel='Value',
    grid=True,
    grid_style=':',
    legend_loc='upper left'
)

# 4. 手动显示或保存
plt.show()
```

### 3. 创建复杂的命名布局 (`make_figure_layout`)

对于非网格的复杂布局（例如，一个图占左边，两个图占右边），`subplot_mosaic` 是最佳选择。`make_figure_layout` 将其封装在了一个方便的上下文管理器中。

**场景**：创建一个布局，其中左侧是一个大的主图 (`'main'`)，右侧是两个小的附图 (`'hist_x'` 和 `'hist_y'`)。

```python
layout = [
    ['main', 'hist_x'],
    ['main', 'hist_y'],
]

# 使用上下文管理器，可以确保 figure 资源被正确管理
with mpl_utils.make_figure_layout(layout, figsize=(10, 5)) as (fig, axes):
    # 通过名称访问子图
    x, y = np.random.randn(2, 100)

    # 主散点图
    axes['main'].scatter(x, y, c=np.arctan2(y, x))
    mpl_utils.style_ax(axes['main'], title='Main Scatter Plot', xlabel='X value', ylabel='Y value')

    # X 分布直方图
    axes['hist_x'].hist(x, bins=20)
    mpl_utils.style_ax(axes['hist_x'], title='X Distribution', hide_spines=['left', 'top', 'right'])

    # Y 分布直方图
    axes['hist_y'].hist(y, bins=20, orientation='horizontal')
    mpl_utils.style_ax(axes['hist_y'], title='Y Distribution', hide_spines=['bottom', 'top', 'right'])

# 退出 with 代码块后，默认会调用 plt.show()
```

### 4. 临时自定义样式和颜色 (`CombinedStyleContext` & `color_cycle_context`)

有时你只想为某张图临时改变样式或颜色循环，而不影响全局设置。`mpl_utils` 的上下文管理器为此而生。

**重要提示**：这些上下文管理器**必须在创建 Figure/Axes 之前**使用才能生效！

**场景**：在一张图中使用 `seaborn-v0_8-darkgrid` 风格，并自定义颜色循环为 `viridis` colormap 中的 5 种颜色。

```python
# 使用 color_cycle_context 从 colormap 'viridis' 中获取 5 种颜色
# 同时，使用 CombinedStyleContext 应用 'seaborn-v0_8-darkgrid' 基础样式
with mpl_utils.CombinedStyleContext(style='seaborn-v0_8-darkgrid'):
    with mpl_utils.color_cycle_context('viridis', n=5):

        # 在上下文管理器内部创建图表
        fig, ax = plt.subplots(figsize=(8, 5))

        # 现在的 plot 会自动使用新的颜色循环
        for i in range(5):
            ax.plot(np.arange(10) * (i+1), label=f'Line {i+1}')

        mpl_utils.style_ax(ax, title='Custom Style and Color Cycle', legend_loc='best')

# 离开 with 代码块后，Matplotlib 的样式和颜色循环会恢复原状
plt.show()
```

### 5. 保存图表 (`save_figure`)

`save_figure` 提供了一个标准化的接口来保存图表，它会自动创建目录并释放内存。你可以将其与 `fn_finalize` 参数结合使用，实现绘图和保存的无缝衔接。

**场景**：创建一个图表，然后将其保存到指定路径，而不是显示它。

```python
# 定义一个保存函数，它将接收 Figure 对象作为参数
# 我们使用 lambda 函数来简化
save_path = 'output/my_first_plot.png'
save_fn = lambda fig: mpl_utils.save_figure(fig, save_path, dpi=150)

# 在 make_grids 中指定 fn_finalize=save_fn
# 这样，在循环结束后，图表将自动被保存而不是显示
for i, j, ax in mpl_utils.make_grids(2, 1, figsize=(6, 8), fn_finalize=save_fn):
    ax.plot(np.random.rand(20), 'o-')
    mpl_utils.style_ax(ax, title=f'Saved Plot {i+1}', grid=False)

# 终端会打印: Figure saved to: output/my_first_plot.png
```

### 总结


* **快速布局**：使用 `make_grids` 和 `make_figure_layout`。
* **轻松定型**：使用 `style_ax` 和 `style_figure` 保证风格统一。
* **灵活定制**：使用 `CombinedStyleContext` 和 `color_cycle_context` 临时改变外观。
* **可靠输出**：使用 `save_figure` 和 `fn_finalize` 参数管理图表生命周期。
