Getting started¶
This page takes you from zero to a first polite edit, both from Python and from the CLI.
Prerequisites¶
- Windows. pptlive talks to PowerPoint over COM (
pywin32); there is no cross-platform path. - Microsoft PowerPoint, installed and running, with a deck open. pptlive
attaches to the running app — it never closes it, and it can't run
PowerPoint hidden (
Application.Visible = Falseis refused by the app). - Python 3.10+.
Install¶
pywin32 is pulled in automatically on Windows. Click is the only other
runtime dependency. The MCP server is an optional extra — see
MCP server.
Hello, deck¶
Open a presentation in PowerPoint, then run:
import pptlive as pl
with pl.attach() as ppt:
deck = ppt.presentations.active
print(deck.name)
for entry in deck.outline():
print(f"{entry['slide']:>3}. {entry['title']}")
for bullet in entry["bullets"]:
print(f" • {bullet}")
attach() connects to the already-running PowerPoint instance — it won't
launch one. If PowerPoint isn't running you get a
PowerPointNotRunningError. Use
pl.connect(launch_if_missing=True) when
you'd rather launch PowerPoint if it isn't already up.
Every shape listing carries an anchor_id like ph:3:title or shape:3:2 —
those strings are how the CLI and LLM tool-use loops address text. See
Anchor IDs for the scheme.
Your first polite edit¶
import pptlive as pl
with pl.attach() as ppt:
deck = ppt.presentations.active
with deck.edit("Update the agenda slide"):
deck.anchor_by_id("ph:2:title").set_text("Agenda")
deck.anchor_by_id("ph:2:body").set_text("Intro\nDemo\nQ&A")
A few things are happening that aren't obvious from the code:
deck.edit("…")callsApplication.StartNewUndoEntry()on entry, fencing the block. PowerPoint groups the in-session edits that follow, so both mutations collapse into a single Ctrl-Z.- Before the block runs, the user's viewed slide and shape/text
Selectionare snapshotted. On exit they're restored — your script does not jump the user to a different slide. See Politeness. - If a placeholder is missing, you get a typed
AnchorNotFoundError, not a raw COM error. Setting text on a shape with no text frame (a picture, a line) raisesNoTextFrameError.
Same task from the CLI¶
The CLI is intentionally thin over the Python API. Same atomic-undo, same politeness, JSON on stdout:
# What's open?
pptlive status
# What's in the active deck?
pptlive slides # one row per slide (index, layout, title, …)
pptlive outline # titles + body bullets
pptlive slide read 2 # every shape on slide 2 (anchor_id, name, type, geometry)
# Mutate (each is one Ctrl-Z).
pptlive write --anchor-id ph:2:title --text "Agenda"
pptlive write --anchor-id ph:2:body --text "Intro\nDemo\nQ&A"
Every command emits one JSON object on stdout (--text if you'd rather read
it) and uses deterministic exit codes:
| Exit | Meaning |
|---|---|
0 |
ok |
2 |
anchor / slide / shape / deck not found |
3 |
PowerPoint busy / modal dialog |
4 |
PowerPoint not running |
5 |
ambiguous match |
6 |
shape has no text frame |
1 |
other error |
Full reference: CLI.
Two more everyday flows¶
Let a vision model see the slide it just built¶
PowerPoint renders the live, unsaved state — so you can build, look, and iterate:
with pl.attach() as ppt:
deck = ppt.presentations.active
png = deck.slides[4].export_image(width=1280) # temp PNG (or pass a path)
# ...hand `png` to your image tool, look, then revise.
export_image is polite — it doesn't move the user's view. See
Cookbook §5.
Read what the user is looking at¶
Most useful when you want to feed the user's current focus to an LLM, or act on a hotkey-driven selection:
with pl.attach() as ppt:
deck = ppt.presentations.active
sel = deck.selection() # SelectionInfo: type, slide, anchor_id, …
if sel.anchor_id:
print(f"User is on slide {sel.slide}, selection -> {sel.anchor_id}")
else:
print("Nothing selected.")
selection() never moves the user. To act on the selection, target the
opt-in here: anchor. See
Cookbook §6.
Where to next¶
- Concepts — the ideas that shape every pptlive API:
politeness, semantic anchors, the hierarchical anchor scheme, and
EditScope. - Cookbook — end-to-end recipes including an LLM tool-use loop.
- Python API — auto-generated reference for every public symbol.
- Driving an LLM agent? The MCP server exposes the same live control to Claude Desktop and other MCP clients.