锐浪报表助手 - Grid++Report Helper
© 2026 范先生. 免责声明:本技能为基于公开文档与本人实测经验整理的第三方辅助工具,非锐浪软件(rubylong.cn)官方出品,与厂商无隶属或授权关系。Grid++Report 及其相关名称归各自权利人所有。
基于锐浪软件官方《Grid++Report 6 帮助文档》及本人实际使用过程的实测经验构建的 WorkBuddy 技能。
💡 调用成本低指引(必读)
本技能的全部知识已沉淀到本地
references/文档(API、对象模型、交叉表、脚本接口均已实测并本地化)。 调用本技能时,直接用下面这些本地文档解答,无需联网检索任何外部知识库。 本技能为离线自包含设计,所有知识均已内置,运行时不依赖任何外部知识库或网络服务。
⚠️ 关键事实 (2026-08 实测验证, Grid++Report 6.8.8.0)
以下事实与旧版官方文档/网上资料不同, 必须遵守, 否则生成的模板在设计器中打不开:
-
COM ProgID: 引擎是
gregn.GridppReport, 设计器是grdes.GRDesigner。 旧文档写的GridppReport.GridppReport是错的, Dispatch 会报"类字符串无效"。 -
GRF 文件真实格式: JSON 文本格式(UTF-8 BOM), 顶层键为 Version/Title/Font/Printer/DetailGrid/PageHeader/PageFooter/Parameter。 不存在 "Object Report" DFM 风格文本格式——手写那种格式生成的文件设计器打不开。
-
禁止手写 GRF 文本: 一律通过 COM API 构建 +
SaveToFile保存, 由引擎自己序列化成合法 JSON。 -
SetBounds 矩形语义: 参数是
(Left, Top, Right, Bottom), 不是 (Left, Top, Width, Height)! 必须用grf_common.set_bounds(ctrl, left, top, width, height)辅助函数。 -
TextAlign 死循环陷阱: 只能赋合法枚举值 (17/18/20/33/34/36/65/66/68)。 赋 0 等非法值会让模板加载器死循环, 设计器直接卡死打不开。
-
安全加载:
LoadFromStr/LoadFromFile遇到非法模板会无报错卡死, 必须用grf_common.safe_load_from_str/safe_load_from_file(线程+超时)。 -
路径必须反斜杠:
LoadFromFile/SaveToFile传正斜杠路径会静默失败。 -
IGRFont 字号属性是
Point, 没有 Size 属性。 -
列必须设
col.Name = 字段名: 不设列名时 SaveToFile 虽会把全部标题/数据绑定 写入 JSON, 但引擎重新加载时只保留最后一列的标题与 DataField, 其余列在设计器中 空白(对照实验验证)。生成后须用ColumnTitle.TitleCells/ColumnContent.ContentCells逐格回读验证标题与绑定。 -
⚠️ 页面设置(纸型/边距/偏移)必须用
Design*设计态属性:IGRPrinter上 大部分属性是运行时值(反映真实打印机驱动), 设置它们不会持久化进 .grf。 只有Design*前缀属性才被序列化:DesignPaperSize(256=自定义纸型)、DesignPaperWidth/DesignPaperLength(自定义纸宽高,cm)、DesignLeft/Top/Right/BottomMargin、DesignPaperOrientation(1纵向/2横向); 偏移量用非前缀的PrintOffsetX/Y。 直接用PaperSize/PaperWidth/LeftMargin等设置, 保存后仍是默认 A4 —— 这正是 克隆模板"纸型变A4"的根因。统一用grf_common.copy_printer(src.Printer, r)复制。 -
新报表的
BackImage为None, 必须通过Utility.CreatePicture()创建再赋值: 不能r.BackImage.LoadFromFile()(NoneType 报错), 也不能r.BackImage = 文件路径字符串(类型不匹配)。 正确做法:pic = r.Utility.CreatePicture() pic.LoadFromFile(r'背景.png') r.BackImage = picStaticBox 的边框用
box.Border.Styles = 15(四边), 而不是单元格的BorderCustom。 -
克隆复刻的 MemoBox(综合文字框) 边框必须显式复制: 每个控件都有
Border子对象(IGRBorder)。copy_control必须复制Styles/InnerStyles/InnerIndent/Shadow/ShadowColor/ShadowWidth及Border.Pen(颜色/线宽), 否则克隆后 MemoBox 边框丢失。 (已修复grf_clone.py的copy_border, 复刻COBILL_FULL2025.grf后 58 个 MemoBox 边框与源一致) -
静默导出枚举
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 = True→opt.FileName = path→Export(1)。ExportDirect(1,...)会忽略ToXlsxFormat, 只产 legacy.xls。(见grf_export.py) -
脚本事件属性名不带 "On" 前缀:
InitializeScript/ProcessBeginScript/PrintBeginScript/PageStartScript/GlobalScript/... 是可写字符串属性(放 JScript 代码), 可持久化进 .grf。 (不是OnInitializeScript; 注入/查询/清除见grf_script.py+references/scripting.md) -
含 FreeGrid 的大模板嵌入
BackImage必须两遍法: 直接r.BackImage = pic会被序列化丢弃 (真实 13×13 复刻模板实测丢失)。可靠做法: 先正常SaveToFile→ 重新LoadFromFile→ 赋r.BackImage→ 再SaveToFile。r.BackImage赋值须在InsertReportHeader()之后。 -
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
转换逻辑:
openpyxl解析: 表头(首行非空)、列宽(Excel字符宽×0.21→cm)、类型推断(扫描前50行)- 中文表头 → 合法字段名(ASCII表头直接用, 否则
F_列号) - COM构建明细网格模板,
--group-col时生成分组+组页脚汇总 - 同时输出同名
.json数据文件({fields, records}结构, 供 LoadData 测试) - 回读验证(线程+超时保护), 失败则报错退出
模块三: 修改微调报表 (/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对象读出再写入新对象, 避免字符串/枚举转换坑 - 底图
BackImage是IGRPicture对象,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 fill时FreeGrid.Dock = 5(grdsFill), 否则按 grid bbox 绝对定位。 - 单元格对齐枚举:
TextAlign合法值 17/18/20/33/34/36/65/66/68(同关键事实5)。
OCR 后端自动探测(按顺序): rapidocr_onnxruntime → rapidocr_openvino →
easyocr → pytesseract。均无安装时, 脚本仍输出布局结构, 文字留空由用户在
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 引擎的 ExportDirect 与
PrepareExport + 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) | 注意: ToXlsxFormat 在 AsE2XLSOption 子对象上, 不是 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 工作流(智能体执行):
- 用户用中文描述需求(如"结算金额>1000 时合计变红")。
- 智能体翻译成 JScript 片段(对象名用报表里真实的控件
Name/ 参数名; 见下例)。 grf_script.py inject写入目标 .grf 的对应事件(默认InitializeScript)。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):
- 尽量少用——能用分组报表替代就不要用子报表(性能代价大, 明细网格中的子报表尤甚)
- 仅报表头尾的动态数据 → 用报表参数或直接给部件框设值, 不用子报表
- 垂直并行的多个子报表放不同报表节, 一节只放一个
- 含子报表的报表用打印预览(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).Text和g.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.grf、examples/grouped_report.grf- 已验证可加载的示例模板
技术来源
本技能基于锐浪软件官方《Grid++Report 6 帮助文档》及本人实际使用过程的实测经验整理构建。 厂商: 锐浪软件 (www.rubylong.cn) COM 接口行为已通过 Grid++Report 6.8.8.0 + pywin32 实测验证并修正。
Scan to join WeChat group