返回 Skill 列表
extension
分类: 数据与分析无需 API Key

锐浪报表助手(支持自然语言生成报表模板,支持表格生成模板)

锐浪报表助手:支持自然语言生成报表模板,支持表格生成模板,支持导出各种格式,是锐浪报表组件用户的得力助手,基于锐浪报表6.X组件研发。

person作者: user_dfaee483hubcommunity

锐浪报表助手 - Grid++Report Helper

© 2026 范先生. 免责声明:本技能为基于公开文档与本人实测经验整理的第三方辅助工具,非锐浪软件(rubylong.cn)官方出品,与厂商无隶属或授权关系。Grid++Report 及其相关名称归各自权利人所有。

基于锐浪软件官方《Grid++Report 6 帮助文档》及本人实际使用过程的实测经验构建的 WorkBuddy 技能。

💡 调用成本低指引(必读)

本技能的全部知识已沉淀到本地 references/ 文档(API、对象模型、交叉表、脚本接口均已实测并本地化)。 调用本技能时,直接用下面这些本地文档解答,无需联网检索任何外部知识库。 本技能为离线自包含设计,所有知识均已内置,运行时不依赖任何外部知识库或网络服务。

⚠️ 关键事实 (2026-08 实测验证, Grid++Report 6.8.8.0)

以下事实与旧版官方文档/网上资料不同, 必须遵守, 否则生成的模板在设计器中打不开:

  1. COM ProgID: 引擎是 gregn.GridppReport, 设计器是 grdes.GRDesigner。 旧文档写的 GridppReport.GridppReport错的, Dispatch 会报"类字符串无效"。

  2. GRF 文件真实格式: JSON 文本格式(UTF-8 BOM), 顶层键为 Version/Title/Font/Printer/DetailGrid/PageHeader/PageFooter/Parameter。 不存在 "Object Report" DFM 风格文本格式——手写那种格式生成的文件设计器打不开。

  3. 禁止手写 GRF 文本: 一律通过 COM API 构建 + SaveToFile 保存, 由引擎自己序列化成合法 JSON。

  4. SetBounds 矩形语义: 参数是 (Left, Top, Right, Bottom), 不是 (Left, Top, Width, Height)! 必须用 grf_common.set_bounds(ctrl, left, top, width, height) 辅助函数。

  5. TextAlign 死循环陷阱: 只能赋合法枚举值 (17/18/20/33/34/36/65/66/68)。 赋 0 等非法值会让模板加载器死循环, 设计器直接卡死打不开。

  6. 安全加载: LoadFromStr/LoadFromFile 遇到非法模板会无报错卡死, 必须用 grf_common.safe_load_from_str/safe_load_from_file(线程+超时)。

  7. 路径必须反斜杠: LoadFromFile/SaveToFile 传正斜杠路径会静默失败。

  8. IGRFont 字号属性是 Point, 没有 Size 属性。

  9. 列必须设 col.Name = 字段名: 不设列名时 SaveToFile 虽会把全部标题/数据绑定 写入 JSON, 但引擎重新加载时只保留最后一列的标题与 DataField, 其余列在设计器中 空白(对照实验验证)。生成后须用 ColumnTitle.TitleCells/ColumnContent.ContentCells 逐格回读验证标题与绑定。

  10. ⚠️ 页面设置(纸型/边距/偏移)必须用 Design* 设计态属性: IGRPrinter 上 大部分属性是运行时值(反映真实打印机驱动), 设置它们不会持久化进 .grf。 只有 Design* 前缀属性才被序列化: DesignPaperSize(256=自定义纸型)、 DesignPaperWidth/DesignPaperLength(自定义纸宽高,cm)、DesignLeft/Top/Right/BottomMarginDesignPaperOrientation(1纵向/2横向); 偏移量用非前缀的 PrintOffsetX/Y。 直接用 PaperSize/PaperWidth/LeftMargin 等设置, 保存后仍是默认 A4 —— 这正是 克隆模板"纸型变A4"的根因。统一用 grf_common.copy_printer(src.Printer, r) 复制。

  11. 新报表的 BackImageNone, 必须通过 Utility.CreatePicture() 创建再赋值: 不能 r.BackImage.LoadFromFile()(NoneType 报错), 也不能 r.BackImage = 文件路径字符串(类型不匹配)。 正确做法:

    pic = r.Utility.CreatePicture()
    pic.LoadFromFile(r'背景.png')
    r.BackImage = pic
    

    StaticBox 的边框用 box.Border.Styles = 15(四边), 而不是单元格的 BorderCustom

  12. 克隆复刻的 MemoBox(综合文字框) 边框必须显式复制: 每个控件都有 Border 子对象(IGRBorder)。 copy_control 必须复制 Styles/InnerStyles/InnerIndent/Shadow/ShadowColor/ShadowWidthBorder.Pen(颜色/线宽), 否则克隆后 MemoBox 边框丢失。 (已修复 grf_clone.pycopy_border, 复刻 COBILL_FULL2025.grf 后 58 个 MemoBox 边框与源一致)

  13. 静默导出枚举 GRExportType: gretXLS=1(旧版 .xls, OLE2) / gretTXT=2 / gretHTM=3 / gretRTF=4 / gretPDF=5 / gretCSV=6 / gretIMG=7(按输出扩展名决定 png/jpg/bmp)。 ExportDirect(GRExportType, FileName, ShowOptionDlg, DoneOpen)4 参数(非 2 参数)。 真 .xlsx 必须: opt = PrepareExport(1)opt.ToXlsxFormat = Trueopt.FileName = pathExport(1)ExportDirect(1,...) 会忽略 ToXlsxFormat, 只产 legacy .xls。(见 grf_export.py

  14. 脚本事件属性名不带 "On" 前缀: InitializeScript/ProcessBeginScript/PrintBeginScript/ PageStartScript/GlobalScript/... 是可写字符串属性(放 JScript 代码), 可持久化进 .grf。 (不是 OnInitializeScript; 注入/查询/清除见 grf_script.py + references/scripting.md

  15. 含 FreeGrid 的大模板嵌入 BackImage 必须两遍法: 直接 r.BackImage = pic 会被序列化丢弃 (真实 13×13 复刻模板实测丢失)。可靠做法: 先正常 SaveToFile → 重新 LoadFromFile → 赋 r.BackImage → 再 SaveToFiler.BackImage 赋值须在 InsertReportHeader() 之后。

  16. replica 复刻模式(票据/复杂表格)的控件选型: 主网格用单个 FreeGrid(type 13, ColumnCount/RowCount + CellAt(r,c)) 承载全部格子线 + 文字; 表格外自由文字(标题/公司名)用 MemoBox(type 8) 而非 StaticBox; 尽量用 fg.Dock=5(grdsFill) 停靠, 少用绝对位置(location)。 这贴合"少 StaticBox、多自由表格/MemoBox、多 Dock"的复刻要求。

前置条件

  • Grid++Report 6 已安装(下载: http://www.rubylong.cn/gridreport/download.htm )
  • Python 3.12+, pywin32 (pip install pywin32), openpyxl (pip install openpyxl)
  • 图片导入额外需要: opencv-python (pip install opencv-python) 及可选 OCR 库 (rapidocr-onnxruntime / rapidocr-openvino / easyocr / pytesseract)
  • COM组件已注册(安装Grid++Report后自动注册)

验证COM可用: python -c "import win32com.client; r=win32com.client.Dispatch('gregn.GridppReport'); print(r.IsBlank)"

七大功能模块

模块一: 创建报表模板 (/grf-create)

从零创建 .grf 报表模板文件。COM驱动, spec JSON 声明式定义, 生成后自动回读验证。

触发词: 创建报表模板、新建报表模板、生成grf文件、create report template

使用方式:

python scripts/grf_builder.py --spec spec.json --out report.grf

spec.json 格式:

{
  "title": "银鸿丝业2025年缫丝统计表",
  "paper": {"size": 9, "orientation": 2},
  "parameters": [
    {"name": "ReportMonth", "type": 1, "value": "4月"}
  ],
  "page_header": {
    "height": 2.2,
    "controls": [
      {"type": "static", "text": "报表标题", "left": 1, "top": 0.15, "width": 24, "height": 0.9,
       "font": "黑体", "point": 16, "bold": true, "align": "MiddleCenter"},
      {"type": "static", "parameter": "ReportMonth", "left": 11.5, "top": 1.3, "width": 3, "height": 0.6}
    ]
  },
  "columns": [
    {"field": "F_工场", "title": "工场", "type": 1, "width": 1.6, "align": "MiddleCenter"},
    {"field": "F_数量", "title": "数量", "type": 2, "width": 1.5}
  ],
  "group": {"field": "F_工场", "footer": {"label": "小计", "height": 0.55}},
  "page_footer": {"page_number": true, "left": 10.0}
}

spec 字段说明: | 键 | 必填 | 说明 | |----|------|------| | title | 否 | 报表标题 | | paper | 否 | size: 纸张号(9=A4); orientation: 1纵向 2横向 | | parameters | 否 | 参数数组, type 见参数类型枚举, StaticBox 可通过 parameter 键绑定显示 | | page_header | 否 | 页眉高度 + 控件数组(type: static/sysvar) | | columns | | 列数组: field(字段名,建议F_前缀) / title / type(字段类型枚举) / width(cm) / align | | group | 否 | 分组定义: field 分组字段, footer 组页脚汇总(label/height) | | page_footer | 否 | page_number: true 自动生成"第X页/共Y页" |

align 合法值: TopLeft/TopCenter/TopRight/MiddleLeft/MiddleCenter/MiddleRight/BottomLeft/BottomCenter/BottomRight

也可作为库使用:

from grf_builder import build_from_spec
r = build_from_spec(spec)
r.SaveToFile(r'D:\out\report.grf')  # 路径用反斜杠

模块二: Excel导入报表模板 (/grf-from-excel)

解析Excel文件, 提取表头/列宽/数据类型, 通过COM构建模板并回读验证, 同时生成配套JSON数据文件(推模式测试用)。

触发词: Excel导入报表、从Excel创建模板、excel转grf、import excel to report

使用方式:

# 基本转换(默认A4横向)
python scripts/excel_to_grf.py --xlsx template.xlsx --out report.grf

# 指定工作表 / 表头行 / 数据起始行
python scripts/excel_to_grf.py --xlsx template.xlsx --out report.grf \
  --sheet "Sheet1" --header-row 1 --data-start 2

# 指定分组列(第N列, 生成组页脚小计, 对应Excel小计行)
python scripts/excel_to_grf.py --xlsx template.xlsx --out report.grf --group-col 1

# 列宽缩放 / 纵向
python scripts/excel_to_grf.py --xlsx template.xlsx --out report.grf \
  --width-scale 1.2 --portrait

转换逻辑:

  1. openpyxl 解析: 表头(首行非空)、列宽(Excel字符宽×0.21→cm)、类型推断(扫描前50行)
  2. 中文表头 → 合法字段名(ASCII表头直接用, 否则 F_列号)
  3. COM构建明细网格模板, --group-col 时生成分组+组页脚汇总
  4. 同时输出同名 .json 数据文件({fields, records} 结构, 供 LoadData 测试)
  5. 回读验证(线程+超时保护), 失败则报错退出

模块三: 修改微调报表 (/grf-modify)

加载已有 .grf 模板, 通过COM接口修改后保存。所有修改走COM保证文件始终有效, 修改后自动回读验证。

触发词: 修改报表、微调报表模板、调整报表格式、modify report、edit grf

使用方式:

# 查看模板结构(参数/字段/列/分组)
python scripts/grf_modifier.py inspect --grf report.grf

# 命令行修改(就地保存)
python scripts/grf_modifier.py modify --grf report.grf \
  --set-col-width "F_数量=2.5" \
  --set-title-font "F_数量=宋体=12=bold" \
  --set-align "F_金额=MiddleRight"

# 批量修改(JSON配置)
python scripts/grf_modifier.py batch --grf report.grf --config changes.json

changes.json 格式:

{
  "col_width":    {"F_数量": 2.5, "F_金额": 3.0},
  "title_font":   {"F_数量": {"name": "宋体", "point": 12, "bold": true}},
  "content_font": {"F_金额": {"name": "Arial", "point": 11}},
  "align":        {"F_金额": "MiddleRight"},
  "rename_title": {"F_工场": "车间"},
  "add_param":    [{"name": "DeptName", "type": 1, "value": "全部"}]
}

列定位支持按字段名或标题匹配。修改直接写回原文件(修改前请自行备份)。

模块四: 克隆/复刻现有模板 (/grf-clone)

打开并解析一个已有 .grf, 通过 COM API 逐项重建出结构完全一致的等价模板。 用于"验证 Skill 能否吃下真实生产模板"或做模板迁移/备份。已实测: 成功复刻 COBILL_FULL2025.grf(参数驱动蚕茧收购票据, 86参数 + 单ReportHeader带63控件 + 293KB背景底图 + ImageList印章, 复刻后与原文件逐控件/参数/底图字节级一致, 0差异)。

触发词: 克隆报表、复刻模板、打开并解析grf、生成一样的文件、clone report

用法:

python scripts/grf_clone.py --src old.grf --out cloned.grf

实现要点(已踩坑验证):

  • 加载源用 gc.safe_load_from_file()(线程+超时, 防卡死)
  • 取报表头带: src.ReportHeaders.Item(h); 勿用 src.ReportHeader()(无显式插入时返回 None)
  • 控件原生属性(含枚举int)从源COM对象读出再写入新对象, 避免字符串/枚举转换坑
  • 底图 BackImageIGRPicture 对象, r.BackImage = src.BackImage 直接赋值即可
  • ImageList 若 ImageList.Add(img) 失败, 回退到 JSON 注入(仅搬运不透明 base64, 结构仍由引擎序列化保证有效)
  • 源 JSON 中省略的默认属性(如未写 Font/Visible=True)不要再显式写, 否则与原文件序列化不一致
  • 页面设置必须复制: 调用 gc.copy_printer(src.Printer, r), 内部用 Design* 设计态属性 还原纸型/宽高/边距/偏移(⚠️直接用 PaperSize/PaperWidth/LeftMargin 等非Design属性 设置后不会持久化, 克隆版会变成默认 A4 —— 已修复验证)
  • 仅复制模板结构, 不复制运行时数据连接/事件脚本(脚本需另行迁移)

模块五: 图片/表格截图导入报表模板 (/grf-from-image)

上传表格截图或表单照片, 自动解析布局并生成 .grf 模板。支持三种模式:

  • table 模式: 规则表格 → 明细网格报表(字段/列/标题)
  • bg_form 模式: 表单/票据/复杂表格 → 背景图 + 参数框叠加(套打)
  • replica 模式: 票据/复杂表格 → 真实文字 + 真实格子线复刻(无背景图, 可编辑)

触发词: 图片转报表、表格截图生成模板、照片导入报表、图片解析grf、image to grf、复刻票据、克隆图片表格

用法:

# 1) 规则表格
python scripts/image_to_grf.py analyze --image table.png --mode table --out table_spec.json
python scripts/image_to_grf.py build --spec table_spec.json --out table.grf

# 2) 票据/表单(背景图套打)
python scripts/image_to_grf.py analyze --image 票据.png --mode bg_form --out form_spec.json
# 人工编辑 form_spec.json, 给参数框命名/调整位置后
python scripts/image_to_grf.py build --spec form_spec.json --out form.grf

# 3) 票据/复杂表格完整复刻(文字+格线均为真实控件)
python scripts/image_to_grf.py analyze --image 票据.png --mode replica --out replica_spec.json
# 人工编辑 replica_spec.json 补全/修正文字、合并单元格后
python scripts/image_to_grf.py build --spec replica_spec.json --out replica.grf

# 3b) 复刻后把原图作为 BackImage 嵌入供人工核对位置(核对后可在设计器删除)
python scripts/image_to_grf.py build --spec replica_spec.json --out replica_bg.grf --bg

# 3c) 复刻时让主 FreeGrid 用 Dock=Fill 停靠(尽量少用绝对定位)
python scripts/image_to_grf.py build --spec replica_spec.json --out replica_fill.grf --dock fill

analyze 阶段输出 spec.json, 可直接人工审阅修改:

  • table 模式: 输出表头列宽、字段名、单元格原始像素坐标
  • bg_form 模式: 输出背景图路径 + 候选参数框(自动合并连续空单元格) + 静态标签(文字单元格)
  • replica 模式: 输出 grid_lines(横竖线像素坐标) + 网格单元格(row/col) + 空文字(等待人工/LLM 填入); 同时支持 free_cells 描述表格外标题/字段标签

replica 模式 spec 关键字段:

{
  "mode": "replica",
  "paper": {"size": 256, "width": 24.13, "height": 13.97, "orientation": 1},
  "image_size": {"width": 795, "height": 522},
  "grid_lines": {"h": [90, 121, ...], "v": [14, 63, ...]},
  "cells": [
    {"xi": [0, 1], "yi": [1, 3], "text": "正茧", "align": "MiddleCenter", "border": 15}
  ],
  "free_cells": [
    {"x": 170, "y": 18, "w": 360, "h": 25, "text": "绵阳天虹丝绸有限责任公司"}
  ],
  "back_image": "D:\\原图\\票据.png"
}
  • xi/yi: grid_lines 索引, 描述单元格跨哪些线(用于主 FreeGrid 的行列定位)
  • x/y/w/h: 绝对像素坐标, 用于表格外文字/标题(生成 MemoBox, 不用 StaticBox)
  • border: 15=四边边框, 0=无边框; border_width 可调整线粗
  • back_image: 原图路径(供 --bg 两遍法嵌入模板核对; 不写则 --bg 静默跳过)

replica 控件选型(贴合"少 StaticBox、多自由表格/MemoBox、多 Dock"要求):

  • 主网格 = 单个 FreeGrid(type 13): 按 grid_lines 算列宽/行高, 遍历 CellAt(r,c)Text/Border.Styles/ColSpan/RowSpan/TextFormat.TextAlign(注意: 对齐用 TextFormat.TextAlign 枚举 33=MiddleCenter, 不是 Alignment/HorzAlign/VertAlign)。
  • 表格外自由文字(公司名/标题) = MemoBox(type 8), 不用 StaticBox。
  • 停靠: --dock fillFreeGrid.Dock = 5(grdsFill), 否则按 grid bbox 绝对定位。
  • 单元格对齐枚举: TextAlign 合法值 17/18/20/33/34/36/65/66/68(同关键事实5)。

OCR 后端自动探测(按顺序): rapidocr_onnxruntimerapidocr_openvinoeasyocrpytesseract。均无安装时, 脚本仍输出布局结构, 文字留空由用户在 spec 中补全。也可显式 --ocr none 跳过 OCR。

重要提示:

  • 需要完全复刻原图文字和格子线(如票据模板)时, 走 replica 模式: 主网格用单个 FreeGrid 承载全部格子线 + 文字, 表格外自由文字用 MemoBox, 不使用背景图, 文字真实可编辑。
  • replica 对复杂合并单元格需要人工在 spec 中整理(典型流程: analyze 输出空网格 → 多模态/人工识别文字 → 编辑 spec 定义合并格与文字 → build)。
  • 复刻后想人工核对位置, 加 --bg: 用两遍法把原图嵌入 BackImage(见关键事实15), 设计器中对照无误后可删除 BackImage 并把单元格改回不透明。
  • 复杂表单(如含大量合并单元格的票据)若不需要编辑原文字, 也可走 bg_form 模式: 用原图做 BackImage, 再叠加参数框。
  • 像素→厘米换算默认按 96 DPI; 可用 --dpi 指定扫描件的实际 DPI。
  • bg_form/replica 生成后, 在设计器中打开微调即可投入使用。
  • 参考示例:
    • (示例:某内部票据背景图,绝对路径已脱敏)已用 replica 模式生成完整复刻模板 (FreeGrid + MemoBox, 文字与格线均为真实控件), 加 --bg 另存一版嵌入原图供核对, 两版均可正常加载。
    • 工作区验证产物示例: jianspiao_replica_v2.grf(纯控件复刻) 与 jianspiao_replica_v2_bg.grf(已嵌入原图, BackImage 字段=1)。

模块六: 模板静默导出 (/grf-export)

.grf 模板不弹窗导出为 PDF / 图片(PNG/JPG/BMP) / Excel(.xls/.xlsx) / CSV / HTML / RTF / TXT。基于 Grid++Report 6.x 引擎的 ExportDirectPrepareExport + Export 接口实现(枚举与坑见关键事实13)。

触发词: 导出PDF、导出图片、导出Excel、grf转pdf、grf转png、静默导出、export grf

使用方式:

# 指定格式(--format 可省, 由 --out 扩展名推断)
python scripts/grf_export.py --grf report.grf --out report.pdf --format pdf
python scripts/grf_export.py --grf report.grf --out report.png --format png
python scripts/grf_export.py --grf report.grf --out report.xlsx --format xlsx

# 由扩展名自动推断格式
python scripts/grf_export.py --grf report.grf --out report.xlsx

# 批量导出整个目录
python scripts/grf_export.py --grf-dir ./reports --out-dir ./out --format pdf

格式与实现要点: | 目标格式 | 参数 | 实现 | 备注 | |----------|------|------|------| | PDF | --format pdf | ExportDirect(5,...) | 静默生成 PDF | | PNG/JPG/BMP | --format png/jpg/bmp | ExportDirect(7,...) | 扩展名决定图像格式 | | XLSX(真) | --format xlsx | PrepareExport(1)+AsE2XLSOption.ToXlsxFormat=True+Export(1) | 注意: ToXlsxFormatAsE2XLSOption 子对象上, 不是 ExportDirect | | XLS(旧) | --format xls | ExportDirect(1,...) | legacy OLE2 .xls | | CSV/HTML/RTF/TXT | --format csv/html/rtf/txt | ExportDirect(6/3/4/2,...) | 文本类 |

注意事项:

  • 导出会"运行"报表生成输出。纯静态模板(票据/表单, 内容在报表头带)可直接导出。
  • 明细网格的模板若未绑定数据源, 导出的数据区可能为空; 本脚本侧重 "模板布局静默导出", 不负责注入业务数据(可先用 grf_modifier/grf_script 预处理)。
  • 图片导出(gretIMG)为整页渲染; 多页报表默认导出首页到单张图。
  • 已实测: exp_test.pdf(3.7K) / exp_test.png(94K) / exp_test.xlsx(5.0K) 均成功生成。

模块七: 事件脚本 / 自然语言转脚本 (/grf-script)

锐浪报表支持 JScript 事件脚本(在事件字符串属性上写代码, 见关键事实14)。 本模块提供脚本注入/查询/清除工具, 并沉淀"中文需求 → JScript"的映射, 让智能体 能按自然语言描述把逻辑写进 .grf(如"金额>1000 标红""标题拼接站点名")。

触发词: 报表脚本、注入脚本、脚本转脚本、自动加逻辑、动态改控件、自然语言转报表脚本

使用方式:

# 列出报表级可用事件脚本属性名
python scripts/grf_script.py list-events

# 查看某事件当前脚本
python scripts/grf_script.py show --grf report.grf --event InitializeScript

# 注入脚本(直接给代码)
python scripts/grf_script.py inject --grf report.grf --event InitializeScript \
  --code "Report.Title='新标题';"

# 注入脚本(从 .js 文件)
python scripts/grf_script.py inject --grf report.grf --event InitializeScript --file script.js

# 清空某事件脚本
python scripts/grf_script.py clear --grf report.grf --event InitializeScript

自然语言转 JScript 工作流(智能体执行):

  1. 用户用中文描述需求(如"结算金额>1000 时合计变红")。
  2. 智能体翻译成 JScript 片段(对象名用报表里真实的控件 Name / 参数名; 见下例)。
  3. grf_script.py inject 写入目标 .grf 的对应事件(默认 InitializeScript)。
  4. grf_export.py 导出 PDF/图片验证效果; 不满意改 --code 重新 inject。

常用映射示例(完整版见 references/scripting.md):

  • 设标题: Report.Title = '蚕茧收购凭据 - ' + Parameters("P_茧站").Value;
  • 计算填充: MemoBox_合计.Text = (Number(P_重量.Value) * Number(P_单价.Value)).toFixed(2);
  • 条件标红: if (Number(P_金额.Value) > 1000) MemoBox_合计.ForeColor = 0xFF0000;
  • 隐藏空控件: if (!Parameters("P_身份证号").Value) MemoBox_身份证号.Visible = false;
  • 写当前日期: MemoBox_日期.Text = new Date().getFullYear() + '-' + (new Date().getMonth()+1) + '-' + new Date().getDate();

脚本注意事项:

  • 语法是 JScript: 变量 var、字符串拼接 +、注释 //; 颜色用 0xFF0000(红)。
  • 参数/字段取值多为字符串, 数值运算前用 Number(...) 转换。
  • 注入后务必 grf_script.py show 回读验证, 并导出一张图确认效果。
  • 控件/参数必须在模板中已存在(Name 正确), 否则脚本运行报错。

主从表(子报表)支持

Grid++Report 原生支持主从表,通过子报表(SubReport)部件框实现(控件类型 9, gc.CT_SUBREPORT)。 一个报表只能有一个明细网格,主从/表中表/多表格布局都靠子报表实现。已实测 COM 程序化构建可行:

import grf_common as gc
r = gc.create_report(); r.Clear(); r.Unit = 1

# 主表: 常规明细网格 + 分组
g = r.InsertDetailGrid()
g.Recordset.AddField('F_单号', gc.FT_STRING)
grp = g.Groups.Add(); grp.ByFields = 'F_单号'
hdr = grp.Header                        # 也可放在 Footer/页眉等

# 在组头放子报表控件 -> .Report 返回完整的子报表对象(嵌入式定义)
sub_ctrl = hdr.Controls.Add(gc.CT_SUBREPORT).AsSubReport
sub_ctrl.CanGrow = True                 # 子报表数据动态变化, 应设可伸展
sub = sub_ctrl.Report
sub.Clear(); sub.Unit = 1

# 子表: 自己的明细网格
sg = sub.InsertDetailGrid()
sg.Recordset.AddField('F_品名', gc.FT_STRING)
# ... 加列同主表(记得 col.Name = 字段名)

# 主从关联(关键): 子报表参数与主表字段【同名】即可自动取值
p = sub.Parameters.Add()               # Add()无参, 返回IGRParameter
p.Name = 'F_单号'                       # 与主表字段同名
p.DataType = gc.PT_STRING              # 参数类型属性是 DataType(不是ParamType)

主从数据关联机制(引擎内置约定):

  • 子报表运行时, 其参数自动从主报表中同名参数或记录集字段取值
  • 拉模式: 子报表用参数化查询SQL(SQL中引用同名参数)即自动实现主从过滤
  • 推模式: 在子报表的 Initialize/FetchRecord 事件中取主表值填充数据(B/S下用 LoadDataFromURL)
  • sub_ctrl.RelationFields 属性也提供主从字段关联(设计器可视化设置用)

子报表设计原则(官方FAQ):

  1. 尽量少用——能用分组报表替代就不要用子报表(性能代价大, 明细网格中的子报表尤甚)
  2. 仅报表头尾的动态数据 → 用报表参数或直接给部件框设值, 不用子报表
  3. 垂直并行的多个子报表放不同报表节, 一节只放一个
  4. 含子报表的报表用打印预览(PrintViewer)显示, 不能用查询显示器(DisplayViewer)

其他要点:

  • 子报表关联外部模板: sub_ctrl.ReportFile = r'从表.grf'; 运行时编程关联用 sub_ctrl.Report
  • 独立子报表(报表头/尾中唯一部件框 + ParentPageSettings=False): 可集中打印多个不同纸张/方向的报表, ToNewExcelSheet=True 可导出到独立Excel工作表
  • 子报表不定义连接串时自动继承主报表连接串

交叉表(CrossTab)支持

Grid++Report 原生支持交叉表(行列二维交叉统计), 构建在明细网格之上 —— 先像普通报表 定义明细网格, 再 g.IsCrossTab = True 并设定交叉属性。详细机制/属性表/避雷见 references/crosstab.md(已实机验证)。核心代码:

g = r.InsertDetailGrid()
g.Recordset.AddField('F_客户', gc.FT_STRING)
g.Recordset.AddField('F_产品', gc.FT_STRING)
g.Recordset.AddField('F_金额', gc.FT_FLOAT)   # 度量列必须是数字类型!
# 列顺序: 纵向交叉列 -> 横向交叉列 -> 度量列 (记得 col.Name=字段名)
g.IsCrossTab = True                            # 必须先置True, 否则 g.CrossTab=None
ct = g.CrossTab
ct.VCrossFields = 'F_客户'                      # 纵向交叉字段(逗号串, 可多维度)
ct.HCrossFields = 'F_产品'                      # 横向交叉字段(逗号串)
ct.TotalCols   = 1                             # 末尾1个度量列转横向合计列(int计数!非字段名)
r.SaveToFile(r'D:\out\crosstab.grf')

交叉表避雷(高频坑):

  • TotalCols/SubtotalCols整数计数(不是字段名!), 赋字符串报"类型不匹配"
  • 合计列字段必须为数字类型, 否则只复制不累加
  • 横向/纵向重排序(HResort/VResort)不能同时设为否
  • 度量列 = 非交叉字段; ListCols 只读计数, 不可赋值

一维分组小计用分组报表即可, 不要上交叉表(更简单/性能更好)。

脚本说明

| 脚本 | 功能 | 依赖 | |------|------|------| | scripts/grf_common.py | 共享模块: ProgID/枚举常量/set_bounds/set_font/安全加载/build_column/build_summary_footer | pywin32 | | scripts/grf_builder.py | 从spec JSON创建模板 | pywin32 | | scripts/excel_to_grf.py | Excel导入转换 | openpyxl + pywin32 | | scripts/grf_modifier.py | 查看inspect/修改modify/批量batch | pywin32 | | scripts/grf_clone.py | 克隆/复刻现有GRF(解析+等价重建) | pywin32 | | scripts/image_to_grf.py | 图片/表格截图导入模板(table/bg_form/replica, replica=FreeGrid+MemoBox+Dock+--bg两遍法) | opencv-python + OCR(可选) + pywin32 | | scripts/grf_export.py | 模板静默导出 PDF/PNG/XLSX/CSV/HTML/RTF/TXT(单文件/批量) | pywin32 | | scripts/grf_script.py | 事件脚本(JScript) 注入/查询/清除(自然语言转脚本) | pywin32 | | scripts/_gen_examples.py | 重新生成示例模板 | 全部 |

| 参考文档 | 内容 | |----------|------| | references/api_reference.md | 完整API参考(含实测修正) | | references/object_model.md | 对象模型与GRF格式说明 | | references/crosstab.md | 交叉表机制/属性/避雷(已实机验证) | | references/scripting.md | 脚本能力: JScript本质/可访问对象/事件速查/中文→JScript示例映射/注意事项 |

关键API速查

COM ProgID (实测正确)

  • 报表引擎: gregn.GridppReport
  • 设计器: grdes.GRDesigner
  • Python创建: win32com.client.Dispatch('gregn.GridppReport')

编程式定义模板流程

import win32com.client
Report = win32com.client.Dispatch('gregn.GridppReport')
Report.Clear()
Report.Unit = 1                                    # cm
DetailGrid = Report.InsertDetailGrid()             # 插入明细网格
DetailGrid.Recordset.AddField('F_数量', 2)         # 加字段(grftInteger=2)
col = DetailGrid.Columns.Add()                     # 加列(自动生成标题格/内容格)
col.Name = 'F_数量'                                # ⚠️必须设列名, 否则重载后标题/绑定丢失
col.Width = 2.5
col.TitleCell.Text = '数量'
col.ContentCell.DataField = 'F_数量'
grp = DetailGrid.Groups.Add()                      # 加分组
grp.ByFields = 'F_工场'
ph = Report.InsertPageHeader()                     # 页眉
box = ph.Controls.Add(1).AsStaticBox               # 1=StaticBox
Report.SaveToFile(r'D:\out\report.grf')            # 路径必须反斜杠

常用枚举 (完整定义见 scripts/grf_common.py)

字段类型 grftFieldType: 1=String, 2=Integer, 3=Float, 4=Currency, 5=Boolean, 6=DateTime

参数类型 grptParamType: 1=String, 2=Integer, 3=Float, 5=Boolean, 6=DateTime, 9=Calendar

文本对齐 grtaTextAlign (⚠️只能用这些值, 赋其他值会导致模板加载死循环): 17=TopLeft, 18=TopCenter, 20=TopRight, 33=MiddleLeft, 34=MiddleCenter, 36=MiddleRight, 65=BottomLeft, 66=BottomCenter, 68=BottomRight

汇总函数 grsfSummaryFun: 1=Sum, 2=Avg, 3=Count, 4=Min, 5=Max

系统变量 grsvSystemVar: 1=当前日期时间, 2=总页数, 3=页号, 4=记录号, 19=记录总数

控件类型 grctControlType: 1=StaticBox, 2=ShapeBox, 3=SystemVarBox, 4=FieldBox, 5=SummaryBox, 6=RichTextBox, 7=PictureBox, 8=MemoBox, 9=SubReport, 10=Line, 11=Chart, 12=Barcode, 13=FreeGrid

纸张/方向/单位: 9=A4, 8=A3; 1=纵向, 2=横向; 1=cm, 2=inch

易错点备忘

  • 加列必须 col.Name = 字段名, 否则保存重载后只有最后一列标题/绑定存活(见关键事实9)
  • SetBounds(L, T, R, B) 是矩形坐标——用 gc.set_bounds() 包装宽高语义
  • IGRFont.Point 是字号, 无 Size 属性
  • 网格单元格边框用 BorderCustom=15(四边); 组页脚用 Footer.PrintGridBorder=True
  • StaticBox 边框用 box.Border.Styles = 15, 不是 BorderCustom
  • 背景图赋值: pic = r.Utility.CreatePicture(); pic.LoadFromFile(path); r.BackImage = pic
  • 集合是 1-based: Columns.Item(1), Groups.Item(1)
  • 加载任意来源的 .grf 必须用 gc.safe_load_from_file()(线程+超时), 直接调用会卡死
  • StaticBox 绑定参数显示: box.Parameter = '参数名'(而不是 Text)
  • 回读验证列内容用 g.ColumnTitle.TitleCells.Item(i).Textg.ColumnContent.ContentCells.Item(i).DataField(逐格核对, 不能只看数量)

数据源

  • 拉模式: Recordset.ConnectionString + Recordset.QuerySQL
  • 推模式: FetchRecord 事件中向记录集填入数据
  • JSON/XML: Recordset.LoadData(file)LoadDataFromXML(text)LoadDataFromURL(url)

导出格式

通过 ExportOption 对象设置: AsE2PDFOption(PDF) / AsE2XLSOption(Excel) / AsE2HTMOption(HTML) / AsE2CSVOption(CSV) / AsE2IMGOption(图像) / AsE2RTFOption(RTF)

参考文档

  • references/api_reference.md - 完整API参考(含实测修正)
  • references/object_model.md - 对象模型与GRF格式说明
  • references/crosstab.md - 交叉表(CrossTab)机制/属性/避雷(已实机验证)
  • references/scripting.md - 脚本能力: JScript本质/可访问对象/事件速查/中文需求→JScript示例映射
  • examples/simple_table.grfexamples/grouped_report.grf - 已验证可加载的示例模板

技术来源

本技能基于锐浪软件官方《Grid++Report 6 帮助文档》及本人实际使用过程的实测经验整理构建。 厂商: 锐浪软件 (www.rubylong.cn) COM 接口行为已通过 Grid++Report 6.8.8.0 + pywin32 实测验证并修正。