| name | pptx-generation |
| description | Generate academic PowerPoint presentations (.pptx) using python-pptx. Use this skill for making PPT, slides, presentations, 生成PPT, 做PPT, 写PPT, 幻灯片. Provides complete helper functions and templates. Preferred over scientific-slides and frontend-slides for all PPTX generation. |
Academic Presentation Generation
Overview
Create publication-quality academic presentations (.pptx) for group meetings, thesis defenses, conference talks, and product introductions.
Templates
Group Meeting (10-15 slides)
- Title slide (project name, date, presenter)
- Background / Context (1-2 slides)
- This Week's Progress (3-5 slides)
- Results & Figures (2-3 slides)
- Challenges / Questions (1 slide)
- Next Steps (1 slide)
Thesis Defense (30-50 slides)
- Title → 2. Outline → 3. Background (5-8) → 4. Aims (1-2) → 5-7. Aim chapters (8-12 each) → 8. Discussion (2-3) → 9. Conclusions (1-2) → 10. Future → 11. Acknowledgments → 12. Backup
Conference Talk (12-20 slides, 10-15 min)
- Title → 2. Problem → 3. Key Question → 4. Approach → 5-9. Results → 10. Implications → 11. Acknowledgments
Product / Project Introduction (10-15 slides)
- Title → 2. Problem → 3. Solution → 4. Architecture → 5-8. Core features → 9-10. Case studies → 11. Comparison → 12. Quick start → 13. Roadmap → 14. Summary
Design Principles
- One message per slide: two messages → two slides
- Visual hierarchy: title → key message → supporting data → source
- Minimal text: bullet points, not paragraphs
- Figure-first: lead with figures, explain verbally
- Consistent styling: same fonts, colors, layout throughout
Technical Implementation
Dependencies
pip install -q python-pptx Pillow
CRITICAL: Execution Pattern
ALWAYS write the ENTIRE script FIRST, then run it. NEVER send text before running the script.
The script MUST be self-contained. Install deps, define helpers, build slides, save — all in ONE bash call:
pip install -q python-pptx Pillow 2>/dev/null && python3 << 'PPTXEOF'
PPTXEOF
Color Themes
Academic palettes — pick ONE and use consistently:
THEMES = {
"lancet": {
"primary": (0x00, 0x46, 0x8B),
"accent": (0xED, 0x00, 0x00),
"accent2": (0x42, 0xB5, 0x40),
"light": (0xE8, 0xEE, 0xF4),
"text": (0x33, 0x33, 0x33),
"muted": (0x88, 0x88, 0x88),
},
"npg": {
"primary": (0x3C, 0x54, 0x88),
"accent": (0xE6, 0x4B, 0x35),
"accent2": (0x00, 0xA0, 0x87),
"light": (0xEE, 0xF0, 0xF5),
"text": (0x33, 0x33, 0x33),
"muted": (0x84, 0x91, 0xB4),
},
"nejm": {
"primary": (0x00, 0x72, 0xB5),
: (, , ),
: (, , ),
: (, , ),
: (, , ),
: (, , ),
},
}
Complete Template Script
Copy this entire template. Fill in the SLIDES list with actual content. Do not restructure the helpers — just fill in content.
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.dml.color import RGBColor
import os
OUTPUT_DIR = os.path.expanduser("~/.scienceclaw/workspace")
os.makedirs(OUTPUT_DIR, exist_ok=True)
FILENAME = "presentation.pptx"
OUTPUT_PATH = os.path.join(OUTPUT_DIR, FILENAME)
THEME = "lancet"
T = {
"lancet": {"p":(0,70,139),"a":(237,0,0),"a2":(66,181,64),"l":(232,238,244),"t":(51,51,51),"m":(136,136,136)},
"npg": {"p":(60,84,136),"a":(230,75,53),"a2":(0,160,135),:(,,),:(,,),:(,,)},
: {:(,,),:(,,),:(,,),:(,,),:(,,),:(,,)},
}[THEME]
(): RGBColor(*T[key])
prs = Presentation()
prs.slide_width = Inches()
prs.slide_height = Inches()
W, H = prs.slide_width, prs.slide_height
():
s = slide.shapes.add_shape(, left, top, w, h)
s.line.fill.background()
fill: s.fill.solid(); s.fill.fore_color.rgb = RGBColor(*fill)
: s.fill.background()
s
():
box = slide.shapes.add_textbox(left, top, w, h)
box.text_frame.word_wrap =
p = box.text_frame.paragraphs[]
p.text = text
p.font.size = Pt(size)
p.font.bold = bold
p.font.color.rgb = rgb(color)
p.alignment = align
box.text_frame.paragraphs[].space_after = Pt()
box
():
box = slide.shapes.add_textbox(left, top, w, h)
tf = box.text_frame
tf.word_wrap =
i, item (items):
p = tf.paragraphs[] i == tf.add_paragraph()
p.text = item
p.font.size = Pt(size)
p.font.color.rgb = rgb(color)
p.space_after = Pt()
p.level =
box
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, H, fill=T[])
add_shape(s, , Inches(), W, Inches(), fill=((,T[][]-), (,T[][]-), (,T[][]-)))
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=, align=PP_ALIGN.LEFT)
subtitle:
add_text(s, Inches(), Inches(), Inches(), Inches(), subtitle, size=, color=)
presenter:
add_text(s, Inches(), Inches(), Inches(), Inches(), presenter, size=, color=)
s
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, H, fill=T[])
add_shape(s, Inches(), Inches(), Inches(), Pt(), fill=T[])
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=)
s
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, Inches(), fill=T[])
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=)
add_shape(s, Inches(), Inches(), Inches(), Pt(), fill=T[])
add_bullets(s, Inches(), Inches(), Inches(), Inches(), bullets, size=)
note:
add_text(s, Inches(), Inches(), Inches(), Inches(), note, size=, color=)
s
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, Inches(), fill=T[])
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=)
add_shape(s, Inches(), Inches(), Inches(), Pt(), fill=T[])
left_title:
add_text(s, Inches(), Inches(), Inches(), Inches(), left_title, size=, bold=, color=)
add_bullets(s, Inches(), Inches(), Inches(), Inches(), left_items, size=)
right_title:
add_text(s, Inches(), Inches(), Inches(), Inches(), right_title, size=, bold=, color=)
add_bullets(s, Inches(), Inches(), Inches(), Inches(), right_items, size=)
s
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, Inches(), fill=T[])
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=)
add_shape(s, Inches(), Inches(), Inches(), Pt(), fill=T[])
cols = (headers)
tbl = s.shapes.add_table((rows)+, cols, Inches(), Inches(), Inches(), Inches()).table
j, h (headers):
c = tbl.cell(, j); c.text = h
p c.text_frame.paragraphs:
p.font.size = Pt(); p.font.bold = ; p.font.color.rgb = RGBColor(,,)
c.fill.solid(); c.fill.fore_color.rgb = rgb()
i, row (rows):
j, val (row):
c = tbl.cell(i+, j); c.text = (val)
p c.text_frame.paragraphs:
p.font.size = Pt(); p.font.color.rgb = rgb()
c.fill.solid(); c.fill.fore_color.rgb = RGBColor(*(T[] i%== (,,)))
s
():
s = prs.slides.add_slide(prs.slide_layouts[])
add_shape(s, , , W, H, fill=T[])
add_text(s, Inches(), Inches(), Inches(), Inches(), title, size=, bold=, color=, align=PP_ALIGN.CENTER)
items:
add_bullets(s, Inches(), Inches(), Inches(), Inches(), items, size=, color=)
s
title_slide(
,
subtitle=,
presenter=,
)
content_slide(, [
,
,
,
])
end_slide(, [])
prs.save(OUTPUT_PATH)
()
Output Convention
- Save to
~/.scienceclaw/workspace/<descriptive-name>.pptx
- Print the absolute path and slide count on success
- If the script fails, print the traceback — do NOT silently swallow errors
Error Handling
- If
python-pptx fails to install: tell the user to run pip install python-pptx on the host
- If script errors: read traceback, fix, re-run (max 3 attempts)
- After success: verify the file exists and has non-zero size before telling the user
Language & Content Rules
- Match the user's language. If user writes in Chinese, all slide content must be Chinese.
- Keep bullet points to 3-5 per slide, 6-10 words per bullet.
- Use the academic palette consistently.
- The model decides which helper to use for each slide (title_slide, content_slide, two_col_slide, table_slide, section_slide, end_slide).