|
|
@@ -0,0 +1,300 @@
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+from pptx import Presentation
|
|
|
+from pptx.dml.color import RGBColor
|
|
|
+from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE
|
|
|
+from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
|
|
+from pptx.util import Inches, Pt
|
|
|
+
|
|
|
+
|
|
|
+OUT_DIR = Path("public/ai-courseware-templates")
|
|
|
+W, H = 13.333, 7.5
|
|
|
+
|
|
|
+
|
|
|
+def rgb(value: str) -> RGBColor:
|
|
|
+ value = value.lstrip("#")
|
|
|
+ return RGBColor(int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16))
|
|
|
+
|
|
|
+
|
|
|
+def box(slide, x, y, w, h, color, radius=False, alpha=None, line=None):
|
|
|
+ shape_type = MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE if radius else MSO_AUTO_SHAPE_TYPE.RECTANGLE
|
|
|
+ shape = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
|
|
|
+ shape.fill.solid()
|
|
|
+ shape.fill.fore_color.rgb = rgb(color)
|
|
|
+ if alpha is not None:
|
|
|
+ shape.fill.transparency = alpha
|
|
|
+ shape.line.fill.background() if line is None else setattr(shape.line.color, "rgb", rgb(line))
|
|
|
+ return shape
|
|
|
+
|
|
|
+
|
|
|
+def circle(slide, x, y, d, color, alpha=None):
|
|
|
+ shape = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.OVAL, Inches(x), Inches(y), Inches(d), Inches(d))
|
|
|
+ shape.fill.solid()
|
|
|
+ shape.fill.fore_color.rgb = rgb(color)
|
|
|
+ if alpha is not None:
|
|
|
+ shape.fill.transparency = alpha
|
|
|
+ shape.line.fill.background()
|
|
|
+ return shape
|
|
|
+
|
|
|
+
|
|
|
+def line(slide, x1, y1, x2, y2, color, width=1.5, alpha=None):
|
|
|
+ shape = slide.shapes.add_connector(1, Inches(x1), Inches(y1), Inches(x2), Inches(y2))
|
|
|
+ shape.line.color.rgb = rgb(color)
|
|
|
+ shape.line.width = Pt(width)
|
|
|
+ if alpha is not None:
|
|
|
+ shape.line.transparency = alpha
|
|
|
+ return shape
|
|
|
+
|
|
|
+
|
|
|
+def text(slide, value, x, y, w, h, size=20, color="#1F2937", bold=False, font="Microsoft YaHei", align=PP_ALIGN.LEFT):
|
|
|
+ shape = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
|
|
|
+ frame = shape.text_frame
|
|
|
+ frame.clear()
|
|
|
+ frame.word_wrap = True
|
|
|
+ frame.vertical_anchor = MSO_ANCHOR.MIDDLE
|
|
|
+ paragraph = frame.paragraphs[0]
|
|
|
+ paragraph.alignment = align
|
|
|
+ run = paragraph.add_run()
|
|
|
+ run.text = value
|
|
|
+ run.font.name = font
|
|
|
+ run.font.size = Pt(size)
|
|
|
+ run.font.bold = bold
|
|
|
+ run.font.color.rgb = rgb(color)
|
|
|
+ return shape
|
|
|
+
|
|
|
+
|
|
|
+def placeholder(slide, label, x, y, w, h, border, fill="#FFFFFF"):
|
|
|
+ card = box(slide, x, y, w, h, fill, radius=True)
|
|
|
+ card.line.color.rgb = rgb(border)
|
|
|
+ card.line.width = Pt(1.2)
|
|
|
+ text(slide, label, x + 0.18, y + h / 2 - 0.18, w - 0.36, 0.36, 12, border, True, align=PP_ALIGN.CENTER)
|
|
|
+ return card
|
|
|
+
|
|
|
+
|
|
|
+def header(slide, title, eyebrow, palette, page_no, light=False):
|
|
|
+ primary, secondary, accent, background, text_color = palette
|
|
|
+ color = "#FFFFFF" if light else text_color
|
|
|
+ text(slide, eyebrow.upper(), 0.65, 0.38, 3.8, 0.28, 9, secondary if not light else "#D7E8E5", True)
|
|
|
+ text(slide, title, 0.65, 0.68, 8.7, 0.55, 25, color, True)
|
|
|
+ text(slide, f"{page_no:02d}", 12.05, 0.46, 0.55, 0.35, 11, accent if not light else "#F7CE77", True, align=PP_ALIGN.RIGHT)
|
|
|
+ line(slide, 0.65, 1.35, 12.65, 1.35, secondary if not light else "#FFFFFF", 1, 0.45)
|
|
|
+
|
|
|
+
|
|
|
+def add_notes(slide, primary="#3478C8"):
|
|
|
+ text(slide, "可替换标题", 0.7, 6.9, 1.3, 0.2, 8, primary, True)
|
|
|
+ text(slide, "可替换正文 / 图片 / 互动题", 2.1, 6.9, 3.3, 0.2, 8, "#8A96A3")
|
|
|
+
|
|
|
+
|
|
|
+def fairytale(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, bg)
|
|
|
+ circle(slide, 10.9, -0.55, 3.0, "#F9D86D", 0.18)
|
|
|
+ for x, y, d, c in [(0.0, 5.95, 2.2, "#A8D7A5"), (1.7, 6.2, 2.7, "#77BDA7"), (9.8, 6.05, 3.2, "#A8D7A5"), (11.3, 5.8, 2.4, "#77BDA7")]:
|
|
|
+ circle(slide, x, y, d, c, 0.1)
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "音乐小森林", 0.72, 1.05, 6.2, 0.7, 34, dark, True)
|
|
|
+ text(slide, "用耳朵发现声音,用身体回应节奏", 0.75, 1.83, 5.6, 0.4, 17, primary)
|
|
|
+ box(slide, 0.75, 2.7, 5.2, 0.9, "#FFFFFF", True)
|
|
|
+ text(slide, "课题名称 / 班级 / 执教教师", 1.05, 2.95, 4.6, 0.36, 16, "#61736F", True, align=PP_ALIGN.CENTER)
|
|
|
+ circle(slide, 8.3, 1.35, 2.1, "#F07F6A")
|
|
|
+ circle(slide, 9.55, 2.45, 1.6, "#F7C96D")
|
|
|
+ circle(slide, 7.35, 3.5, 1.25, "#49A795")
|
|
|
+ text(slide, "♪", 8.88, 1.72, 0.7, 0.7, 42, "#FFFFFF", True, align=PP_ALIGN.CENTER)
|
|
|
+ text(slide, "♫", 9.96, 2.77, 0.7, 0.7, 31, "#FFFFFF", True, align=PP_ALIGN.CENTER)
|
|
|
+ elif page == 2:
|
|
|
+ header(slide, "今天我们要做什么?", "学习目标", palette, page)
|
|
|
+ for idx, value in enumerate(["听见声音的变化", "用动作表现节奏", "和同伴合作展示"]):
|
|
|
+ circle(slide, 1.15, 2.05 + idx * 1.2, 0.58, [primary, secondary, accent][idx])
|
|
|
+ text(slide, str(idx + 1), 1.15, 2.14 + idx * 1.2, 0.58, 0.28, 14, "#FFFFFF", True, align=PP_ALIGN.CENTER)
|
|
|
+ placeholder(slide, value, 1.95, 1.95 + idx * 1.2, 8.2, 0.72, "#BFD8D1", "#FFFFFF")
|
|
|
+ placeholder(slide, "主视觉 / 情境插画", 10.55, 1.95, 2.0, 3.95, "#6EBAA4", "#E7F4EE")
|
|
|
+ else:
|
|
|
+ header(slide, ["听一听,找一找", "动一动,拍一拍", "收获小星星", "课后继续玩音乐", "课堂回声"][page - 3], "课堂任务", palette, page)
|
|
|
+ placeholder(slide, "主视觉 / 图片", 0.72, 1.75, 5.25, 4.4, "#BFD8D1", "#FFFFFF")
|
|
|
+ placeholder(slide, "一句学生任务", 6.42, 1.75, 5.95, 0.75, "#BFD8D1", "#FFFFFF")
|
|
|
+ for idx in range(3):
|
|
|
+ placeholder(slide, f"步骤 {idx + 1} / 可替换短句", 6.42, 2.85 + idx * 0.95, 5.95, 0.66, "#BFD8D1", "#FFFFFF")
|
|
|
+ add_notes(slide, primary)
|
|
|
+
|
|
|
+
|
|
|
+def warm(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, bg)
|
|
|
+ box(slide, 0, 0, W, 0.18, secondary)
|
|
|
+ circle(slide, 10.6, 0.8, 1.55, "#F8D5B1", 0.15)
|
|
|
+ circle(slide, 11.75, 1.72, 0.92, "#E9A7A7", 0.18)
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "把音乐带进今天", 0.78, 1.0, 6.6, 0.72, 33, dark, True)
|
|
|
+ text(slide, "温暖、清晰、适合师生共同阅读的音乐课堂", 0.82, 1.83, 6.4, 0.38, 16, "#5D6E76")
|
|
|
+ placeholder(slide, "课题名称", 0.82, 2.73, 4.9, 0.8, "#8FBFB3", "#FFFFFF")
|
|
|
+ box(slide, 7.25, 1.22, 4.8, 4.25, "#FFFFFF", True)
|
|
|
+ text(slide, "♪", 8.25, 1.75, 2.6, 1.4, 66, primary, True, align=PP_ALIGN.CENTER)
|
|
|
+ text(slide, "用一段旋律,开启一节课", 7.8, 3.55, 3.7, 0.42, 15, "#5B6E75", True, align=PP_ALIGN.CENTER)
|
|
|
+ else:
|
|
|
+ titles = ["学习地图", "感受这段音乐", "发现音乐小秘密", "合作练习", "我的课堂收获", "课后聆听"]
|
|
|
+ header(slide, titles[page - 2], "温暖手绘", palette, page)
|
|
|
+ if page in {2, 6}:
|
|
|
+ for idx in range(3):
|
|
|
+ placeholder(slide, f"{idx + 1}. 可替换学习要点", 0.9, 1.95 + idx * 1.05, 7.0, 0.68, "#9ECABD", "#FFFFFF")
|
|
|
+ placeholder(slide, "插画 / 图片", 8.45, 1.95, 3.8, 3.75, "#9ECABD", "#ECF6F1")
|
|
|
+ elif page == 5:
|
|
|
+ for idx in range(4):
|
|
|
+ box(slide, 0.9 + idx * 2.9, 2.15, 2.35, 1.8, "#FFFFFF", True)
|
|
|
+ text(slide, f"第 {idx + 1} 步", 1.12 + idx * 2.9, 2.45, 1.9, 0.28, 12, secondary, True, align=PP_ALIGN.CENTER)
|
|
|
+ text(slide, "可替换任务", 1.12 + idx * 2.9, 2.95, 1.9, 0.35, 15, dark, True, align=PP_ALIGN.CENTER)
|
|
|
+ else:
|
|
|
+ placeholder(slide, "主视觉 / 听赏素材", 0.9, 1.9, 5.2, 3.95, "#9ECABD", "#FFFFFF")
|
|
|
+ placeholder(slide, "课堂问题", 6.65, 1.9, 5.55, 0.75, "#9ECABD", "#FFFFFF")
|
|
|
+ placeholder(slide, "学生观察 / 回答区", 6.65, 2.95, 5.55, 2.9, "#9ECABD", "#FFFFFF")
|
|
|
+ add_notes(slide, primary)
|
|
|
+
|
|
|
+
|
|
|
+def doodle(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, "#17233D")
|
|
|
+ for x, y, symbol, size in [(0.7, 0.6, "♪", 22), (11.75, 0.62, "♫", 25), (11.65, 6.55, "♩", 19), (0.72, 6.5, "♬", 18)]:
|
|
|
+ text(slide, symbol, x, y, 0.6, 0.5, size, "#FFFFFF", True)
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "音符涂鸦课堂", 0.85, 1.05, 6.4, 0.72, 34, "#FFFFFF", True)
|
|
|
+ text(slide, "让节奏、旋律和想法在黑板上跳起来", 0.88, 1.85, 6.5, 0.36, 16, "#D5E0F1")
|
|
|
+ box(slide, 0.88, 2.72, 5.0, 0.85, "#FFFFFF", True)
|
|
|
+ text(slide, "课题名称", 1.15, 2.95, 4.45, 0.36, 16, "#17233D", True, align=PP_ALIGN.CENTER)
|
|
|
+ circle(slide, 8.5, 1.55, 2.15, accent)
|
|
|
+ circle(slide, 9.85, 3.05, 1.35, secondary)
|
|
|
+ text(slide, "♪", 9.12, 2.0, 0.85, 0.55, 36, "#17233D", True, align=PP_ALIGN.CENTER)
|
|
|
+ else:
|
|
|
+ titles = ["节奏挑战", "旋律发现", "听辨 A / B", "小组创作", "快速检测", "一句话收获"]
|
|
|
+ header(slide, titles[page - 2], "music doodle", palette, page, light=True)
|
|
|
+ if page in {2, 4, 6}:
|
|
|
+ placeholder(slide, "可替换任务标题", 0.85, 1.85, 7.1, 0.75, "#93B0D8", "#FFFFFF")
|
|
|
+ for idx in range(3):
|
|
|
+ box(slide, 0.85, 2.95 + idx * 0.86, 0.55, 0.55, [accent, secondary, primary][idx], True)
|
|
|
+ text(slide, str(idx + 1), 0.85, 3.07 + idx * 0.86, 0.55, 0.22, 12, "#17233D", True, align=PP_ALIGN.CENTER)
|
|
|
+ placeholder(slide, "可替换步骤或选项", 1.62, 2.88 + idx * 0.86, 6.33, 0.68, "#93B0D8", "#FFFFFF")
|
|
|
+ placeholder(slide, "节奏谱 / 图片", 8.45, 1.85, 3.7, 3.7, "#93B0D8", "#FFFFFF")
|
|
|
+ else:
|
|
|
+ placeholder(slide, "主视觉", 0.85, 1.85, 5.15, 3.95, "#93B0D8", "#FFFFFF")
|
|
|
+ placeholder(slide, "观察问题", 6.5, 1.85, 5.65, 0.72, "#93B0D8", "#FFFFFF")
|
|
|
+ placeholder(slide, "学生回答 / 对比结果", 6.5, 2.9, 5.65, 2.9, "#93B0D8", "#FFFFFF")
|
|
|
+ add_notes(slide, "#FFFFFF")
|
|
|
+
|
|
|
+
|
|
|
+def ink(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, "#F7F1E7")
|
|
|
+ for x, y, w, h, c, a in [(8.4, 5.8, 4.9, 1.2, "#36594B", 0.25), (9.7, 5.25, 3.7, 1.25, "#36594B", 0.38), (10.7, 4.85, 2.6, 1.1, "#9D2B22", 0.78)]:
|
|
|
+ box(slide, x, y, w, h, c, True, a)
|
|
|
+ circle(slide, 11.75, 0.48, 0.48, "#9D2B22")
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "听见东方的留白", 0.82, 1.1, 6.6, 0.76, 34, "#332A24", True, font="Microsoft YaHei")
|
|
|
+ text(slide, "民乐、戏曲与传统文化主题的音乐课件", 0.85, 1.92, 6.2, 0.36, 16, "#746A5F")
|
|
|
+ line(slide, 0.85, 2.6, 4.9, 2.6, accent, 2)
|
|
|
+ placeholder(slide, "课题名称", 0.85, 3.0, 4.85, 0.78, "#B99058", "#FFFDF8")
|
|
|
+ text(slide, "印", 11.86, 0.58, 0.27, 0.22, 9, "#FFFFFF", True, align=PP_ALIGN.CENTER)
|
|
|
+ else:
|
|
|
+ titles = ["入境:一幅声音画卷", "初听:听见什么", "乐器与音色", "文化线索", "表现与分享", "余韵"]
|
|
|
+ header(slide, titles[page - 2], "东方音乐", palette, page)
|
|
|
+ if page in {2, 5}:
|
|
|
+ placeholder(slide, "意境主视觉 / 山水或乐器", 0.85, 1.85, 5.35, 3.95, "#B99058", "#FFFDF8")
|
|
|
+ placeholder(slide, "一条核心问题", 6.7, 1.85, 5.35, 0.75, "#B99058", "#FFFDF8")
|
|
|
+ for idx in range(3):
|
|
|
+ placeholder(slide, f"可替换文化线索 {idx + 1}", 6.7, 2.95 + idx * 0.9, 5.35, 0.62, "#B99058", "#FFFDF8")
|
|
|
+ else:
|
|
|
+ for idx in range(3):
|
|
|
+ box(slide, 1.05 + idx * 3.85, 2.05, 3.1, 2.65, "#FFFDF8", True)
|
|
|
+ text(slide, f"线索 {idx + 1}", 1.35 + idx * 3.85, 2.35, 2.5, 0.3, 12, primary, True, align=PP_ALIGN.CENTER)
|
|
|
+ text(slide, "可替换内容", 1.35 + idx * 3.85, 3.15, 2.5, 0.4, 16, dark, True, align=PP_ALIGN.CENTER)
|
|
|
+ add_notes(slide, primary)
|
|
|
+
|
|
|
+
|
|
|
+def stage(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, "#1C2940")
|
|
|
+ circle(slide, 8.2, -1.05, 4.0, "#D8B45B", 0.7)
|
|
|
+ circle(slide, 10.6, -0.5, 3.3, "#6B7E9E", 0.72)
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "舞台正在发生", 0.85, 1.08, 7.0, 0.75, 35, "#FFFFFF", True)
|
|
|
+ text(slide, "用现场感打开世界音乐与演出欣赏", 0.88, 1.95, 6.7, 0.36, 16, "#DDE6F4")
|
|
|
+ box(slide, 0.88, 2.85, 5.2, 0.82, "#FFFFFF", True)
|
|
|
+ text(slide, "课题名称", 1.15, 3.07, 4.65, 0.36, 16, dark, True, align=PP_ALIGN.CENTER)
|
|
|
+ line(slide, 0.88, 4.35, 5.05, 4.35, accent, 2)
|
|
|
+ else:
|
|
|
+ titles = ["演出现场:先看再听", "声音从哪里来", "音乐要素拆解", "文化背景", "编排一段小演出", "谢幕与回响"]
|
|
|
+ header(slide, titles[page - 2], "stage documentary", palette, page, light=True)
|
|
|
+ if page in {2, 3, 5}:
|
|
|
+ placeholder(slide, "横版演出照片 / 主视觉", 0.85, 1.85, 7.25, 3.85, "#C7D2E2", "#FFFFFF")
|
|
|
+ placeholder(slide, "作品信息 / 核心问题", 8.55, 1.85, 3.65, 0.78, "#C7D2E2", "#FFFFFF")
|
|
|
+ placeholder(slide, "听赏提示", 8.55, 2.98, 3.65, 1.25, "#C7D2E2", "#FFFFFF")
|
|
|
+ placeholder(slide, "感受或证据", 8.55, 4.55, 3.65, 1.15, "#C7D2E2", "#FFFFFF")
|
|
|
+ else:
|
|
|
+ for idx in range(3):
|
|
|
+ placeholder(slide, f"演出线索 {idx + 1}", 0.9 + idx * 3.9, 2.05, 3.2, 2.7, "#C7D2E2", "#FFFFFF")
|
|
|
+ add_notes(slide, "#FFFFFF")
|
|
|
+
|
|
|
+
|
|
|
+def geometry(slide, page, palette):
|
|
|
+ primary, secondary, accent, bg, dark = palette
|
|
|
+ box(slide, 0, 0, W, H, "#F8FBFA")
|
|
|
+ box(slide, 8.7, 0, 4.63, H, "#E7F3EF")
|
|
|
+ box(slide, 9.55, 1.05, 2.8, 1.2, secondary, True, 0.15)
|
|
|
+ box(slide, 10.85, 5.55, 1.8, 1.1, accent, True, 0.2)
|
|
|
+ if page == 1:
|
|
|
+ text(slide, "看见旋律的结构", 0.82, 1.1, 7.3, 0.74, 34, dark, True)
|
|
|
+ text(slide, "用清晰关系图、时间线与对比框架组织知识", 0.86, 1.93, 6.8, 0.36, 16, "#657871")
|
|
|
+ placeholder(slide, "课题名称", 0.86, 2.9, 4.85, 0.78, "#6FAE99", "#FFFFFF")
|
|
|
+ for idx, x in enumerate([8.95, 10.25, 11.55]):
|
|
|
+ circle(slide, x, 3.0 + idx * 0.58, 0.42, [primary, secondary, accent][idx])
|
|
|
+ line(slide, x + 0.42, 3.21 + idx * 0.58, x + 1.0, 3.21 + idx * 0.58, primary, 1.2, 0.35)
|
|
|
+ else:
|
|
|
+ titles = ["学习路线", "旋律时间线", "结构对比", "规律发现", "项目任务", "结论与迁移"]
|
|
|
+ header(slide, titles[page - 2], "modern geometry", palette, page)
|
|
|
+ if page in {2, 3, 4}:
|
|
|
+ placeholder(slide, "概念 / 主问题", 0.85, 1.78, 11.6, 0.72, "#7BB6A4", "#FFFFFF")
|
|
|
+ for idx in range(4):
|
|
|
+ x = 0.95 + idx * 3.0
|
|
|
+ circle(slide, x, 3.15, 0.58, [primary, secondary, accent, primary][idx])
|
|
|
+ text(slide, str(idx + 1), x, 3.3, 0.58, 0.18, 11, "#FFFFFF", True, align=PP_ALIGN.CENTER)
|
|
|
+ placeholder(slide, f"结构节点 {idx + 1}", x - 0.35, 4.0, 1.28, 0.9, "#7BB6A4", "#FFFFFF")
|
|
|
+ if idx < 3:
|
|
|
+ line(slide, x + 0.58, 3.43, x + 2.75, 3.43, secondary, 1.5, 0.35)
|
|
|
+ else:
|
|
|
+ placeholder(slide, "左侧:素材 / 图表 / 旋律片段", 0.85, 1.85, 5.35, 3.85, "#7BB6A4", "#FFFFFF")
|
|
|
+ placeholder(slide, "右侧:推理步骤与结论", 6.7, 1.85, 5.55, 3.85, "#7BB6A4", "#FFFFFF")
|
|
|
+ add_notes(slide, primary)
|
|
|
+
|
|
|
+
|
|
|
+BUILDERS = {
|
|
|
+ "fairytale-forest": (fairytale, ("#3478C8", "#32A07C", "#F08A3C", "#F7FBF8", "#24495C")),
|
|
|
+ "warm-handdrawn": (warm, ("#347D73", "#8FC6B6", "#E98B73", "#F7FBFA", "#284C52")),
|
|
|
+ "music-doodle": (doodle, ("#5164A7", "#80B9D7", "#F6C945", "#17233D", "#17233D")),
|
|
|
+ "ink-wash": (ink, ("#9D2B22", "#36594B", "#C58A35", "#F7F1E7", "#332A24")),
|
|
|
+ "stage-documentary": (stage, ("#D8B45B", "#8AA1C2", "#E7C469", "#1C2940", "#1C2940")),
|
|
|
+ "modern-geometry": (geometry, ("#287B72", "#4FAD93", "#E07A35", "#F8FBFA", "#20342F")),
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def build_template(template_id: str, builder, palette) -> None:
|
|
|
+ presentation = Presentation()
|
|
|
+ presentation.slide_width = Inches(W)
|
|
|
+ presentation.slide_height = Inches(H)
|
|
|
+ presentation.core_properties.title = f"{template_id} 音乐课件母版"
|
|
|
+ presentation.core_properties.author = "系统模板"
|
|
|
+ presentation.core_properties.subject = "可编辑音乐课件母版"
|
|
|
+ blank = presentation.slide_layouts[6]
|
|
|
+ for page in range(1, 8):
|
|
|
+ slide = presentation.slides.add_slide(blank)
|
|
|
+ builder(slide, page, palette)
|
|
|
+ presentation.save(OUT_DIR / f"{template_id}.pptx")
|
|
|
+
|
|
|
+
|
|
|
+def main() -> None:
|
|
|
+ OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
+ for template_id, (builder, palette) in BUILDERS.items():
|
|
|
+ build_template(template_id, builder, palette)
|
|
|
+ print(f"{template_id}.pptx")
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|