ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于 YAML 与 python-pptx 的质量意识培训课件自动化生成与校验

基于 YAML 与 python-pptx 的质量意识培训课件自动化生成与校验 简介围绕体系管理与质量意识展开的47页培训课件面向企业质量管理人员、生产一线班组长及新入职员工用于质量意识普及与体系管理入门培训。内容从质量的重要性切入梳理戴明、朱兰、石川馨等六位质量大师对质量的定义讲清质量意识的内涵、产品的定义、哪些岗位需要具备质量意识以及产品质量从检验、生产、设计到管理、习惯的认知提升路径同时围绕人、机、料、法、环分析质量来源并给出零缺陷原则、三不原则、首件检查与不合格品处理等可落地做法配有质量意识小故事与常见缺失表现便于直接用于内训宣讲和对照自查。全包共1个pptx文件压缩包约2.88MB按目录分节编排页面结构清晰已有91人学习。1. 当一份 47 页的质量意识培训 PPT 需要被反复交付每年体系审核前两周质量部的群里都会出现同一个文件47页PPT体系管理质量意识培训.pptx。改掉封面日期塞进两页上季度的客户投诉案例文件名变成「…_终版_v3_真的最终.pptx」。讲完之后没人说得清和上一版的差别在哪也没人知道第 31 页那句「全员参与」到底挂在哪份程序文件下面。把它当成一个工程交付物来看问题的形状就变了内容有没有单一数据源版式有没有硬约束47 这个页数能不能被脚本校验一次改动能不能被 diff 出来。质量意识培训最怕的不是讲得不好而是每换一个讲师就重排一遍版式、每换一次版本就丢一批案例。这篇写给三类人需要把课件做成可维护资产的体系专员和质量工程师中小企业里同时兼文档管理、内训和 IT 的「全干工程师」以及要准备审核材料、又不想手工调 47 页字号和行距的人。2. 质量意识培训课件的知识骨架与 47 页篇幅配比2.1 先定要改变的行为再定页数质量体系管理培训的失败模式高度一致把标准条款念一遍听众点头回到工位照旧。原因不在讲得枯燥而在目标设错了——培训要改变的是三个可观察的行为不是记住几个定义发现问题第一时间上报而不是先自己判断「这不归我管」按文件写的做文件没写的先提变更而不是凭经验绕过去不合格品不流入下道工序宁可停线也不「先用着看」。这三个行为对应到内容上就是「为什么重要成本→ 体系怎么管框架→ 我的岗位做什么职责与流程→ 遇到问题用什么工具方法→ 做错会怎样案例与红线→ 怎么算过关考核」。按这个链条排页47 页刚好少了讲不透多了现场压不住节奏。2.2 47 页的模块配比表模块页数要回答的问题必备要素封面与目录2这是什么培训文件编号、版本号、适用部门、生效日期为什么讲质量5出问题谁买单质量成本四分类、内部与外部失败成本占比体系框架8体系到底管什么PDCA、过程方法、风险思维、条款地图过程与职责10我这个岗位做什么岗位质量职责、三检制、变更管理、追溯要求工具与方法8出问题怎么处理8D、5Why、不合格品控制、纠正措施闭环案例与红线8做错会怎样脱敏真实案例、红线行为清单、后果描述承诺与考核4怎么算过关考核方式、签字确认、复训周期封底与联系2有问题找谁归口部门、联系方式、文件受控说明合计47这张表的用法不是照着抄而是拿来对齐分歧。业务部门总想加页质量部总想减页有了页数配比讨论就从「这页要不要」变成「这页占哪个模块的额度」效率完全不同。质量成本那 5 页尤其不能省——把一次返工的料、工、停线、运费、客户罚款算成一页表比讲十条「质量第一」都有用。2.3 把现有课件抽成大纲看清手里到底有什么动手重排之前先知道现版本讲了什么。python-pptx 能把每页的文字倒出来几十行就够from pptx import Presentation prs Presentation(47页PPT体系管理质量意识培训.pptx) print(f总页数: {len(prs.slides)}) for i, slide in enumerate(prs.slides, 1): texts [] for shape in slide.shapes: if shape.has_text_frame: for para in shape.text_frame.paragraphs: line .join(run.text for run in para.runs).strip() if line: texts.append(line) elif shape.has_table: # 表格不是 text_frame要单独取 for row in shape.table.rows: texts.append( | .join(c.text.strip() for c in row.cells)) # 每页只打印前两条避免刷屏足够判断这页在讲什么 print(f{i:02d} | / .join(texts[:2])[:80])逻辑很直白遍历每页的所有 shape文本框走text_frame表格走table把非空段落拼起来。参数上有两个容易踩的点——has_text_frame和has_table必须分开判断表格自带的text_frame拿不到单元格内容paragraphs里的文字分布在多个run上直接读paragraph.text在不规范的旧课件里经常丢字用.join(run.text ...)更稳。跑完之后通常会看到两种页一种是纯标题页和纯目录页另一种是塞了十行小字的「大杂烩页」。前者合并后者拆成两到三页讲页数自然就长回来了。2.4 版式约束要提前定死别等生成完再改培训现场的条件决定版式参数而不是设计审美。常见的会议室纵深 8 到 10 米投影亮度一般这些约束应该写死在生成规则里正文不小于 18pt标题 32pt 起步每页正文不超过 6 行单行不超过 30 个汉字正文与背景对比度不低于 4.5:1深底白字在小投影仪上反光严重一页只讲一个观点需要两个观点的内容拆页。为什么不用 Word 手册或者录播视频替代手册的现场阅读率低讲的时候没人翻视频的更新成本高一份案例换掉就得重录。PPT 可拆可讲、可插车间照片、可现场提问仍然是体系管理内训最稳的载体。代价就是版本会失控——所以下一步要把它变成脚本产物。3. 用 python-pptx 从 YAML 数据源生成 47 页课件3.1 目录结构与依赖quality-deck/ ├── content/ │ ├── meta.yaml # 标题、版本、文件编号 │ └── slides.yaml # 每页内容唯一的真源 ├── assets/ # 车间照片、流程图 ├── build_deck.py ├── check_deck.py └── out/python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install python-pptx pyyaml把内容抽到 YAML 的意义在于改一句话就是改一行文本diff 出来清清楚楚不用打开 PPT 比对形状。版本号也从文件名挪进了meta.yaml输出文件名由脚本拼出来。3.2 内容数据源的结构meta: title: 体系管理质量意识培训 version: v2.0 doc_no: QP-HR-014 owner: 质量管理部 modules: - key: cost pages: - title: 一次返工的真实成本 bullets: - 材料报废 1.2 万元 - 停线 4 小时影响交付 2 个批次 - 客户罚款与加急运费 3.6 万元 - 合计约 5 万元相当于该产线单班利润 footer_note: 数据来源内部质量成本台账 clauses: [6.1, 10.2]clauses字段现在看着多余到第 5 章做覆盖检查时是主角。3.3 中文字体设置一个必须动 XML 的参数python-pptx 的run.font.name只写a:latin中文会回退到默认宋体投影上细得看不清。要补a:eafrom pptx.oxml.ns import qn from pptx.util import Pt def set_run_font(run, name微软雅黑, size18, boldFalse, colorNone): 统一设置中西文字体、字号、颜色。 run.font.name name # 只影响 a:latin run.font.size Pt(size) run.font.bold bold if color is not None: run.font.color.rgb color rPr run._r.get_or_add_rPr() if rPr.find(qn(a:ea)) is None: ea rPr.makeelement(qn(a:ea), {typeface: name}) # a:ea 在 schema 里必须排在 a:latin 之后、a:cs 之前 # 顺序插错PowerPoint 打开时会提示需要修复 rPr.insert_element_before( ea, a:cs, a:sym, a:hlinkClick, a:hlinkMouseOver, a:rtl, a:extLst )insert_element_before的第二个参数起是「后继元素列表」python-pptx 会找到第一个存在的元素插到它前面。这样插出来的a:ea位置合法Office 不会报修复。color传RGBColor(0x80, 0x80, 0x80)这类值页脚用得上。3.4 单页生成函数与主流程from pptx import Presentation from pptx.util import Inches from pptx.dml.color import RGBColor import yaml def add_content_slide(prs, page_no, title, bullets, footerNone, total47): slide prs.slides.add_slide(prs.slide_layouts[6]) # 6 号是空白版式 tb slide.shapes.add_textbox(Inches(0.8), Inches(0.5), Inches(11.7), Inches(0.9)) tb.text_frame.word_wrap True p tb.text_frame.paragraphs[0] r p.add_run(); r.text title set_run_font(r, size32, boldTrue) body slide.shapes.add_textbox(Inches(0.9), Inches(1.7), Inches(11.5), Inches(5.0)) tf body.text_frame tf.word_wrap True for i, text in enumerate(bullets): para tf.paragraphs[0] if i 0 else tf.add_paragraph() para.line_spacing 1.25 # 18pt 正文配 1.25 行距6 行刚好铺满 para.space_after Pt(8) br para.add_run() br.text • text # python-pptx 对项目符号支持弱直接打字符最稳 set_run_font(br, size18) ft slide.shapes.add_textbox(Inches(0.8), Inches(6.85), Inches(11.7), Inches(0.4)) fr ft.text_frame.paragraphs[0].add_run() fr.text f{footer or } {page_no} / {total} set_run_font(fr, size12, colorRGBColor(0x80, 0x80, 0x80)) return slide def build(content_path, out_path): cfg yaml.safe_load(open(content_path, encodingutf-8)) total sum(len(m[pages]) for m in cfg[modules]) prs Presentation() prs.slide_width Inches(13.333) # 16:9默认 10x7.5 是 4:3 prs.slide_height Inches(7.5) no 0 for module in cfg[modules]: for page in module[pages]: no 1 add_content_slide(prs, no, page[title], page[bullets], page.get(footer_note), total) prs.save(out_path) return total主流程里slide_layouts[6]是默认模板里的空白版式用它的好处是所有元素位置都由代码控制不会被母版里的占位符带偏。total由 YAML 页数实时算出页脚自然跟着内容变不会出现「写死 47 但实际 45 页」的尴尬。3.5 五个必调参数与代价参数设置位置推荐值不调的后果页面尺寸prs.slide_width/height13.333 × 7.5 英寸4:3 拉伸变形投影两侧黑边中文字体rPr的a:latina:ea微软雅黑 / 思源黑体中文回退宋体后排看不清正文字号run.font.size18pt现场变念稿会行距paragraph.line_spacing1.256 行挤成一团观感差自动调整text_frame.word_wrapTrueauto_size保持默认文本溢出到页面外肉眼看不见3.6 表格页怎么加成本、职责矩阵、案例对照这类内容用表格比项目符号清楚。默认表格样式是蓝色主题一般直接接受重点调列宽和字号shape slide.shapes.add_table(rows5, cols3, leftInches(1), topInches(2), widthInches(11), heightInches(3)) table shape.table table.first_row True # 首行加粗强调 table.columns[0].width Inches(2.5) table.columns[1].width Inches(3.0) table.columns[2].width Inches(5.5) data [[环节, 金额, 说明], [材料报废, 1.2 万, 整批退料], [停线, —, 4 小时], [客户罚款, 3.6 万, 含加急运费], [合计, 约 5 万, 单班利润量级]] for r, row in enumerate(data): for c, val in enumerate(row): cell table.cell(r, c) cell.text val for para in cell.text_frame.paragraphs: for run in para.runs: set_run_font(run, size16, bold(r 0))单元格文字同样要走set_run_font否则表格里的中文又变回宋体——这是新手最常漏的一处。cell.margin_left默认约 0.1 英寸列窄的时候要调到Inches(0.05)不然数字会换行。4. 生成之后的自检页数、单页字数与占位符残留4.1 三道必过的校验生成能跑通不代表交付可用。每次构建后至少查三件事总页数是否等于约定值单页中文字符数是否超过上限超过说明这页讲不完有没有{{...}}这类没替换掉的占位符。渠道方审材料时一页残留的占位符比内容错了还难解释。import re import sys from pptx import Presentation MAX_CN_PER_PAGE 180 # 超过这个数18pt 正文一定放不下 REQUIRED_TOTAL 47 PLACEHOLDER re.compile(r\{\{.*?\}\}) CN re.compile(r[\u4e00-\u9fff]) def iter_text(slide): for shape in slide.shapes: if shape.has_text_frame: yield shape.text_frame.text if shape.has_table: for row in shape.table.rows: for cell in row.cells: yield cell.text def check(path): prs Presentation(path) total len(prs.slides) errors [] if total ! REQUIRED_TOTAL: errors.append(f页数不符实际 {total}期望 {REQUIRED_TOTAL}) for i, slide in enumerate(prs.slides, 1): text \n.join(iter_text(slide)) cn len(CN.findall(text)) if cn MAX_CN_PER_PAGE: errors.append(f第 {i} 页汉字 {cn} 个超过 {MAX_CN_PER_PAGE}) for m in PLACEHOLDER.findall(text): errors.append(f第 {i} 页残留占位符 {m}) for e in errors: print([FAIL], e) return 1 if errors else 0 if __name__ __main__: sys.exit(check(sys.argv[1]))返回值直接当退出码用这是接进流水线的前提。MAX_CN_PER_PAGE是按 18pt、6 行、每行 30 字反推的上限改版式就要同步改这个数两个参数要绑在一起看。4.2 接进提交前检查和持续集成#!/usr/bin/env bash set -euo pipefail OUTout/体系管理质量意识培训_$(date %Y%m%d).pptx python build_deck.py --content content/slides.yaml --out $OUT python check_deck.py $OUT echo 校验通过$OUTset -euo pipefail保证校验不过就中断不会把坏文件当成产物发出去。挂到 git 的 pre-commit 或者流水线的构建阶段都行一次配置长期有效。4.3 常见报错与定位方式现象常见原因定位方式打开提示「需要修复」XML 子元素顺序不合法多半是a:ea插错位置用insert_element_before指定后继元素别直接append中文显示为宋体只设了a:latin没设a:ea打印rPr.xml看有没有a:ea节点表格文字换行难看单元格默认左右边距过大调小cell.margin_left/right页数与预期不符某模块pages少写了一项或 YAML 缩进错sum(len(m[pages]) for m in cfg[modules])先打印再生成图片被拉变形只给了 width 没给 height同时给两个值或用图像库先算宽高比脚本报KeyError某页缺title或bullets字段在循环里 try 包住并打印页码快速定位到 YAML 行5. 给每页打条款标签反查覆盖并派生讲师手册课件做到这一步已经可复现了但还有一个更高价值的问题没回答这 47 页到底覆盖了体系里的哪些要求有没有条款讲了但课件里找不到把第 3 章 YAML 里的clauses字段用起来写一个反向索引就够了。from collections import defaultdict import yaml cfg yaml.safe_load(open(content/slides.yaml, encodingutf-8)) idx defaultdict(list) for module in cfg[modules]: for page in module[pages]: for c in page.get(clauses, []): idx[c].append(page[title]) # 审核关注度最高的几项按自家体系文件实际清单替换 required [4.4, 5.3, 6.1, 7.3, 8.7, 10.2] missing [c for c in required if c not in idx] print(未覆盖条款:, missing or 无) for c in sorted(idx): print(f{c} - {len(idx[c])} 页: {idx[c]})输出会暴露两类问题。一类是missing里的条款说明培训内容有空洞需要在对应模块补页——注意补页要占用配比表里的额度别让总页数失控。另一类是某个条款挂了七八页而另一个条款只有一页页数分布严重倾斜往往意味着课件在讲自己熟悉的内容而不是学员真正需要的内容。讲师手册可以从备注页自动派生不用另写一份文档from pptx import Presentation prs Presentation(out/体系管理质量意识培训_20250101.pptx) for i, slide in enumerate(prs.slides, 1): if slide.has_notes_slide: note slide.notes_slide.notes_text_frame.text.strip() if note: print(f## 第 {i} 页\n\n{note}\n)备注在生成阶段从 YAML 的speaker_note字段写进slide.notes_slide.notes_text_frame.text重定向输出就是讲师手册的 Markdown 版。讲师换人时只要照着备注念都能保证口径一致。一个值得固定的约定区分标签前缀clause:后面跟体系条款号process:后面跟内部流程名比如「不合格品控制」「变更管理」。两种标签混在一起写覆盖率脚本就没法区分「标准要求」和「公司自定动作」缺失排查会变成一团乱麻。本文还有配套的精品资源点击获取
返回列表