一套课件的制作时间,大头往往不在"想讲什么",而在"把想讲的东西摆到页面上":调字号、对齐、配图、分页、统一版式。把其中机械的部分交给脚本和模型,教师只需要提供教学内容与最后校对,这是本教程要落地的目标。下面这套流程不依赖任何特定厂商的模型,换成网页版对话工具,把生成的 JSON 复制出来同样适用。
这套流程能做出什么
先说成果。走完下面的步骤,你会得到:
- 一个可编辑的
.pptx文件,不是图片拼贴,文字、表格、色块都能在 PowerPoint 或 WPS 里继续改; - 结构完整的课件骨架:封面、学习目标、情境导入、知识点分页、例题、课堂练习、小结、作业;
- 每页都有备注区,可以放讲稿提示,放映时自己看得到、学生看不到;
- 一套可复用的 JSON 数据格式,下次换一节课,只改输入不改脚本。
以初中物理《浮力》为例,输入一份两三百字的备课要点,输出的页序大致是:封面 → 学习目标 3 条 → 情境导入(为什么木头浮、铁块沉)→ 浮力的方向与施力物体 → 阿基米德原理 → 三种状态对比表 → 例题 2 道 → 课堂练习 3 题 → 小结与作业。
对于一节常规课,熟练之后从输入要点到拿到可编辑成品,通常能压进半小时以内;剩余时间留给内容校对和个性化调整。重复性排版工作的耗时下降更明显,因为版式由母版统一控制,脚本只负责填内容。
前置条件清单
开始前确认这几样东西就位:
1. Python 环境:本地能跑 python --version。版本以官方文档当前版本为准,3.x 系列即可。
2. python-pptx 库:用于读写 pptx 文件。安装命令见下。
3. 一份母版文件:自己学校或自己常用的 .pptx,留出空白版式即可。有统一模板,生成的成品才有辨识度。
4. 一个能稳定输出结构化文本的大模型入口:网页版、API 都行。要求它按 JSON 输出。
5. 素材来源:图片、图表建议自制或使用明确授权的素材,避免课堂展示时出现版权争议。
6. 一份待备课的教案要点:不需要写得很细,知识点、重难点、例题方向写清楚就够。
安装依赖:
```bash
pip install python-pptx
```
如果国内网络下载慢,可自行配置镜像源,具体地址以所用镜像站官方说明为准。
第一步:把备课要点写成结构化提示词
提示词的质量直接决定输出能不能用。核心是两件事:明确要求输出 JSON,明确每一页的字段含义。
可以直接套用下面这段,把方括号内容替换成自己的课:
```text
你是一位[初中物理]教师,请为一节[40]分钟的课设计课件大纲。
课题:[浮力]
学情:[学生已学过力的三要素和二力平衡,尚未接触阿基米德原理]
重难点:[重点:浮力的方向与阿基米德原理;难点:理解排开液体的重力]
可用例题方向:[弹簧测力计称重法测浮力、漂浮与悬浮的受力分析]
要求:
1. 只输出 JSON,不要任何解释文字,不要 Markdown 代码围栏。
2. 幻灯片总数控制在 16 到 22 页之间。
3. 每页正文条目不超过 5 条,每条不超过 30 个字,避免文字堆砌。
4. 数值、公式、史实必须准确;不确定的内容省略,不要编造。
5. JSON 结构严格遵循下面这个示例,字段名与层级不要改动:
{在此粘贴第二步的 JSON 示例}
```
把 JSON 示例贴在提示词里,是减少字段漂移的有效办法。模型看到具体的键名,比只看到文字描述更不容易跑偏。
第二步:固定 JSON 中间格式
脚本和模型之间的契约就是这份 JSON。字段定得越死,后面越省事。
```json
{
"meta": {
"subject": "初中物理",
"grade": "八年级",
"title": "浮力",
"periods": 1
},
"slides": [
{
"type": "cover",
"title": "浮力",
"subtitle": "初中物理 · 八年级下册"
},
{
"type": "objectives",
"title": "学习目标",
"items": [
"知道浮力的方向与施力物体",
"会用弹簧测力计测量浮力大小",
"能说出阿基米德原理的内容"
]
},
{
"type": "bullets",
"title": "浮力的方向",
"bullets": [
"浸在液体中的物体受到向上的托力",
"施力物体是液体",
"方向总是竖直向上"
],
"note": "可让学生用手压水中的空瓶感受浮力"
},
{
"type": "table",
"title": "三种状态的受力关系",
"headers": ["状态", "受力关系", "常见实例"],
"rows": [
["漂浮", "F浮 = G", "木块"],
["悬浮", "F浮 = G", "悬浮的鸡蛋"],
["下沉", "F浮 < G", "铁块"]
]
},
{
"type": "practice",
"title": "课堂练习",
"items": [
"一物体重 5 N,浸没在水中时弹簧测力计读数为 3 N,求浮力大小",
"同一物体分别浸没在水和酒精中,哪次受到的浮力大?为什么?"
]
},
{
"type": "summary",
"title": "小结",
"items": ["浮力方向竖直向上", "阿基米德原理", "浮沉条件"]
}
]
}
```
type 字段是脚本的分发依据,先定 cover、objectives、bullets、table、practice、summary 六种,覆盖大多数课型。后面想加"实验步骤""思维导图"页,再加一个分支就行。
第三步:准备母版并确认版式索引
打开自己常用的 pptx 文件,删掉正文页,保留母版和版式,另存为 template.pptx。脚本里通常用"空白"版式,然后手动加文本框,这样做的好处是排版完全由脚本控制,不受占位符位置限制。
版式索引在不同模板中不一样,不要凭记忆写死。先跑一段代码把索引打出来:
```python
from pptx import Presentation
prs = Presentation("template.pptx")
for i, layout in enumerate(prs.slide_layouts):
print(i, layout.name)
for i, master in enumerate(prs.slide_masters):
print("master", i, master.name)
```
输出里名字类似"空白""标题幻灯片"的那一项,就是你要用的索引。记下来填到下一步的代码里。
第四步:写生成脚本
新建 build_ppt.py,把 JSON 读进来,按 type 分发到不同的渲染函数。下面是一份可直接改用的骨架:
```python
import json
from pptx import Presentation
from pptx.util import Cm, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.oxml.ns import qn
FONT = "微软雅黑"
DARK = (0x33, 0x33, 0x33)
ACCENT = (0x1F, 0x5C, 0xA8)
BLANK_LAYOUT_INDEX = 6 # 以第三步打印出的实际索引为准
def style_run(run, size, bold=False, color=DARK):
"""统一设置字号、颜色和中英文字体"""
run.font.size = Pt(size)
run.font.bold = bold
run.font.color.rgb = RGBColor(*color)
rPr = run._r.get_or_add_rPr()
font.name 只覆盖拉丁字母,中文需要额外写 a:ea
for tag in ("a:latin", "a:ea", "a:cs"):
for old in rPr.findall(qn(tag)):
rPr.remove(old)
el = rPr.makeelement(qn(tag), {"typeface": FONT})
rPr.append(el)
def add_paragraph(tf, text, size, bold=False, color=DARK, first=False):
p = tf.paragraphs[0] if first else tf.add_paragraph()
p.text = text
for run in p.runs:
style_run(run, size, bold, color)
return p
def add_title(slide, text, width):
box = slide.shapes.add_textbox(Cm(1.5), Cm(1.0), width, Cm(1.6))
tf = box.text_frame
tf.word_wrap = True
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
add_paragraph(tf, text, 32, bold=True, color=ACCENT, first=True)
return box
def add_body(slide, items, width, top=Cm(3.4), size=20):
box = slide.shapes.add_textbox(Cm(1.5), top, width, Cm(11))
tf = box.text_frame
tf.word_wrap = True
for i, item in enumerate(items):
add_paragraph(tf, "· " + item, size, first=(i == 0))
tf.paragraphs[-1].space_after = Pt(10)
return box
def add_table(slide, headers, rows, width, top=Cm(3.4)):
shape = slide.shapes.add_table(
len(rows) + 1, len(headers), Cm(1.5), top, width, Cm(1.0) * (len(rows) + 1)
)
table = shape.table
for c, h in enumerate(headers):
cell = table.cell(0, c)
cell.text = h
for p in cell.text_frame.paragraphs:
for run in p.runs:
style_run(run, 18, bold=True)
for r, row in enumerate(rows, start=1):
for c, val in enumerate(row):
cell = table.cell(r, c)
cell.text = val
for p in cell.text_frame.paragraphs:
for run in p.runs:
style_run(run, 16)
return table
def build(json_path, template_path, out_path):
with open(json_path, encoding="utf-8") as f:
data = json.load(f)
prs = Presentation(template_path)
blank = prs.slide_layouts[BLANK_LAYOUT_INDEX]
body_width = prs.slide_width - Cm(3.0)
for s in data["slides"]:
slide = prs.slides.add_slide(blank)
t = s.get("type", "bullets")
if t == "cover":
box = slide.shapes.add_textbox(Cm(2.0), Cm(7.0), body_width, Cm(3.0))
tf = box.text_frame
tf.word_wrap = True
add_paragraph(tf, s.get("title", ""), 44, bold=True, color=ACCENT, first=True)
add_paragraph(tf, s.get("subtitle", ""), 20)
elif t == "table":
add_title(slide, s.get("title", ""), body_width)
add_table(slide, s["headers"], s["rows"], body_width)
else:
add_title(slide, s.get("title", ""), body_width)
items = s.get("items") or s.get("bullets") or []
add_body(slide, items, body_width)
备注区放讲稿提示
if s.get("note"):
slide.notes_slide.notes_text_frame.text = s["note"]
prs.save(out_path)
print("已生成:", out_path)
if __name__ == "__main__":
build("lesson.json", "template.pptx", "浮力.pptx")
```
运行:
```bash
python build_ppt.py
```
打开生成的 pptx,重点检查三处:文字有没有溢出文本框、表格列宽是否合理、中文字体是否按预期显示。
第五步:处理公式、图片和留白
课件里绕不开三类特殊内容,各有各的处理方式。
公式。python-pptx 直接写数学公式的能力有限,实践中有两种做法:一是用纯文本近似表达,例如 F浮 = ρ液 g V排,配合上下标不明显的场景够用;二是先用公式编辑器或绘图工具导出透明背景 PNG,再用 slide.shapes.add_picture() 贴进去。脚本里可以预留占位:
```python
from pptx.util import Cm
from pptx.enum.shapes import MSO_SHAPE
def add_image_slot(slide, top=Cm(4.0), left=Cm(14.0)):
shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, left, top, Cm(10.0), Cm(7.0)
)
shape.text_frame.text = "此处插入实验示意图"
for p in shape.text_frame.paragraphs:
for run in p.runs:
style_run(run, 14, color=(0x99, 0x99, 0x99))
return shape
```
表格。前面脚本里已经封装好。列数控制在 3 到 4 列,行数控制在 6 行以内,投影到教室后排才看得清。
留白。一页塞太多字是课件通病。在提示词里限定"每条不超过 30 字""每页不超过 5 条",比事后手动删要省力。
第六步:批量生成与导出
一学期几十节课,逐节手工点太慢。把每节课的 JSON 放在 lessons/ 目录下,用一个循环跑完:
```python
from pathlib import Path
from build_ppt import build
for jf in sorted(Path("lessons").glob("*.json")):
build(str(jf), "template.pptx", f"out/{jf.stem}.pptx")
```
需要交付 PDF 版本时,可以用 LibreOffice 命令行转换(具体参数以官方文档为准):
```bash
soffice --headless --convert-to pdf --outdir out out/浮力.pptx
```
如果本机没有安装 LibreOffice,也可以直接用 PowerPoint 或 WPS 的"导出为 PDF"功能。
备选路线:Markdown 直出幻灯片
有些老师更习惯写 Markdown。这条路线的工具链更轻,适合讲义型课件:
```bash
pandoc lesson.md -o lesson.pptx --reference-doc=template.pptx
```
--reference-doc 指定母版,pandoc 会继承其中的字体和配色。用 Marp 则更适合直接投屏:
```bash
npx @marp-team/marp-cli lesson.md --pdf -o lesson.pdf
```
两条路线的取舍:需要精细控制每页版式、要嵌表格和公式图片,用 python-pptx;内容以文字为主、追求写作速度,用 Markdown 工具链。
常见坑与排错
版式索引对不上,生成的页面版式错乱。 不同模板的 slide_layouts 顺序不同,务必按第三步把索引打印出来再写进脚本。
中文显示成宋体或出现方框。 python-pptx 的 run.font.name 只设置拉丁字体,中文字体要写到 a:ea 节点上,也就是脚本里 style_run 那段。另一条更省事的路径是在母版里就把中文字体设好,脚本只调字号。
生成的 JSON 带 Markdown 围栏或前后废话。 解析前做一次清洗:截取第一个 { 到最后一个 } 之间的内容再 json.loads。仍然失败就重试一次,并在提示词里强化"只输出 JSON"。
文字溢出文本框。 自动缩放只在 PowerPoint 打开文件时才重新计算,脚本阶段看不出来。稳妥做法是控制每页条数和单条字数,并把文本框宽度按幻灯片宽度减去页边距来算。生成后逐页翻一遍仍是必要步骤。
学科内容出错。 模型可能把单位写错、把历史年代记混。物理单位、化学方程式、数学符号、史实年代这几类内容必须人工过一遍,这一步不能省。
图片版权。 优先使用自制图表、拍摄照片或明确标注可商用的素材,来源不明的网络图片不要直接放进课堂课件。
学生隐私。 学生姓名、成绩、家庭信息不要粘贴到外部服务里。做分层练习时,用"基础组、提升组"这类抽象标签替代真实姓名。
保存失败。 目标 pptx 正在被 PowerPoint 打开时,脚本会报权限错误。关闭文件再跑一次即可。
放映时字体变了。 换电脑演示前,用"嵌入字体"或在演示机上确认字体可用,否则行距和换行会跟原稿不一样。
下一步建议
流程跑通之后,可以从这几个方向继续打磨:
1. 沉淀模板库。按学科和课型分别准备母版,新授课、复习课、实验课的版式需求差别很大。
2. 沉淀提示词库。把每种课型的提示词存成文本文件,每次只替换课题和学情两段,比每次重写省时间。
3. 积累 JSON 样例。把生成效果好的 JSON 留下来,下次作为提示词里的示例,输出质量会稳定不少。
4. 配套讲稿。备注区已经预留了位置,可以让模型顺带生成逐字稿和板书设计,讲稿与课件分开放,改起来互不影响。
5. 接入分层练习。同一份知识点,生成基础、提升两套练习题,课后按学情发放。
6. 版本管理。用 Git 或网盘保留每学期的 JSON 与 pptx,第二年只需要更新学情和例题,不必从头再来。
课件本身只是载体,真正的价值在于把教师从排版里释放出来,把时间还给教学设计和课堂互动。
