看代码
import datetime
from pathlib import Path
from docx import Document
from docx.oxml import OxmlElement, ns
from docx.shared import Inches, Pt, Cm, Mm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_TAB_ALIGNMENT
from docx.text.font import Font
from docx.oxml.ns import qn
BASE_DIR = Path(__file__).resolve().parent.parent
# 对其方式
WD_ALIGN = {
"center": WD_ALIGN_PARAGRAPH.CENTER,
"left": WD_ALIGN_PARAGRAPH.LEFT,
"right": WD_ALIGN_PARAGRAPH.RIGHT,
"justify": WD_ALIGN_PARAGRAPH.JUSTIFY,
# "distribute": WD_ALIGN_PARAGRAPH.DISTRIBUTE, # 分散对齐, 占满整行
# "justify_med": WD_ALIGN_PARAGRAPH.JUSTIFY_MED, # 类似于左对齐
# "justify_hi": WD_ALIGN_PARAGRAPH.JUSTIFY_HI, # 类似于左对齐
# "justify_low": WD_ALIGN_PARAGRAPH.JUSTIFY_LOW, # 类似于左对齐
# "thal_justify": WD_ALIGN_PARAGRAPH.THAI_JUSTIFY, # 类似于左对齐
}
class Report:
"""工具类"""
def __init__(self):
self.doc = None
def init_doc(self, font_name):
"""
直接初始化默认的正文字体和大小
https://blog.csdn.net/weixin_42763696/article/details/105492135
https://blog.csdn.net/qq_40272386/article/details/114867630
"""
doc = Document()
doc.styles['Normal'].font.name = font_name
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
doc.styles['Normal'].font.size = Pt(12)
doc.add_picture('{}/images/0001.jpg'.format(BASE_DIR), width=Inches(1.25)) # 1.25间距插入图片
self.doc = doc
def init_header(self, text):
"""
添加页眉设置
https://baijiahao.baidu.com/s?id=1665454009794833226 很详细的页眉页脚设置
"""
section = self.doc.sections[0]
header = section.header
p = header.paragraphs[0]
run = p.add_run(text) # 这个文字其实应该抽出去但是我懒了
font = run.font
font.size = Pt(10.5)
run.font.underline = True # 添加下划线
def create_element(self, name):
return OxmlElement(name)
def create_attribute(self, element, name, value):
element.set(ns.qn(name), value)
def add_page_number(self):
"""添加页码"""
p = self.doc.sections[0].footer.paragraphs[0]
run = p.add_run()
fldChar1 = self.create_element('w:fldChar')
self.create_attribute(fldChar1, 'w:fldCharType', 'begin')
instrText = self.create_element('w:instrText')
self.create_attribute(instrText, 'xml:space', 'preserve')
instrText.text = "PAGE"
fldChar2 = self.create_element('w:fldChar')
self.create_attribute(fldChar2, 'w:fldCharType', 'end')
run._r.append(fldChar1)
run._r.append(instrText)
run._r.append(fldChar2)
font = run.font
font.size = Pt(10.5)
p.alignment = WD_ALIGN.get("center")
def add_paragraph(self, text, font_name="黑体", size_num=12, bold=False, alignment=None, line_spacing=1.5,
style=None):
"""定义段落格式"""
p = self.doc.add_paragraph(style=style)
run = p.add_run(text)
run.font.color.rgb = RGBColor(0, 0, 0)
if alignment:
p.alignment = WD_ALIGN.get(alignment)
font = run.font
font.name = font_name
run.element.rPr.rFonts.set(qn("w:eastAsia"), font_name)
font.size = Pt(size_num)
run.bold = bold
pformat = p.paragraph_format
pformat.line_spacing = line_spacing # 行间距
# https://www.jianshu.com/p/ceea5ade0cda
# https://blog.csdn.net/star565/article/details/103411328
# https://blog.csdn.net/ibiao/article/details/78595295 table.style
# https://www.jianshu.com/p/8d8a75a50190 段落格式设置
def add_table(self, data, aufofix=True, style="Table Grid", col_width=None, vertical_alignment=None):
# 注意data中的数据和t_row, t_col = len(data), len(data[0]), 必须对应, 否则列宽可能失败(
# 就算我最后一行不写任何东西都要用["", "", "", "", ""]填充)
t_row, t_col = len(data), len(data[0])
table = self.doc.add_table(rows=t_row, cols=t_col, style=style) # 添加一个表格
table.autofit = aufofix
for i in range(t_row):
# table.rows[1].height = Cm(2) # 可以通过索引指定行高
for j in range(t_col):
table.cell(i, j).text = data[i][j]
# 但是列宽必须每个cell单独设置, 否则设置不成功, 同时table.autofit = False(是否开启自动调整列宽功能)
if col_
table.cell(i, j).width = Cm(col_width.get(j, 0))
if vertical_alignment:
table.cell(i, j).vertical_alignment = vertical_alignment
self.add_paragraph("\n")
def add_page_break(self):
self.doc.add_page_break()
def save(self, path):
self.doc.save(path)
class AgileReport:
"""每个报告都不一样可以改造成继承也可以直接调用调用"""
def __init__(self, report: Report):
self.report = report
def part1(self, version, creator):
"""创建报告前部固定位置信息"""
self.report.add_paragraph("\n" * 2)
text = "{}测试报告".format(version)
self.report.add_paragraph(text, font_name="黑体", alignment="center", size_num=24, bold=True)
texts = ["文件编号:XXXXXXXXXXX", "版 本 号: A", "受控状态:受控", "密 级:内部公开"]
for text in texts:
self.report.add_paragraph(" " * 20 + text, font_name="黑体", size_num=14, alignment="left")
self.report.doc.add_paragraph("\n" * 8)
text = "XXXXXX有限公司"
self.report.add_paragraph(text, font_name="黑体", alignment="center", size_num=16, bold=True)
self.report.add_page_break()
self.report.add_paragraph("文档修订记录", font_name="黑体", alignment="center", size_num=14, bold=True)
time1 = datetime.datetime.now().strftime("%Y-%m-%d")
data = [["修订号", "日期", "内容", "作者", "审核"],
["1", time1, "创建", creator, ""],
["", "", "", "", ""]] # table填充的数据
col_width = {0: 3, 1: 3, 2: 3, 3: 3, 4: 3.6} # 经过一次次试验发现, word默认宽15.6cm (当然你也可以超过这个长度, 可能会使得表格看起来怪异)
vertical_alignment = WD_TAB_ALIGNMENT.CENTER
self.report.add_table(data, col_width=col_width, vertical_alignment=vertical_alignment)
self.report.add_page_break()
def part2(self, product, project):
# 发现其实标题也是段落, 只不过加了个style而已, 呢就直接统一掉
self.report.add_paragraph("1 测试范围", size_num=22, bold=True, style="Heading 1")
self.report.add_paragraph("1.1 测试产品信息", size_num=16, bold=True, style="Heading 2")
self.report.add_paragraph("产品名称:{}".format(product))
self.report.add_paragraph("版本信息:{}".format(project))
self.report.add_paragraph("1.2 测试内容", size_num=16, bold=True, style="Heading 2")
self.report.add_paragraph("《XXXXXX产品检测规范.docx》")
self.report.add_paragraph("2 测试环境", size_num=22, bold=True, style="Heading 1")
data = [["设备类型", "操作系统", "说明"], ["Linux", "CentOS release 6.10", ""]] # table填充的数据
col_width = {0: 5, 1: 5, 2: 5, 3: 1, 4: 1} # 列宽
vertical_alignment = WD_TAB_ALIGNMENT.CENTER
self.report.add_table(data, col_width=col_width, vertical_alignment=vertical_alignment)
self.report.add_paragraph("3 测试执行", size_num=22, bold=True, style="Heading 1")
def part_change(self, data):
"""
这一部分信息变动比较大
data = {任务名称: [优先级1: [数据列表], 优先级1: [数据列表]], 任务名称: [优先级1: [数据列表], 优先级1: [数据列表]]}
"""
header = ["序号", "用例标题", "前置条件", "测试步骤", "预期结果", "测试结果", "备注"]
task_idx = 1
for task_key in data:
self.report.add_paragraph("3.{} {}".format(task_idx, task_key), size_num=16, bold=True, style="Heading 2")
pri_idx = 1
for pri_data in data[task_key]:
for pri_key in pri_data:
self.report.add_paragraph("3.{}.{} {}".format(task_idx, pri_idx, pri_key), size_num=16, bold=True)
col_width = {0: 1.2, 1: 2.1, 2: 2.1, 3: 6, 4: 2.1, 5: 2.1, 6: 1.4} # 列宽
vertical_alignment = WD_TAB_ALIGNMENT.CENTER
pri_data = [header] + pri_data[pri_key]
self.report.add_table(pri_data, aufofix=False, col_width=col_width,
vertical_alignment=vertical_alignment)
pri_idx += 1
task_idx += 1
def part3(self, path):
"""报告后半部分固定信息"""
self.report.add_paragraph("4 测试结果统计", size_num=22, bold=True, style="Heading 1")
self.report.add_paragraph("5 测试结论", size_num=22, bold=True, style='Heading 1')
self.report.save(path)
def create_report(self, pjv: list, data: dict, path: str, creator: str):
# 真实报告的步骤可以随便加
self.part1(pjv[-1], creator)
product, project = pjv[0], pjv[1]
self.part2(product, project)
# table填充的数据
self.part_change(data)
self.part3(path)
if __name__ == '__main__':
report = Report()
report.init_doc('微软雅黑')
report.init_header("XXXXXX\t\t测试报告")
report.add_page_number()
real_report = AgileReport(report)
pjv = []
data = {}
path = r"C:\Users\yzt\Desktop\test1.docx"
real_report.create_report(pjv, data, path, "root")
总结:
这个工具包底层是xml组成的, 一些功能没有实现比如目录, 希望有大佬可以实现, 菜鸡只能用用