Veloris.
返回索引
设计实战 2026-02-14

Python操作PPT:python-pptx批量生成幻灯片,数据驱动的演示文稿

2 分钟
415 words

Python操作PPT:python-pptx批量生成幻灯片,数据驱动的演示文稿

PowerPoint是制作演示文稿的常用工具。python-pptx库可以创建、读取和修改PPT文件,实现报告自动生成、模板填充等任务。本篇将介绍如何使用Python自动化PPT操作。


1. python-pptx简介

pip install python-pptx
from pptx import Presentation
from pptx.util import Inches, Pt, Cm, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.chart import XL_CHART_TYPE

# 单位说明
# Inches:英寸
# Pt:磅
# Cm:厘米
# Emu:英制公制单位

2. 创建PPT

from pptx import Presentation
from pptx.util import Inches, Pt

# 创建演示文稿
prs = Presentation()

# 设置幻灯片大小(默认是4:3)
prs.slide_width = Inches(13.333)  # 16:9宽度
prs.slide_height = Inches(7.5)    # 16:9高度

# 获取幻灯片布局
# 0: 标题幻灯片
# 1: 标题和内容
# 2: 节标题
# 3: 两栏内容
# 4: 比较
# 5: 仅标题
# 6: 空白
# 7: 内容与标题
# 8: 图片与标题

# 添加标题幻灯片
title_slide_layout = prs.slide_layouts[0]
slide = prs.slides.add_slide(title_slide_layout)

# 设置标题和副标题
title = slide.shapes.title
subtitle = slide.placeholders[1]

title.text = "演示文稿标题"
subtitle.text = "副标题内容\n作者:张三"

# 添加内容幻灯片
content_layout = prs.slide_layouts[1]
slide2 = prs.slides.add_slide(content_layout)

title2 = slide2.shapes.title
title2.text = "内容页标题"

body = slide2.placeholders[1]
tf = body.text_frame
tf.text = "第一个要点"

# 添加更多段落
p = tf.add_paragraph()
p.text = "第二个要点"
p.level = 0

p = tf.add_paragraph()
p.text = "子要点"
p.level = 1

# 保存
prs.save('presentation.pptx')

3. 幻灯片布局

from pptx import Presentation

# 使用模板
prs = Presentation('template.pptx')

# 查看可用布局
for i, layout in enumerate(prs.slide_layouts):
    print(f"{i}: {layout.name}")

# 查看布局中的占位符
layout = prs.slide_layouts[1]
for shape in layout.placeholders:
    print(f"idx: {shape.placeholder_format.idx}, type: {shape.placeholder_format.type}")

# 使用特定布局
slide = prs.slides.add_slide(prs.slide_layouts[5])  # 仅标题

# 删除幻灯片
def delete_slide(prs, index):
    """删除指定索引的幻灯片"""
    rId = prs.slides._sldIdLst[index].rId
    prs.part.drop_rel(rId)
    del prs.slides._sldIdLst[index]

# 复制幻灯片(需要手动实现)
def duplicate_slide(prs, index):
    """复制幻灯片"""
    source = prs.slides[index]
    # 使用相同布局创建新幻灯片
    new_slide = prs.slides.add_slide(source.slide_layout)
    # 复制形状(简化版)
    for shape in source.shapes:
        if shape.has_text_frame:
            # 复制文本框
            pass
    return new_slide

4. 文本操作

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])  # 空白布局

# 添加文本框
left = Inches(1)
top = Inches(1)
width = Inches(8)
height = Inches(1)

textbox = slide.shapes.add_textbox(left, top, width, height)
tf = textbox.text_frame

# 设置文本框属性
tf.word_wrap = True  # 自动换行
tf.auto_size = None  # 不自动调整大小

# 添加文本
tf.text = "这是第一段文本"

# 添加更多段落
p = tf.add_paragraph()
p.text = "这是第二段文本"

# 设置段落格式
p.alignment = PP_ALIGN.CENTER  # 居中
# PP_ALIGN: LEFT, CENTER, RIGHT, JUSTIFY

p.level = 0  # 缩进级别
p.space_before = Pt(12)  # 段前间距
p.space_after = Pt(12)   # 段后间距

# 设置字体格式
for paragraph in tf.paragraphs:
    for run in paragraph.runs:
        run.font.name = '微软雅黑'
        run.font.size = Pt(18)
        run.font.bold = True
        run.font.italic = False
        run.font.color.rgb = RGBColor(0, 0, 128)

# 设置文本框垂直对齐
tf.paragraphs[0].alignment = PP_ALIGN.CENTER

# 添加带格式的文本
p2 = tf.add_paragraph()
run1 = p2.add_run()
run1.text = "普通文本 "

run2 = p2.add_run()
run2.text = "粗体文本 "
run2.font.bold = True

run3 = p2.add_run()
run3.text = "红色文本"
run3.font.color.rgb = RGBColor(255, 0, 0)

prs.save('text.pptx')

5. 形状操作

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import PP_ALIGN

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])

# 添加矩形
shape = slide.shapes.add_shape(
    MSO_SHAPE.RECTANGLE,
    Inches(1), Inches(1),  # 位置
    Inches(3), Inches(1)   # 大小
)
shape.text = "矩形"

# 设置填充颜色
fill = shape.fill
fill.solid()
fill.fore_color.rgb = RGBColor(0, 176, 240)

# 设置边框
line = shape.line
line.color.rgb = RGBColor(0, 0, 0)
line.width = Pt(2)

# 添加圆角矩形
shape2 = slide.shapes.add_shape(
    MSO_SHAPE.ROUNDED_RECTANGLE,
    Inches(5), Inches(1),
    Inches(3), Inches(1)
)
shape2.text = "圆角矩形"

# 添加椭圆
shape3 = slide.shapes.add_shape(
    MSO_SHAPE.OVAL,
    Inches(1), Inches(3),
    Inches(2), Inches(2)
)
shape3.text = "椭圆"

# 添加箭头
shape4 = slide.shapes.add_shape(
    MSO_SHAPE.RIGHT_ARROW,
    Inches(4), Inches(3),
    Inches(2), Inches(1)
)

# 常用形状类型
# MSO_SHAPE.RECTANGLE - 矩形
# MSO_SHAPE.ROUNDED_RECTANGLE - 圆角矩形
# MSO_SHAPE.OVAL - 椭圆
# MSO_SHAPE.TRIANGLE - 三角形
# MSO_SHAPE.RIGHT_ARROW - 右箭头
# MSO_SHAPE.CHEVRON - V形
# MSO_SHAPE.PENTAGON - 五边形
# MSO_SHAPE.HEXAGON - 六边形
# MSO_SHAPE.STAR_5_POINT - 五角星

# 添加线条
line_shape = slide.shapes.add_connector(
    1,  # 直线连接器
    Inches(1), Inches(6),  # 起点
    Inches(5), Inches(6)   # 终点
)

# 设置形状文本格式
tf = shape.text_frame
tf.paragraphs[0].alignment = PP_ALIGN.CENTER
for run in tf.paragraphs[0].runs:
    run.font.color.rgb = RGBColor(255, 255, 255)
    run.font.bold = True

prs.save('shapes.pptx')

6. 图片操作

from pptx import Presentation
from pptx.util import Inches

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])

# 添加图片
img_path = 'image.png'
left = Inches(1)
top = Inches(1)

# 方式1:自动大小
# pic = slide.shapes.add_picture(img_path, left, top)

# 方式2:指定宽度(保持比例)
pic = slide.shapes.add_picture(img_path, left, top, width=Inches(4))

# 方式3:指定宽高
# pic = slide.shapes.add_picture(img_path, left, top, 
#                                width=Inches(4), height=Inches(3))

# 获取图片尺寸
print(f"宽度:{pic.width.inches} 英寸")
print(f"高度:{pic.height.inches} 英寸")

# 调整图片位置
pic.left = Inches(2)
pic.top = Inches(2)

# 从字节流添加图片
from io import BytesIO

# with open('image.png', 'rb') as f:
#     image_stream = BytesIO(f.read())
# slide.shapes.add_picture(image_stream, Inches(1), Inches(1))

# 添加背景图片(填充整个幻灯片)
def add_background_image(slide, img_path):
    """添加背景图片"""
    left = top = 0
    width = slide.shapes._spTree.getparent().getparent().getparent().sldSz.attrib['cx']
    height = slide.shapes._spTree.getparent().getparent().getparent().sldSz.attrib['cy']
    
    pic = slide.shapes.add_picture(
        img_path, 
        left, top,
        int(width), int(height)
    )
    # 将图片移到最底层
    spTree = slide.shapes._spTree
    spTree.insert(2, pic._element)

prs.save('images.pptx')

7. 表格操作

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])

# 创建表格
rows, cols = 4, 4
left = Inches(1)
top = Inches(1.5)
width = Inches(8)
height = Inches(3)

table = slide.shapes.add_table(rows, cols, left, top, width, height).table

# 设置列宽
table.columns[0].width = Inches(2)
table.columns[1].width = Inches(2)
table.columns[2].width = Inches(2)
table.columns[3].width = Inches(2)

# 填充数据
data = [
    ['姓名', '部门', '销量', '金额'],
    ['张三', '技术部', '100', '10000'],
    ['李四', '销售部', '150', '15000'],
    ['王五', '财务部', '80', '8000'],
]

for i, row_data in enumerate(data):
    for j, cell_data in enumerate(row_data):
        cell = table.cell(i, j)
        cell.text = cell_data
        
        # 设置单元格文本格式
        paragraph = cell.text_frame.paragraphs[0]
        paragraph.alignment = PP_ALIGN.CENTER
        paragraph.font.size = Pt(12)
        
        # 表头样式
        if i == 0:
            paragraph.font.bold = True
            paragraph.font.color.rgb = RGBColor(255, 255, 255)
            cell.fill.solid()
            cell.fill.fore_color.rgb = RGBColor(68, 114, 196)

# 合并单元格
# table.cell(0, 0).merge(table.cell(0, 1))

# 设置边框(需要操作XML)
def set_cell_border(cell, border_color=RGBColor(0, 0, 0), border_width=Pt(1)):
    """设置单元格边框"""
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    
    for edge in ['a:lnL', 'a:lnR', 'a:lnT', 'a:lnB']:
        ln = tcPr.find('.//' + edge.replace(':', '/'))
        if ln is None:
            from pptx.oxml.ns import nsmap
            from lxml import etree
            ln = etree.SubElement(tcPr, '{%s}%s' % (nsmap['a'], edge.split(':')[1]))
        ln.set('w', str(border_width))

prs.save('tables.pptx')

8. 图表操作

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.chart.data import CategoryChartData, ChartData
from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])

# 准备数据
chart_data = CategoryChartData()
chart_data.categories = ['1月', '2月', '3月', '4月']
chart_data.add_series('销量', (100, 150, 130, 180))
chart_data.add_series('目标', (120, 120, 150, 150))

# 添加柱状图
x, y, cx, cy = Inches(1), Inches(1.5), Inches(8), Inches(4.5)
chart = slide.shapes.add_chart(
    XL_CHART_TYPE.COLUMN_CLUSTERED,  # 簇状柱形图
    x, y, cx, cy,
    chart_data
).chart

# 设置图表标题
chart.has_title = True
chart.chart_title.text_frame.text = "月度销售数据"

# 设置图例
chart.has_legend = True
chart.legend.position = XL_LEGEND_POSITION.BOTTOM
chart.legend.include_in_layout = False

# 添加折线图
slide2 = prs.slides.add_slide(prs.slide_layouts[6])

line_chart = slide2.shapes.add_chart(
    XL_CHART_TYPE.LINE,
    Inches(1), Inches(1.5), Inches(8), Inches(4.5),
    chart_data
).chart

line_chart.has_title = True
line_chart.chart_title.text_frame.text = "销售趋势"

# 添加饼图
slide3 = prs.slides.add_slide(prs.slide_layouts[6])

pie_data = CategoryChartData()
pie_data.categories = ['产品A', '产品B', '产品C', '产品D']
pie_data.add_series('占比', (30, 25, 20, 25))

pie_chart = slide3.shapes.add_chart(
    XL_CHART_TYPE.PIE,
    Inches(2), Inches(1.5), Inches(6), Inches(5),
    pie_data
).chart

pie_chart.has_title = True
pie_chart.chart_title.text_frame.text = "产品占比"

# 常用图表类型
# XL_CHART_TYPE.COLUMN_CLUSTERED - 簇状柱形图
# XL_CHART_TYPE.COLUMN_STACKED - 堆积柱形图
# XL_CHART_TYPE.BAR_CLUSTERED - 簇状条形图
# XL_CHART_TYPE.LINE - 折线图
# XL_CHART_TYPE.LINE_MARKERS - 带标记的折线图
# XL_CHART_TYPE.PIE - 饼图
# XL_CHART_TYPE.DOUGHNUT - 圆环图
# XL_CHART_TYPE.AREA - 面积图

prs.save('charts.pptx')

9. 读取PPT

from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE_TYPE

prs = Presentation('presentation.pptx')

# 遍历幻灯片
for i, slide in enumerate(prs.slides):
    print(f"\n=== 幻灯片 {i+1} ===")
    
    # 遍历形状
    for shape in slide.shapes:
        print(f"形状类型:{shape.shape_type}")
        
        # 文本框
        if shape.has_text_frame:
            for para in shape.text_frame.paragraphs:
                print(f"文本:{para.text}")
        
        # 表格
        if shape.has_table:
            table = shape.table
            for row in table.rows:
                row_text = [cell.text for cell in row.cells]
                print(f"表格行:{row_text}")
        
        # 图片
        if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
            print(f"图片:{shape.name}")

# 提取所有文本
def extract_text(prs):
    """提取PPT中的所有文本"""
    text_content = []
    for slide in prs.slides:
        slide_text = []
        for shape in slide.shapes:
            if shape.has_text_frame:
                for para in shape.text_frame.paragraphs:
                    if para.text.strip():
                        slide_text.append(para.text)
        text_content.append('\n'.join(slide_text))
    return text_content

all_text = extract_text(prs)
for i, text in enumerate(all_text):
    print(f"幻灯片{i+1}\n{text}\n")

10. 实战案例

案例:自动生成数据报告PPT

"""
实战案例:自动生成数据报告PPT
"""
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION
from datetime import datetime

def create_report_ppt(data, output_file):
    """
    创建数据报告PPT
    
    Args:
        data: 报告数据
        output_file: 输出文件
    """
    prs = Presentation()
    prs.slide_width = Inches(13.333)
    prs.slide_height = Inches(7.5)
    
    # === 封面 ===
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    
    # 标题
    title_box = slide.shapes.add_textbox(
        Inches(1), Inches(2.5), Inches(11.333), Inches(1.5)
    )
    tf = title_box.text_frame
    p = tf.paragraphs[0]
    p.text = data['title']
    p.font.size = Pt(44)
    p.font.bold = True
    p.font.color.rgb = RGBColor(0, 51, 102)
    p.alignment = PP_ALIGN.CENTER
    
    # 副标题
    subtitle_box = slide.shapes.add_textbox(
        Inches(1), Inches(4.5), Inches(11.333), Inches(1)
    )
    tf = subtitle_box.text_frame
    p = tf.paragraphs[0]
    p.text = f"{data['period']} | {data['author']}"
    p.font.size = Pt(24)
    p.font.color.rgb = RGBColor(102, 102, 102)
    p.alignment = PP_ALIGN.CENTER
    
    # === 概述页 ===
    slide2 = prs.slides.add_slide(prs.slide_layouts[6])
    add_slide_title(slide2, "报告概述")
    
    # 关键指标
    metrics = data['metrics']
    for i, (name, value, change) in enumerate(metrics):
        x = Inches(1 + i * 3)
        add_metric_card(slide2, x, Inches(2), name, value, change)
    
    # 概述文本
    summary_box = slide2.shapes.add_textbox(
        Inches(1), Inches(4.5), Inches(11.333), Inches(2)
    )
    tf = summary_box.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = data['summary']
    p.font.size = Pt(16)
    
    # === 数据图表页 ===
    slide3 = prs.slides.add_slide(prs.slide_layouts[6])
    add_slide_title(slide3, "销售趋势")
    
    # 添加图表
    chart_data = CategoryChartData()
    chart_data.categories = data['chart_data']['categories']
    for series_name, series_data in data['chart_data']['series'].items():
        chart_data.add_series(series_name, series_data)
    
    chart = slide3.shapes.add_chart(
        XL_CHART_TYPE.LINE_MARKERS,
        Inches(1), Inches(1.8), Inches(11.333), Inches(5),
        chart_data
    ).chart
    
    chart.has_legend = True
    chart.legend.position = XL_LEGEND_POSITION.BOTTOM
    
    # === 数据表格页 ===
    slide4 = prs.slides.add_slide(prs.slide_layouts[6])
    add_slide_title(slide4, "详细数据")
    
    # 添加表格
    table_data = data['table_data']
    rows = len(table_data)
    cols = len(table_data[0])
    
    table = slide4.shapes.add_table(
        rows, cols,
        Inches(1), Inches(1.8),
        Inches(11.333), Inches(0.5 * rows)
    ).table
    
    for i, row_data in enumerate(table_data):
        for j, cell_data in enumerate(row_data):
            cell = table.cell(i, j)
            cell.text = str(cell_data)
            p = cell.text_frame.paragraphs[0]
            p.font.size = Pt(12)
            p.alignment = PP_ALIGN.CENTER
            
            if i == 0:  # 表头
                p.font.bold = True
                p.font.color.rgb = RGBColor(255, 255, 255)
                cell.fill.solid()
                cell.fill.fore_color.rgb = RGBColor(68, 114, 196)
    
    # === 总结页 ===
    slide5 = prs.slides.add_slide(prs.slide_layouts[6])
    add_slide_title(slide5, "总结与建议")
    
    conclusions_box = slide5.shapes.add_textbox(
        Inches(1), Inches(1.8), Inches(11.333), Inches(5)
    )
    tf = conclusions_box.text_frame
    tf.word_wrap = True
    
    for i, conclusion in enumerate(data['conclusions']):
        if i == 0:
            p = tf.paragraphs[0]
        else:
            p = tf.add_paragraph()
        p.text = f"• {conclusion}"
        p.font.size = Pt(18)
        p.space_after = Pt(12)
    
    # 保存
    prs.save(output_file)
    print(f"报告已生成:{output_file}")

def add_slide_title(slide, title_text):
    """添加幻灯片标题"""
    title_box = slide.shapes.add_textbox(
        Inches(0.5), Inches(0.3), Inches(12.333), Inches(0.8)
    )
    tf = title_box.text_frame
    p = tf.paragraphs[0]
    p.text = title_text
    p.font.size = Pt(32)
    p.font.bold = True
    p.font.color.rgb = RGBColor(0, 51, 102)

def add_metric_card(slide, left, top, name, value, change):
    """添加指标卡片"""
    # 背景框
    shape = slide.shapes.add_shape(
        1,  # 矩形
        left, top, Inches(2.5), Inches(2)
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = RGBColor(240, 240, 240)
    shape.line.fill.background()
    
    # 指标名称
    name_box = slide.shapes.add_textbox(left, top + Inches(0.2), Inches(2.5), Inches(0.5))
    p = name_box.text_frame.paragraphs[0]
    p.text = name
    p.font.size = Pt(14)
    p.font.color.rgb = RGBColor(102, 102, 102)
    p.alignment = PP_ALIGN.CENTER
    
    # 指标值
    value_box = slide.shapes.add_textbox(left, top + Inches(0.7), Inches(2.5), Inches(0.8))
    p = value_box.text_frame.paragraphs[0]
    p.text = value
    p.font.size = Pt(28)
    p.font.bold = True
    p.font.color.rgb = RGBColor(0, 51, 102)
    p.alignment = PP_ALIGN.CENTER
    
    # 变化率
    change_box = slide.shapes.add_textbox(left, top + Inches(1.5), Inches(2.5), Inches(0.4))
    p = change_box.text_frame.paragraphs[0]
    p.text = change
    p.font.size = Pt(12)
    p.font.color.rgb = RGBColor(0, 176, 80) if '+' in change else RGBColor(255, 0, 0)
    p.alignment = PP_ALIGN.CENTER

# 使用示例
if __name__ == '__main__':
    report_data = {
        'title': '2024年度销售报告',
        'period': '2024年1-12月',
        'author': '销售部',
        'summary': '本年度销售业绩整体表现良好,总销售额达到1.2亿元,同比增长15%。'
                   '其中Q4表现最为突出,贡献了全年35%的销售额。',
        'metrics': [
            ('总销售额', '1.2亿', '+15%'),
            ('订单数', '12,580', '+8%'),
            ('客户数', '3,256', '+12%'),
            ('客单价', '9,538', '+6%'),
        ],
        'chart_data': {
            'categories': ['1月', '2月', '3月', '4月', '5月', '6月',
                          '7月', '8月', '9月', '10月', '11月', '12月'],
            'series': {
                '2024年': [800, 850, 920, 880, 950, 1000, 1050, 1100, 1150, 1200, 1300, 1400],
                '2023年': [700, 720, 780, 800, 850, 880, 900, 950, 980, 1000, 1050, 1100],
            }
        },
        'table_data': [
            ['产品', 'Q1', 'Q2', 'Q3', 'Q4', '合计'],
            ['产品A', '2000', '2500', '2800', '3200', '10500'],
            ['产品B', '1500', '1800', '2000', '2500', '7800'],
            ['产品C', '1000', '1200', '1500', '1800', '5500'],
        ],
        'conclusions': [
            '销售额同比增长15%,超额完成年度目标',
            '产品A仍是主力产品,贡献45%的销售额',
            'Q4销售旺季表现突出,建议加大备货',
            '新客户增长12%,客户维护工作成效显著',
            '建议2025年重点拓展产品C的市场份额',
        ]
    }
    
    create_report_ppt(report_data, '销售报告.pptx')

11. 总结

🔑 核心要点

知识点要点
创建PPTPresentation(), add_slide()
文本add_textbox(), text_frame, add_paragraph()
形状add_shape(), 填充、边框设置
图片add_picture(), 指定位置和大小
表格add_table(), 单元格操作
图表add_chart(), CategoryChartData

✅ 学习检查清单

  • 能创建PPT并添加幻灯片
  • 能添加和格式化文本
  • 能添加形状和图片
  • 能创建表格
  • 能添加图表

📖 下一步学习

掌握了PPT操作后,让我们学习邮件自动化:


常见问题 FAQ

💬 python-pptx能保留原PPT的动画效果吗?

不能。python-pptx不支持动画、过渡效果和视频嵌入。如果需要保留这些,只修改文本和数据内容,不要重新创建幻灯片。

💬 怎么用现有模板生成PPT?

Presentation("template.pptx")加载模板,然后在模板基础上添加或修改幻灯片。模板中的母版和布局会被保留。


系列导航

End of file.