Add File
This commit is contained in:
517
main/test/make_ppt.py
Normal file
517
main/test/make_ppt.py
Normal file
@@ -0,0 +1,517 @@
|
||||
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生成完成!")
|
||||
Reference in New Issue
Block a user