206.python-docx 操作word

看代码

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_width:
                    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组成的, 一些功能没有实现比如目录, 希望有大佬可以实现, 菜鸡只能用用

posted @ 2021-11-26 17:25  楠海  阅读(197)  评论(0编辑  收藏  举报