Files
k3GPT/main/test/make_ppt.py
2025-11-19 19:42:45 +08:00

518 lines
18 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.enum.text import MSO_ANCHOR # 导入正确的垂直对齐枚举
from pptx.enum.shapes import MSO_SHAPE
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.table import Table
def apply_gradient_background(slide):
"""应用统一的渐变背景"""
background = slide.background
fill = background.fill
fill.gradient()
fill.gradient_stops[0].color.rgb = RGBColor(0, 32, 96) # 深蓝
fill.gradient_stops[1].color.rgb = RGBColor(0, 112, 192) # 蓝色
def create_content_slide(prs, title_text, subtitle_text=None, content_items=None):
"""创建带二级标题的内容页"""
slide_layout = prs.slide_layouts[6] # 空白布局
slide = prs.slides.add_slide(slide_layout)
apply_gradient_background(slide)
# 添加主标题
left = Cm(1.5)
top = Cm(1.5)
width = Cm(10)
height = Cm(2)
title_box = slide.shapes.add_textbox(left, top, width, height)
tf = title_box.text_frame
tf.clear()
p = tf.add_paragraph()
p.text = title_text
p.font.size = Pt(36)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
# 添加二级标题(如果有)
if subtitle_text:
left = Cm(1.5)
top = Cm(3)
width = Cm(10)
height = Cm(1.5)
subtitle_box = slide.shapes.add_textbox(left, top, width, height)
tf = subtitle_box.text_frame
tf.clear()
p = tf.add_paragraph()
p.text = subtitle_text
p.font.size = Pt(24)
p.font.color.rgb = RGBColor(200, 230, 255) # 浅蓝色
p.font.italic = True
# 添加内容区域
if content_items:
# 添加内容文字
left_text = Cm(2)
top_text = Cm(5.5 if subtitle_text else 4.5)
width_text = Cm(27.5)
height_text = Cm(8)
text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
tf = text_box.text_frame
tf.clear()
for item in content_items:
p = tf.add_paragraph()
p.text = item["text"]
p.font.size = Pt(item.get("size", 18))
p.font.color.rgb = RGBColor(255, 255, 255)
p.space_after = Pt(item.get("space_after", 8))
p.level = item.get("level", 0)
if item.get("bold", False):
p.font.bold = True
if item.get("bullet", False):
p.text = "" + p.text
return slide
def create_table_slide(prs, title_text, subtitle_text, headers, data):
"""创建带表格的数据页"""
slide = create_content_slide(prs, title_text, subtitle_text)
# 计算表格位置和大小
left = Cm(2)
top = Cm(5.5 if subtitle_text else 4.5)
width = Cm(26.5)
height = Cm(8)
# 创建表格 (行数=数据行数+1列数=标题数)
rows = len(data) + 1
cols = len(headers)
table = slide.shapes.add_table(rows, cols, left, top, width, height).table
# 设置表格样式
table.first_row = True # 强调第一行
table.horz_banding = True # 横向条纹
# 设置表头
for col_idx, header in enumerate(headers):
cell = table.cell(0, col_idx)
cell.text = header
cell.fill.solid()
cell.fill.fore_color.rgb = RGBColor(0, 64, 128) # 深蓝色表头
cell.text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255)
cell.text_frame.paragraphs[0].font.bold = True
cell.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
# 填充表格数据
for row_idx, row_data in enumerate(data, start=1):
for col_idx, cell_data in enumerate(row_data):
cell = table.cell(row_idx, col_idx)
cell.text = str(cell_data)
cell.fill.solid()
cell.fill.fore_color.rgb = RGBColor(255, 255, 255)
cell.fill.fore_color.alpha = 0.2 # 半透明白色
cell.text_frame.paragraphs[0].font.color.rgb = RGBColor(0, 0, 0) # 黑色文字
cell.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
# 设置表格边框
for cell in table.iter_cells():
cell.margin_left = Cm(0.1)
cell.margin_right = Cm(0.1)
cell.margin_top = Cm(0.05)
cell.margin_bottom = Cm(0.05)
cell.vertical_anchor = MSO_ANCHOR.MIDDLE
return slide
def create_chart_slide(prs, title_text, subtitle_text, categories, data):
"""创建图表的数据页"""
slide = create_content_slide(prs, title_text, subtitle_text)
#半透明白色背景
left_content = Cm(1.5)
top_content = Cm(5 if subtitle_text else 4) # 根据是否有二级标题调整位置
width_content = Cm(28.5)
height_content = Cm(9)
content_box = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, left_content, top_content, width_content, height_content
)
content_box.fill.solid()
content_box.fill.fore_color.rgb = RGBColor(255, 255, 255)
content_box.fill.fore_color.alpha = 0.2 # 20%透明度
content_box.line.color.rgb = RGBColor(200, 230, 255)
# 添加图表数据
chart_data = ChartData()
chart_data.categories = categories
for lable_data in data:
chart_data.add_series(lable_data[0], lable_data[1])
# 添加图表
left_chart = Cm(3)
top_chart = Cm(6)
width_chart = Cm(25)
height_chart = Cm(7)
chart = slide.shapes.add_chart(
XL_CHART_TYPE.LINE, left_chart, top_chart, width_chart, height_chart, chart_data
)
# 设置图表颜色以匹配主题
chart.chart.plots[0].series[0].format.fill.solid()
chart.chart.plots[0].series[0].format.fill.fore_color.rgb = RGBColor(100, 180, 255)
chart.chart.plots[0].series[1].format.fill.solid()
chart.chart.plots[0].series[1].format.fill.fore_color.rgb = RGBColor(200, 230, 255)
return slide
def create_image_layout_slide(prs, title_text, subtitle_text, images):
"""创建图片布局页面"""
slide = create_content_slide(prs, title_text, subtitle_text)
# 清除默认内容区域
for shape in slide.shapes:
if shape.shape_type == MSO_SHAPE.ROUNDED_RECTANGLE:
sp = shape._element
sp.getparent().remove(sp)
# 根据图片数量决定布局
if len(images) == 1:
# 单张大图布局
left = Cm(3)
top = Cm(5 if subtitle_text else 4)
width = Cm(25)
height = Cm(12)
pic = slide.shapes.add_picture(images[0]["path"], left, top, width, height)
# 添加图片说明
if "caption" in images[0]:
left_cap = Cm(3)
top_cap = Cm(17 if subtitle_text else 16)
width_cap = Cm(25)
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
p.text = images[0]["caption"]
p.font.size = Pt(14)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
elif len(images) == 2:
# 两张图片并排布局
# 第一张图片
left1 = Cm(2)
top1 = Cm(5 if subtitle_text else 4)
width1 = Cm(13)
height1 = Cm(12)
pic1 = slide.shapes.add_picture(images[0]["path"], left1, top1, width1, height1)
# 第二张图片
left2 = Cm(16)
top2 = Cm(5 if subtitle_text else 4)
width2 = Cm(13)
height2 = Cm(12)
pic2 = slide.shapes.add_picture(images[1]["path"], left2, top2, width2, height2)
# 添加图片说明
for i, img in enumerate(images):
if "caption" in img:
left_cap = Cm(2 if i == 0 else 16)
top_cap = Cm(17 if subtitle_text else 16)
width_cap = Cm(13)
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
p.text = img["caption"]
p.font.size = Pt(12)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
elif len(images) >= 3:
# 三张图片网格布局
img_size = Cm(8)
gap = Cm(1)
# 第一行
for i in range(min(3, len(images))):
left = Cm(3) + (img_size + gap) * i
top = Cm(5 if subtitle_text else 4)
pic = slide.shapes.add_picture(images[i]["path"], left, top, img_size, img_size)
if "caption" in images[i]:
left_cap = left
top_cap = top + img_size + Cm(0.5)
width_cap = img_size
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
p.text = images[i]["caption"]
p.font.size = Pt(10)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
# 第二行如果有4张以上图片
img_size = Cm(4)
gap = Cm(1)
for i in range(3, len(images)):
left = Cm(3) + (img_size + gap) * (i - 3)
top = Cm(16 if subtitle_text else 15)
pic = slide.shapes.add_picture(images[i]["path"], left, top, img_size, img_size)
if "caption" in images[i]:
left_cap = left
top_cap = top + img_size + Cm(0.5)
width_cap = img_size
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
p.text = images[i]["caption"]
p.font.size = Pt(10)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
return slide
def create_unified_ppt(output_filename):
# 创建一个16:9宽屏演示文稿对象
prs = Presentation()
#prs.slide_width = Inches(13.333) # 16:9的宽度
#prs.slide_height = Inches(7.5) # 16:9的高度
prs.slide_width = Inches(16) # 16:9的宽度
prs.slide_height = Inches(9) # 16:9的高度
# ===== 封面幻灯片 =====
slide_layout = prs.slide_layouts[6] # 空白布局
slide = prs.slides.add_slide(slide_layout)
apply_gradient_background(slide)
# 添加宽屏标题
left = Cm(1.5)
top = Cm(4)
width = Cm(30)
height = Cm(4)
title_box = slide.shapes.add_textbox(left, top, width, height)
tf = title_box.text_frame
p = tf.add_paragraph()
p.text = "专业商务演示"
p.font.size = Pt(48)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
p.alignment = PP_ALIGN.LEFT
# 添加副标题
left = Cm(1.5)
top = Cm(8)
width = Cm(20)
height = Cm(2)
subtitle_box = slide.shapes.add_textbox(left, top, width, height)
tf = subtitle_box.text_frame
p = tf.add_paragraph()
p.text = "包含表格和图片布局的演示文稿"
p.font.size = Pt(20)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.LEFT
# ===== 目录幻灯片 =====
create_content_slide(
prs,
"内容目录",
content_items=[
{"text": "1. 项目概述", "size": 24, "space_after": 12},
{"text": "2. 市场分析", "size": 24, "space_after": 12},
{"text": "3. 财务数据", "size": 24, "space_after": 12},
{"text": "4. 产品展示", "size": 24, "space_after": 12},
{"text": "5. 技术架构", "size": 24, "space_after": 12}
]
)
# ===== 一级标题内容页 =====
create_content_slide(
prs,
"项目概述",
content_items=[
{"text": "项目背景", "size": 22, "bold": True, "space_after": 8},
{"text": "随着数字化转型加速,企业需要更高效的解决方案来应对市场变化。", "bullet": True},
{"text": "本项目旨在开发一套智能化管理系统,提升企业运营效率。", "bullet": True},
{"text": "项目目标", "size": 22, "bold": True, "space_after": 8, "space_before": 12},
{"text": "构建可扩展的技术架构支持未来5年业务增长", "bullet": True},
{"text": "实现关键业务流程自动化,减少人工干预", "bullet": True},
{"text": "提供数据分析和决策支持功能", "bullet": True}
]
)
# ===== 带二级标题的内容页 =====
create_content_slide(
prs,
"技术架构",
"核心技术组件", # 二级标题
content_items=[
{"text": "前端技术", "size": 22, "bold": True, "space_after": 8},
{"text": "采用React框架构建响应式用户界面", "bullet": True},
{"text": "使用TypeScript提高代码质量", "bullet": True},
{"text": "后端技术", "size": 22, "bold": True, "space_after": 8, "space_before": 12},
{"text": "基于Spring Boot的微服务架构", "bullet": True},
{"text": "使用Kubernetes进行容器编排", "bullet": True},
{"text": "数据库技术", "size": 22, "bold": True, "space_after": 8, "space_before": 12},
{"text": "主数据库: PostgreSQL 14", "bullet": True},
{"text": "缓存层: Redis集群", "bullet": True},
{"text": "数据分析: Elasticsearch", "bullet": True}
]
)
# ===== 表格数据页面 =====
headers = ["季度", "营收(万元)", "同比增长", "利润率", "市场份额"]
data = [
["Q1 2023", 1250, "12.5%", "18.2%", "22.4%"],
["Q2 2023", 1430, "15.8%", "19.1%", "23.7%"],
["Q3 2023", 1580, "18.2%", "20.3%", "25.1%"],
["Q4 2023", 1720, "20.1%", "21.5%", "26.8%"],
["Q1 2024", 1850, "22.7%", "22.8%", "28.3%"]
]
create_table_slide(
prs,
"财务数据",
"2023-2024季度表现",
headers,
data
)
# ===== 图片布局页面(单张大图)=====
create_image_layout_slide(
prs,
"产品展示",
"旗舰产品全景",
[{
"path": "product.jpg", # 替换为实际图片路径
"caption": "图1: 公司旗舰产品XYZ系列"
}]
)
# ===== 图片布局页面(两张并排)=====
create_image_layout_slide(
prs,
"技术对比",
"新旧技术方案比较",
[
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "图2: 传统技术方案"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "图3: 创新技术方案"
}
]
)
# ===== 图片布局页面(三栏)=====
create_image_layout_slide(
prs,
"应用场景",
"多领域解决方案",
[
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "工业制造"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "金融服务"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "医疗健康"
},
]
)
# ===== 图片布局页面(多栏)=====
create_image_layout_slide(
prs,
"应用场景",
"多领域解决方案",
[
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "工业制造"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "金融服务"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "医疗健康"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "医疗健康"
},
{
"path": "product.jpg", # 替换为实际图片路径
"caption": "医疗健康"
},
]
)
# ===== 图表幻灯片 =====
slide = create_chart_slide(
prs,
"市场分析",
"近五年增长趋势", # 二级标题
categories = ['2019', '2020', '2021', '2022', '2023'],
data = [
['市场规模(亿元)', [45, 52, 61, 78, 92]],
['年增长率(%)', [8.2, 15.6, 17.3, 27.9, 17.9]],
]
)
# ===== 结束幻灯片 =====
slide = create_content_slide(
prs,
"感谢聆听",
"期待与您合作"
)
# 在内容区域添加联系信息
left = Cm(12)
top = Cm(12)
width = Cm(16)
height = Cm(3)
contact_box = slide.shapes.add_textbox(left, top, width, height)
tf = contact_box.text_frame
p = tf.add_paragraph()
p.text = "联系方式"
p.font.size = Pt(24)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER
p.space_after = Pt(16)
p = tf.add_paragraph()
p.text = "邮箱: contact@example.com\n电话: 123-456-7890\n网址: www.example.com"
p.font.size = Pt(18)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
p.space_before = Pt(8)
# 保存演示文稿
prs.save(output_filename)
if __name__ == "__main__":
# 使用前请替换示例图片路径为实际图片路径
create_unified_ppt("professional_presentation_with_tables_and_images.pptx")
print("带表格和图片布局的PPT生成完成!")