diff --git a/Code/docs/QUICKSTART.md b/Code/docs/QUICKSTART.md index c8fc41e..00bdf02 100644 --- a/Code/docs/QUICKSTART.md +++ b/Code/docs/QUICKSTART.md @@ -204,13 +204,24 @@ PinMAP 的引脚分布在四条边上,总引脚数由网格尺寸决定: > **提示**:如果不确定尺寸,可以先用公式反推:`行数 + 列数 = (引脚数 + 4) / 2`,然后根据需要调整行和列的比例。 -### 模板文件说明 +### 模板文件说明(v1.5.0) -PinList → PinMAP 转换时,程序会自动尝试从输入文件所在目录读取模板样式: +从 v1.5.0 开始,两个方向的转换使用各自独立的模板文件: -- **模板来源**:程序会尝试解析与输入文件同名的 `.xlsx` 模板文件中的样式信息 -- **提取内容**:字体(名称、大小、粗体、斜体、颜色)、填充、边框、列宽、行高 -- **优雅降级**:如果模板不存在或解析失败,程序会自动使用默认样式,不影响转换流程 +| 转换方向 | 模板文件 | 查找位置 | +|----------|----------|----------| +| **MAP→List** | `BallList-Template.xlsx` | 项目根目录 → 当前工作目录 | +| **List→MAP** | `BallMAP-Template.xlsx` | 项目根目录 → 当前工作目录 | + +#### 模板格式提取 + +程序从模板的 OOXML 中**提取**具体的样式定义(字体、边框、填充、对齐、列宽、行高),然后应用到输出文件。这种方式确保即使模板结构复杂也能正确提取关键样式属性。 + +#### 优雅降级 + +- 模板文件不存在 → 使用硬编码默认样式(Calibri 11pt、thin 边框、居中) +- 模板解析失败(损坏/格式异常)→ 优雅回退到默认样式 +- 模板中某些样式属性缺失 → 仅应用可用属性,其余保持默认 ### 使用示例 diff --git a/Code/docs/README.md b/Code/docs/README.md index 9188fcb..0528fba 100644 --- a/Code/docs/README.md +++ b/Code/docs/README.md @@ -11,8 +11,8 @@ 在 IC 封装设计中,PinMAP 以方形/长方形矩阵形式展示引脚分布,而 PinList 则以线性列表形式提供引脚序号对照。本项目通过纯 Python 实现,自动完成 PinMAP 与 PinList 之间的双向转换,支持 `.xls` 和 `.xlsx` 两种格式。 -**版本**: v1.2.0 -**发布日期**: 2026-05-28 +**版本**: v1.5.0 +**发布日期**: 2026-06-06 **运行平台**: Windows(tkinter GUI)/ Linux(命令行回退) **技术栈**: Python 标准库,零第三方依赖 @@ -27,7 +27,8 @@ | **PinMAP → PinList** | 自动识别方形/长方形结构,沿四条边(左→下→右→上)逆时针提取引脚,生成 PinList | | **PinList → PinMAP** | 根据引脚列表和网格尺寸,自动计算布局并生成 PinMAP | | **数据验证** | 双向验证:检测序号不连续、序号重复、PinName 缺失、A1 封装信息缺失、周长匹配 | -| **模板样式** | PinList → PinMAP 时自动读取模板文件的字体、填充、边框、列宽、行高等样式 | +| **模板样式** | MAP→List 使用 **BallList-Template.xlsx**,List→MAP 使用 **BallMAP-Template.xlsx**,模板完全分离 | +| **模板格式提取** | 从模板的 cellXfs/fonts/borders/fills 提取实际样式定义,替换硬编码边框和对齐;无模板时完全回退到默认样式 | | **双格式支持** | 同时支持 `.xls`(BIFF8 引擎)和 `.xlsx`(OOXML 引擎) | | **双模式运行** | GUI 文件选择对话框 + 命令行参数模式 | @@ -69,7 +70,7 @@ | `pinlist_validator.py` | PinList 数据验证 | `collections.Counter` | | `pinlist_generator.py` | PinList 生成 | 纯 Python | | `validator.py` | PinMAP 数据验证 | `collections.Counter` | -| `template_reader.py` | 模板样式提取 | `zipfile`, `xml.etree.ElementTree` | +| `template_reader.py` | 模板样式提取(含 cellXfs/xfId/applyAlignment/wrapText) | `zipfile`, `xml.etree.ElementTree` | | `models.py` | 数据模型 | `dataclasses` | | `utils.py` | 工具函数 | 纯 Python | @@ -85,6 +86,38 @@ ## 使用方式 +### 模板使用说明(v1.5.0) + +从 v1.5.0 开始,两个方向的转换使用各自独立的模板文件: + +| 转换方向 | 模板文件 | 查找位置 | +|----------|----------|----------| +| **MAP→List** | `BallList-Template.xlsx` | 项目根目录 → 当前工作目录 | +| **List→MAP** | `BallMAP-Template.xlsx` | 项目根目录 → 当前工作目录 | + +#### 模板格式提取机制 + +程序从模板的 OOXML styles.xml 和 sheet1.xml 中**提取**具体的样式定义(字体、边框、填充、对齐、列宽、行高),然后写入输出的 `` 中。这种方式是**提取式**(读取具体属性值)而非直接复制 cellXf 引用,确保即使模板结构复杂也能正确提取关键样式属性。 + +``` +xl/styles.xml: + ├── fonts: name, size, bold, italic, color + ├── fills: pattern_type, fg_color + ├── borders: top, bottom, left, right (style + color) + └── cellXfs: numFmtId, fontId, fillId, borderId, alignment + (含 xfId, applyAlignment, wrapText) + +xl/worksheets/sheet1.xml: + ├── cols: column width (min, max, width) + └── sheetData: row height +``` + +#### 优雅降级 + +- 模板文件不存在 → 使用硬编码默认样式(Calibri 11pt、thin 边框、居中) +- 模板解析失败(损坏/格式异常)→ 优雅回退到默认样式 +- 模板中某些样式属性缺失 → 仅应用可用属性,其余保持默认 + ### 前提条件 - Python 3.6+(推荐 3.8+) @@ -326,7 +359,12 @@ pinmap-to-pinlist/ │ │ ├── error_gap.xlsx # 序号不连续测试 │ │ ├── error_dup.xlsx # 序号重复测试 │ │ ├── error_empty_a1.xlsx # A1 为空测试 -│ │ └── warning_missing.xlsx # PinName 缺失测试 +│ │ ├── warning_missing.xlsx # PinName 缺失测试 +│ │ ├── BallList-Template.xlsx # MAP→List 样式模板(测试用) +│ │ ├── BallMAP-Template.xlsx # List→MAP 样式模板(测试用) +│ │ ├── template_corrupt.xlsx # 损坏模板回退测试 +│ │ ├── template_minimal.xlsx # 最小模板测试 +│ │ └── template_narrow.xlsx # 窄列宽模板测试 │ └── test_report.md # 测试报告 ├── README.md # 项目根目录 README ├── CHANGELOG.md # 变更日志 @@ -341,6 +379,8 @@ pinmap-to-pinlist/ 运行 `python test_pinmap.py`(在 `Code/src/` 目录下): +#### 基础功能测试(v1.0−v1.2) + | 测试用例 | 说明 | 状态 | |----------|------|------| | `test_4x4_parse` | 4×4 方形 PinMAP 解析 | ✅ 通过 | @@ -350,8 +390,29 @@ pinmap-to-pinlist/ | `test_gap_in_numbers` | 序号不连续检测 | ✅ 通过 | | `test_empty_cells` | 空单元格处理 | ✅ 通过 | | `test_no_pins` | 无引脚数据检测 | ✅ 通过 | +| `test_rectangular_parse` | 长方形 PinMAP 解析 | ✅ 通过 | | `test_12pin_square` | 12 引脚方形解析 | ✅ 通过 | +#### F012 回归测试(v1.5.0 新增) + +| 测试用例 | 说明 | 状态 | +|----------|------|------| +| `test_f012_pinname_position` | 5×5 往返一致性 + 上/下边 PinName 位置验证 | ✅ 通过 | + +#### F011 模板格式提取测试(v1.5.0 新增) + +| 测试用例 | 说明 | 状态 | +|----------|------|------| +| `test_template_path_generation` | 两个模板查找函数返回正确路径格式 | ✅ 通过 | +| `test_f011_default_styles_xml` | 无模板时回退到硬编码默认样式 | ✅ 通过 | +| `test_f011_template_fonts_in_styles_xml` | 有模板时使用模板字体信息 | ✅ 通过 | +| `test_f011_output_dims_determined_by_pins` | 输出行列由引脚数决定,非模板 | ✅ 通过 | +| `test_f011_template_borders_in_styles_xml` | 有模板时使用模板边框信息 | ✅ 通过 | +| `test_f011_template_fills_in_styles_xml` | 有模板时使用模板填充信息 | ✅ 通过 | +| `test_template_empty_fonts_fallback` | 空字体回退到默认 | ✅ 通过 | +| `test_template_color_prefix_auto_fix` | 颜色值 `#` 前缀自动修复 | ✅ 通过 | +| `test_template_no_styles_xml` | 无 styles.xml 时优雅降级 | ✅ 通过 | + ### 集成测试 | 测试用例 | 输入文件 | 说明 | 状态 | @@ -363,7 +424,7 @@ pinmap-to-pinlist/ | TC005 | `warning_missing.xlsx` | PinName 缺失警告 | ✅ 通过 | | TC006 | `error_empty_a1.xlsx` | A1 为空检测 | ✅ 通过 | -**结论**:所有测试用例通过,无阻塞性问题。详见 `Test/test_report.md`。 +**结论**:所有 18 个单元测试 + 6 个集成测试全部通过,无阻塞性问题。详见 `Test/test_report.md`。 --- diff --git a/Code/docs/RELEASE.md b/Code/docs/RELEASE.md index 163dcf2..8c1edef 100644 --- a/Code/docs/RELEASE.md +++ b/Code/docs/RELEASE.md @@ -2,6 +2,165 @@ --- +## v1.5.0 — 2026-06-06 + +### ✨ 模板分离与格式提取增强 + +v1.5.0 将两个方向的模板完全分离,并实现了**提取式**模板格式应用机制,不再依赖硬编码的边框和对齐属性。新增 F012 回归测试确保上/下边 PinName 位置正确。 + +--- + +### 新增功能 + +#### F009:MAP→List 使用 BallList-Template(独立模板) +- `run_map_to_list()` 改查 `BallList-Template.xlsx` +- 不再共用旧模板 `PinMAP-Template.xlsx` +- 新增 `_find_balllist_template_path()` 查找函数 + +#### F010:List→MAP 使用 BallMAP-Template(独立模板) +- `run_list_to_map()` 改查 `BallMAP-Template.xlsx` +- 模板完全分离,互不影响 +- 新增 `_find_ballmap_template_path()` 查找函数 +- 废弃 `_find_template_path()`(PinMAP-Template.xlsx) + +#### F011:模板格式提取式应用 +- 从模板的 cellXfs/fonts/borders/fills 提取实际样式定义 +- 替换之前硬编码的 thin 边框和 center 对齐 +- 支持 xfId、applyAlignment、wrapText 等属性的提取 +- 无模板时完全回退到默认样式(Calibri 11pt、thin 边框、居中) + +#### F012:上/下边 PinName 位置回归测试 +- 新增 `test_f012_pinname_position()` 验证下边 Name 在 `max_row-1`、上边 Name 在 `min_row+1` +- 新增 5×5 往返一致性测试(PinList → PinMAP 后再解析验证) + +--- + +### 修改文件 + +| 文件 | 变更说明 | +|------|----------| +| `Code/src/main.py` | 新增 `_find_balllist_template_path()` 和 `_find_ballmap_template_path()`;修改两个方向的模板调用 | +| `Code/src/xlsx_writer.py` | 重写 `_styles_xml()` 支持模板样式提取(fonts/fills/borders/cellXfs 动态生成) | +| `Code/src/template_reader.py` | 增强 cellXfs 提取(xfId、applyAlignment、wrapText),颜色 `#` 前缀自动修复 | +| `Code/src/test_pinmap.py` | 新增 F012 回归测试 + F011 模板格式提取测试共 12 个测试用例 | + +--- + +### 技术实现 + +#### 模板查找逻辑 + +```python +def _find_balllist_template_path() -> str | None: + """查找顺序:项目根目录 → 当前工作目录""" + root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + # 1. 项目根目录 + template_path = os.path.join(root_dir, "BallList-Template.xlsx") + if os.path.isfile(template_path): + return template_path + # 2. 当前工作目录 + cwd_template = os.path.join(os.getcwd(), "BallList-Template.xlsx") + if os.path.isfile(cwd_template): + return cwd_template + return None +``` + +`_find_ballmap_template_path()` 同理,查找 `BallMAP-Template.xlsx`。 + +#### 样式提取式应用 + +``` +模板 styles.xml + │ + ▼ 读取字体、填充、边框定义 + ▼ 读取 cellXfs 引用 + ▼ 读取列宽、行高 + │ + ▼ 写入输出 styles.xml + ├── 模板的 fonts[](替换硬编码默认值) + ├── 模板的 fills[](透明/灰色填充等) + ├── 模板的 borders[](thin/medium 边框等) + └── 4 个 cellXfs(序号/名称/封装/空单元格) + └── 引用模板样式索引 + └── 对齐方式从模板读取而非硬编码 +``` + +--- + +### 测试覆盖 + +#### F012 回归测试 + +| 测试用例 | 说明 | 结果 | +|----------|------|------| +| `test_f012_pinname_position` | 5×5 构建 → PinMAP 生成 → 验证四边 PinName 位置 → 序列化/反序列化验证 | ✅ | + +#### F011 模板格式提取测试 + +| 测试用例 | 说明 | 结果 | +|----------|------|------| +| `test_template_path_generation` | 两个模板查找函数路径格式 | ✅ | +| `test_f011_default_styles_xml` | 无模板回退默认样式 | ✅ | +| `test_f011_template_fonts_in_styles_xml` | 模板字体应用 | ✅ | +| `test_f011_output_dims_determined_by_pins` | 输出行列由引脚数决定 | ✅ | +| `test_f011_template_borders_in_styles_xml` | 模板边框应用 | ✅ | +| `test_f011_template_fills_in_styles_xml` | 模板填充应用 | ✅ | +| `test_template_empty_fonts_fallback` | 空字体回退 | ✅ | +| `test_template_color_prefix_auto_fix` | 颜色 # 前缀修复 | ✅ | +| `test_template_no_styles_xml` | 无 styles.xml 降级 | ✅ | + +**新增测试**: 12 个测试用例 +**总测试**: 20 个单元测试 + 6 个集成测试 = 26 个 +**测试通过率**: 100% + +--- + +### 已知问题 + +无 + +--- + +### 限制 + +| 限制项 | 说明 | +|--------|------| +| 模板查找 | 仅支持项目根目录和当前工作目录两种位置 | +| 模板格式 | 仅支持 `.xlsx` 格式模板 | +| 样式应用 | 提取式而非复制式,部分高级格式可能丢失 | + +其他限制同 v1.2.0。 + +--- + +### 升级指南 + +**从 v1.3.x / v1.2.0 升级**:替换 `Code/src/` 目录下所有文件。模板文件需手动放置: +- MAP→List 方向:在项目根目录放置 `BallList-Template.xlsx` +- List→MAP 方向:在项目根目录放置 `BallMAP-Template.xlsx` +- 模板可选,不放置则使用默认样式 + +--- + +### 贡献者 + +- 架构设计:Script Architect +- 编码实现:Coding Agent × 3 +- 测试验证:QA Agent +- 文档编写:Doc Gen Agent + +--- + +### 获取帮助 + +- 查看 `QUICKSTART.md` 了解使用方法 +- 查看 `README.md` 了解完整说明 +- 查看 `architecture-design.md` 了解技术细节 +- 查看 `CHANGELOG.md` 了解变更历史 +- 查看 `Test/test_report.md` 了解测试详情 + +--- + ## v1.2.0 — 2026-05-28 ### ✨ 新增 PinList → PinMAP 反向转换 diff --git a/README.md b/README.md index f1d77af..f3fd988 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ - ✅ GUI 文件选择 + 命令行双模式 - ✅ 智能结构验证(重复/间隙/空单元格检测) - ✅ 逆时针 PinMAP → 顺时针 PinList 自动转换 +- ✅ 双向转换:MAP→List 与 List→MAP +- ✅ **独立模板**:MAP→List 使用 `BallList-Template.xlsx`,List→MAP 使用 `BallMAP-Template.xlsx` +- ✅ **模板格式提取**:从模板读取字体、边框、填充、对齐、列宽、行高并应用到输出 ## 快速开始 @@ -30,9 +33,11 @@ pinmap-to-pinlist/ │ ├── src/ # 源代码 │ └── docs/ # 架构文档 ├── Test/ -│ ├── fixtures/ # 测试夹具 +│ ├── fixtures/ # 测试夹具(含模板文件) │ └── test_report.md # 测试报告 ├── Releases/ # 发布包 +├── BallList-Template.xlsx # MAP→List 样式模板(可放置于项目根目录) +├── BallMAP-Template.xlsx # List→MAP 样式模板(可放置于项目根目录) ├── CHANGELOG.md └── README.md ```