简介:这份PPT课件面向临床护理人员、规培医生及医学相关专业学生,围绕呼吸机的基础模式与参数调节展开系统讲解,适合用于科室业务培训、教学授课或临床操作前的知识梳理。课件共23页,内容覆盖呼吸机种类与适应症、禁忌症,重点梳理IPPV、SIPPV(A/C)、PRVC、SIMV、PSV、CPAP等常用工作模式的原理与适用场景,并对潮气量、呼吸频率、PEEP、吸呼比等核心参数的设置范围逐一说明,同时专设常见报警及处理章节,解析气道压高低限、潮气量、分钟通气量、氧源与窒息通气等报警的成因与应对思路,另附气道吸痰规范与设备保养维修要点,结构完整、层次清晰。资源为单个pptx文件,压缩包约782KB,已有多人浏览学习,可作为呼吸机使用教学的入门与复习参考。
1. 拿到一份「呼吸机简单模式及参数调节」课件,先别急着改字
带教老师发来一份《呼吸机简单模式及参数调节》的 pptx,多数人的第一反应是双击打开、直接改文字,改完发现参数表的行高全乱了,波形图放大到投影仪上糊成一片,换台电脑打开字体从雅黑变成宋体,PEEP 那一列还错位到了吸气压力下面。问题不在排版手艺,而在于这份课件本质是一张会呼吸的数据表:模式、参数、报警阈值之间是联动的,改一个潮气量,理想体重换算、分钟通气量、报警下限都得跟着动。
把课件当成数据产品来做,是这篇要讲透的路子。目标读者分两类:一类是 ICU、呼吸治疗和护理带教,需要把简单模式的参数逻辑讲清楚、讲得不出错;另一类是设备科、医学教育信息化或课件制作岗,需要一套能复用的生成脚本,让 60 页的课件改一个参数就整体重排。下面从骨架搭建讲到波形插图、批量生成和上机演示,每一步都给可复现的命令和代码。
2. 用 python-pptx 搭出呼吸机简单模式课件的最小骨架
先确定一件事:不要从空白 pptx 开始画。呼吸机课件的版式高度重复,通常是「一页一个模式、一页一张参数表、一页一组波形」,这种重复结构最适合用母版加脚本生成,后期改版式只改母版,不改内容。
2.1 环境准备与依赖确认
我一般用独立虚拟环境,避免和本机已有的 Office 自动化库冲突。python-pptx 只依赖 lxml 和 Pillow,装起来很轻。
python -m venv .venv source .venv/bin/activate # Windows 下用 .venv\Scripts\activate pip install python-pptx==1.0.2 matplotlib numpy python -c "import pptx; print(pptx.__version__)"版本号不追求最新,1.0.x 系列的add_table、notes_slide接口稳定,团队里几个人装同一个版本,生成的 XML 才一致。如果只在 Windows 上跑,matplotlib 的字体缓存目录建议单独指定,否则中文标签会出方框。
export MPLCONFIGDIR=./.mplcache这个环境变量指向项目内的临时目录,避免脚本在 CI 或共享机器上因无写权限而报「font cache」错误。参数说明:MPLCONFIGDIR只影响字体缓存位置,不影响绘图结果。
2.2 先读母版占位符,再写内容
模板做好之后,第一步不是填字,是把每个版式的占位符索引打印出来。占位符的idx才是稳定标识,位置顺序会随你拖动而变。
# inspect_layout.py 枚举母版版式与占位符索引 from pptx import Presentation prs = Presentation("vent_template.pptx") for i, layout in enumerate(prs.slide_layouts): phs = [(ph.placeholder_format.idx, str(ph.placeholder_format.type), ph.name) for ph in layout.placeholders] print(f"layout[{i}] {layout.name} -> {phs}")输出类似layout[3] 标题和内容 -> [(0, 'CENTER_TITLE', '标题'), (1, 'BODY', '内容占位符')]。逻辑说明:idx是 PowerPoint 内部编号,脚本里用slide.placeholders[1]取值;name只用于人看。参数说明:如果模板里同一版式存在两个类型相同的占位符,把idx写死在配置里,不要用循环顺序取,否则换模板就错位。
2.3 中英文字体必须分别设置
python-pptx 的run.font.name只写a:latin,中文会回落到主题字体。课件里「潮气量」「吸气压力」这类词一旦回落成宋体,投影效果会很差,所以要同时写a:ea。
from pptx.oxml.ns import qn from pptx.util import Pt from pptx.dml.color import RGBColor def set_font(run, name="微软雅黑", size=12, bold=False, color=None): """同时设置西文/中文/复杂文种字体,避免中文回退""" run.font.size = Pt(size) run.font.bold = bold run.font.name = name if color: run.font.color.rgb = RGBColor(*color) rPr = run._r.get_or_add_rPr() for tag in ("a:latin", "a:ea", "a:cs"): el = rPr.find(qn(tag)) if el is None: el = rPr.makeelement(qn(tag), {}) rPr.append(el) el.set("typeface", name)逻辑说明:get_or_add_rPr()拿到运行属性的 XML 节点,再补齐三个字体标签。参数说明:name用系统里确定存在的字体名,Windows 用「微软雅黑」、macOS 用「PingFang SC」,不要写「黑体-简」这类只在特定机器存在的名字;size是磅值,正文表格 12pt、标题 28pt 是投影可读的下限。
2.4 参数表用一个二维列表驱动
呼吸机课件的参数页,本质就是「参数名 / 缩写 / 常用起始值 / 调节范围 / 依据」五列。把它写成二维列表,脚本只认列表,不认内容,以后换模式只换数据。
# 简单模式的参数骨架,数值仅为课件展示用,实际以设备说明书与科室规范为准 PARAM_ROWS = [ ["参数", "缩写", "常用起始值", "调节范围", "主要依据"], ["潮气量", "VT", "6-8 mL/kg(理想体重)", "4-12 mL/kg", "肺保护性通气"], ["呼吸频率", "RR", "12-20 次/分", "8-35 次/分", "PaCO2 目标"], ["呼气末正压", "PEEP", "5 cmH2O", "0-20 cmH2O", "氧合与血流动力学"], ["吸入氧浓度", "FiO2", "21% 起调", "21%-100%", "SpO2 目标"], ["吸呼比", "I:E", "1:1.5-1:2", "1:1-1:4", "阻塞/限制性通气"], ]| 模式 | 触发方式 | 主要控制量 | 常见适用场景 |
|---|---|---|---|
| 容量控制 A/C | 时间/患者触发 | 潮气量、频率、流速 | 无自主呼吸、需保证分钟通气量 |
| 压力控制 A/C | 时间/患者触发 | 吸气压力、吸气时间 | 气道压高、漏气风险大 |
| SIMV | 时间触发 + 自主 | 指令频率、支持压力 | 撤机过渡 |
| PSV | 患者触发 | 支持压力、Esens | 自主呼吸稳定、撤机评估 |
| CPAP | 患者触发 | 持续正压水平 | 自主呼吸良好、需维持功能残气量 |
表格里每一行都对应课件里的一页或一段,PARAM_ROWS里的数值只是展示框架,写脚本时不要把它当成处方,正式讲课前必须用设备说明书和科室规范逐条核对。
3. 简单模式与参数调节内容怎么落到每一页
内容架构决定了后面所有脚本的形状。呼吸机课件最容易出问题的地方,是把「模式」和「参数」混在一页讲,学员记不住哪句话属于哪个层级。
3.1 每个模式的页面统一四段式
我习惯把每个简单模式固定成四块:定义与适用人群、机器怎么送气、屏幕上你会看到什么、什么时候该换模式。四块对应母版上的四个占位符,脚本按顺序灌进去,页与页之间只有内容差异。
MODE_PAGES = { "容量控制 A/C": { "define": "时间触发或患者触发,按设定潮气量送气,保证分钟通气量下限。", "mech": "吸气相恒流或减速流,吸气时间由流速与潮气量共同决定。", "wave": "压力-时间曲线呈方形平台,气道峰压随肺顺应性变化。", "switch": "气道压持续偏高或出现人机对抗时,考虑转为压力控制。", }, "压力控制 A/C": { "define": "设定吸气压力与吸气时间,潮气量由顺应性和阻力决定。", "mech": "吸气相压力恒定,流速呈减速波,吸气末流速降至阈值后切换。", "wave": "压力-时间曲线为矩形,潮气量随肺顺应性波动。", "switch": "潮气量持续低于目标值时,需上调压力或改回容量控制。", }, }逻辑说明:字典的键是模式名,值是四个内容槽。参数说明:wave槽不要写「波形正常」这种空话,要写清波形形状特征,方便后面配 matplotlib 生成的插图一一对应。
3.2 参数调节页用「设定值—监测值—报警限」三列并排
学员最容易搞混的是「我设的值」和「机器测出来的值」。把一页拆成三列并排展示,比讲十遍定义都清楚。表格里设定值列和报警限列用脚本自动关联,比如潮气量报警下限一般设在设定值的 80% 左右,这种联动关系写进代码就不会讲错。
def build_alarm_limits(vt_set, rr_set): """从设定值推导课件展示用的报警限,仅用于教学示意""" return { "低潮气量报警": round(vt_set * 0.8, 0), "高潮气量报警": round(vt_set * 1.2, 0), "低呼吸频率报警": max(4, int(rr_set - 6)), "窒息报警时间": 20, # 秒 } print(build_alarm_limits(vt_set=450, rr_set=16)) # {'低潮气量报警': 360.0, '高潮气量报警': 540.0, '低呼吸频率报警': 10, '窒息报警时间': 20}逻辑说明:把推算过程写成函数,课件里那一页表格就能由同一份数据渲染,避免出现「设定 450、报警下限写 300」这种前后不一致。参数说明:0.8 / 1.2只是教学示例比例,窒息报警时间这类阈值必须按机型和制度确认后再上页面,讲课时明确说这是某一类设备的常见做法,不是通用标准。
3.3 波形插图用 matplotlib 生成,不用截图
截图最大的问题是分辨率不可控,投影到 150 寸屏幕上放大就虚。用代码画,dpi想调多高调多高,还能把 PEEP 水平线、触发点标注一起画上去。
import numpy as np import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False t = np.linspace(0, 12, 2400) # 12 秒,约 3 个呼吸周期 rr = 15 # 呼吸频率 次/分 period = 60.0 / rr ti = period * 0.33 # 吸气时间约占 1/3 peep, pmax = 5.0, 20.0 phase = np.mod(t, period) p = np.where(phase < ti, pmax, peep + (pmax - peep) * np.exp(-(phase - ti) / (period * 0.12))) fig, ax = plt.subplots(figsize=(8, 3.2)) ax.plot(t, p, lw=2, color="#1f4e79") ax.axhline(peep, ls="--", lw=1, color="#c00000") ax.text(0.2, peep + 0.6, "PEEP", color="#c00000", fontsize=10) ax.set_xlabel("时间 (s)"); ax.set_ylabel("气道压 (cmH2O)") ax.set_ylim(0, 28); ax.grid(alpha=0.3) fig.tight_layout() fig.savefig("wave_pc.png", dpi=300, bbox_inches="tight")逻辑说明:np.where直接在数组上做分段,吸气相取压力平台值,呼气相用指数衰减模拟,比逐点循环快得多。参数说明:dpi=300是投影和印刷都够用的下限,bbox_inches="tight"去掉多余留白,pmax改一个数就能生成不同压力水平的对照图,做「压力上调 5 cmH2O 前后对比」那一页特别省事。
3.3.1 把图片按占位符尺寸插入
插图宽度要跟母版内容框对齐,硬编码Inches(6)在 16:9 和 4:3 模板之间会跳。
from pptx.util import Emu def insert_picture(slide, ph_idx, img_path): ph = slide.placeholders[ph_idx] pic = slide.shapes.add_picture(img_path, ph.left, ph.top, width=ph.width) # 若图高超出占位符,按高度等比缩放 if pic.height > ph.height: ratio = ph.height / pic.height pic.height = Emu(int(pic.height * ratio)) pic.width = Emu(int(pic.width * ratio)) pic.left = ph.left + Emu(int((ph.width - pic.width) / 2)) return pic逻辑说明:先按宽度对齐,再判断高度是否溢出,溢出就按比例缩回并水平居中。参数说明:ph.width、ph.height都是 EMU 单位,传入Emu()前要转成整数,直接传浮点会报类型错误。
4. 批量生成与一致性校验,让几十页课件不出错
一页一页手改,改到第十页就开始乱。把内容抽成一份 JSON,脚本读数据、生成页码、写备注、导出 PDF,整个过程一条命令跑完。
4.1 用 JSON 做单一数据源
{ "deck_title": "呼吸机简单模式及参数调节", "modes": [ {"name": "容量控制 A/C", "vt": 450, "rr": 16, "peep": 5, "fio2": 40}, {"name": "压力控制 A/C", "pinsp": 18, "rr": 16, "peep": 5, "fio2": 40}, {"name": "PSV", "ps": 10, "peep": 5, "fio2": 35, "esens": 25} ] }逻辑说明:modes数组的每个元素渲染一页,页码、目录、参数表全部从这份数据推导。参数说明:vt单位 mL,pinsp、ps、peep单位 cmH2O,fio2用百分数整数,esens用百分数。单位在 JSON 里不写,但要写进字段命名规范,否则第 20 页一定有人填成 0.4 和 40 混用。
4.2 生成入口与页码回填
import json from pptx import Presentation def build(json_path="deck.json", tpl="vent_template.pptx", out="dist/vent_modes.pptx"): data = json.load(open(json_path, encoding="utf-8")) prs = Presentation(tpl) total = len(data["modes"]) + 2 # 封面 + 目录 + 每个模式一页 for i, mode in enumerate(data["modes"], start=1): slide = prs.slides.add_slide(prs.slide_layouts[3]) slide.placeholders[0].text = mode["name"] slide.notes_slide.notes_text_frame.text = f"{mode['name']} 讲稿:先讲定义,再讲波形。" pb = slide.placeholders[10] # 页码占位符 pb.text = f"{i + 2} / {total}" prs.save(out) return out if __name__ == "__main__": print(build())逻辑说明:notes_slide是懒加载的,第一次访问会自动创建备注页,讲稿直接写进去,放映时讲师视图能看到。参数说明:total要按实际页数算,含封面和目录;占位符idx=10是页码位的常见写法,具体值以inspect_layout.py的输出为准。
4.3 上脚本前的三条校验
没人愿意在投影前五分钟发现第 34 页文字溢出。把校验做成脚本的一部分,生成即检查。
| 检查项 | 判断方式 | 处理动作 |
|---|---|---|
| 文字溢出 | 估算行数 × 行高 > 占位符高度 | 缩字号或拆页 |
| 单位混用 | FiO2 值 < 1 或 > 100 | 报错并指出页码 |
| 占位符缺失 | idx不在slide.placeholders | 终止生成,打印版式名 |
| 图片缺失 | 引用的 png 路径不存在 | 跳过并记录到日志 |
import math def est_overflow(text, ph_width_in, ph_height_in, font_pt=12): """粗略估算文本框是否溢出:中文按 1 字宽 = 字号磅值计""" chars_per_line = max(1, int(ph_width_in * 72 / font_pt)) lines = sum(max(1, math.ceil(len(seg) / chars_per_line)) for seg in text.split("\n")) line_height_in = font_pt * 1.4 / 72 return lines * line_height_in > ph_height_in print(est_overflow("潮气量按理想体重 6-8 mL/kg 设定,注意气道峰压变化。", 4.0, 1.0))逻辑说明:按中文字宽近似等于字号磅值估算每行字数,再乘 1.4 倍行距得到所需高度。参数说明:ph_width_in、ph_height_in用ph.width / 914400换算成英寸传入;这个方法偏保守,宁可误报也不要在现场翻车,实际误差在 ±15% 以内。
python build.py && soffice --headless --convert-to pdf --outdir dist dist/vent_modes.pptx这条命令把生成的 pptx 直接转成 PDF,用来快速翻页检查排版。参数说明:--headless无界面运行,适合放在打包脚本里;部分 Linux 环境需要先装中文字体包,否则 PDF 里中文变方框,这一步同时也是字体可用性的检测手段。
4.4 换机器一定踩的三个坑
第一个坑是字体。脚本里写了「微软雅黑」,跑到 Linux 上找不到就静默回退,生成的课件看起来没问题,拷到会场电脑上才变形。稳妥做法是课件里只使用两三种常见字体,并把用到的字体随课件一起交付。
第二个坑是母版占位符索引漂移。同事在母版里插了一个新的文本框,占位符顺序变了,脚本取到的内容全串位。所以配置里的idx要在每次模板更新后重跑一遍inspect_layout.py核对。
第三个坑是表格行高。add_table给的高度只是初始值,行数一多 PowerPoint 会自动撑开,图片和表格就会重叠。解决办法是把表格放在页面下方独立区域,或者干脆拆成两页,别指望脚本能算出 PowerPoint 的实际排版。
5. 上机演示前的最后几项处理
课件生成的最后一段,往往决定现场讲得顺不顺。这一步不写内容,只做工程收尾,把交付物从「能打开的 pptx」变成「能直接讲的版本」。
第一个处理是备注页讲稿的自动填充。把每个模式的讲稿按固定结构写进notes_slide,讲师视图里就是提词器。写讲稿时建议按「定义一句话、参数一句话、波形一句话、什么时候换模式一句话」四句走,每句不超过 40 字,超过就说明这页该拆。批量写入用循环,别一页一页敲:
for slide, mode in zip(new_slides, data["modes"]): lines = [ f"定义:{mode.get('define', '')}", f"参数:VT {mode.get('vt', '-')} mL,RR {mode.get('rr', '-')} 次/分," f"PEEP {mode.get('peep', '-')} cmH2O,FiO2 {mode.get('fio2', '-')}%。", f"波形:{(mode.get('wave') or '')[:30]}", f"换模式:{(mode.get('switch') or '')[:30]}", ] slide.notes_slide.notes_text_frame.text = "\n".join(lines)逻辑说明:四行结构保证抽掉任意一行讲稿依然完整,现场临时删页也不影响。参数说明:[:30]截断是为了防止某条内容写太长把提词器挤满,实际内容仍保留在 JSON 里。
第二个处理是放映参数。课件里如果用了触发式动画演示呼吸周期,记得在母版里把切换方式统一为「单击」,不要混用自动换片;混合之后用翻页笔容易跳页。另外把幻灯片尺寸统一写成prs.slide_width = Inches(13.333)(16:9),投到 4:3 的老设备上会留黑边,但不会变形,比自动缩放安全。
第三个处理是版本标记。在封面页角落放一个极小的版本号和日期,用脚本自动填写,这样现场如果有人问「你这份是哪版」,翻到封面就能答。做法是在 JSON 里加"build": "2025-06-01",生成时写进封面占位符,字号设成 10pt 灰色,远看几乎不干扰画面。
第四个处理是随课件附一份参数对照的 PDF。把 JSON 数据渲染成一张 A4 竖版的速查表,学员课后打印带走。这份 PDF 和课件同源,改一个参数两边一起变,不用再维护一份 Excel。生成命令沿用前面那条soffice --convert-to pdf,把速查表 pptx 单独转一次即可。
最后提醒一句关于内容的边界:课件上的所有参数值都属于教学示意,讲台上讲每一页之前,把机型说明书的对应页放在讲稿备注里,学员问「我们科室的机器是不是也这么设」时,能直接翻到出处,比背数字有用得多。
本文还有配套的精品资源,点击获取