存在这样一个需求,需要使用数据渲染生成一个PPT报告。经过一番查询,笔者决定使用python-pptx来完成这个需求。python-pptx作为一个功能强大的第三方库,完全可以满足笔者的需求。
python-pptx是一个强大的python类库,可以操作PPT的各种元素(表格、图片、统计图表、表格等
)从无到有生成PPT。python-pptx中的方法大都是add开头的,意味着可以很容易新增各种PPT元素
。于是,笔者决定按照文档给出的用法,生成各种PPT元素,最终生成一个完整的PPT。然而,笔者很快就遇到了问题:PPT的布局十分复杂和繁琐,笔者需要确定模板中每个元素的位置、大小、颜色等属性,然后通过python-pptx提供的接口来生成这些元素
。这样子也太过繁琐了。
python-pptx的文档:https://python-pptx.readthedocs.io/en/latest
那么我们该怎么做呢?最直接的想法就是,只修改一下需要渲染的文本数据,而不是从头生成元素。为了实现这样的想法,还需要解决一些问题:
第一个问题很简单,我们可以为PPT中的元素设置特殊的标记id,比如图表就有标题元素,我们可以将标题的文本设置为{chart}
,然后设置一个字典将这个id和需要填充的数据匹配即可。在修改图表数据时,搜索页面中所有元素的标题文本,根据这个id就可以修改图表的数据。
代码如下(示例):
from pptx import Presentation
prs = Presentation(path_to_presentation)
for slide in prs.slides:
for shape in slide.shapes:
if not shape.has_text_frame:
continue
# 方法一
# shape.text_frame.text = "update"
# 方法二
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs:
run.text = "update"
上述代码中存在两种方法可以将文本框的元素改为update,二者的不同之处在于,第一种方法改动后,模板的字体格式以及颜色都会消失,不符合我们的要求。第二种方法则可以在保留模板字体的格式
。
生成一个直方图代码如下(示例):
from pptx import Presentation
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Inches
# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
# define chart data ---------------------
chart_data = CategoryChartData()
chart_data.categories = ['East', 'West', 'Midwest']
chart_data.add_series('Series 1', (19.2, 21.4, 16.7))
# add chart to slide --------------------
x, y, cx, cy = Inches(2), Inches(2), Inches(6), Inches(4.5)
slide.shapes.add_chart(
XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, chart_data
)
prs.save('chart-01.pptx')
修改刚才生成的直方图的数据。
from pptx import Presentation
from pptx.chart.data import CategoryChartData
prs = Presentation("chart-01.pptx")
# define chart data ---------------------
chart_data = CategoryChartData()
chart_data.categories = ['A', 'B', 'C']
chart_data.add_series('Series 1', (2, 4, 6))
for slide in prs.slides:
for shape in slide.shapes:
if not shape.has_chart:
continue
shape.chart.replace_data(chart_data)
prs.save('chart-02.pptx')
上述代码的核心就是replace_data
方法,需要注意的是,不同的图表类型其char_data
的类型(CategoryChartData、XyChartData
)也不相同,修改数据的时候需要注意这个问题,不能用折线图的数据替换直方图中的数据。
代码如下(示例):
presentation = Presentation("your_presentation.pptx")
slide = presentation.slides[0]
for shape in slide.shapes:
# 方法一:如果组合图形中元素存在文本以{开头的的元素,那么删除这个组合图形元素
# MSO_SHAPE_TYPE.GROUP 表示组合图形
if shape.shape_type == MSO_SHAPE_TYPE.GROUP: # 获取组合图形中的元素
for element in shape.shapes:
if element.has_text_frame and element.text_frame.text.startswith("{"):
slide.shapes._spTree.remove(shape._element)
break
# 方法二:删除PPT页面中文本{开头的的元素
if shape.has_text_frame and shape.text_frame.text.startswith("{"):
slide.shapes._spTree.remove(shape._element)
break
代码如下(示例):
class PresentationBuilder(object):
presentation = Presentation("your_presentation.pptx")
@property
def xml_slides(self):
return self.presentation.slides._sldIdLst # pylint: disable=protected-access
def move_slide(self, old_index, new_index):
# 将PPT页面移动位置,结合python-pptx只提供了PPT末尾添加页面的功能,即可实现生成新的页面然后插入指定位置的功能
slides = list(self.xml_slides)
self.xml_slides.remove(slides[old_index])
self.xml_slides.insert(new_index, slides[old_index]) # also works for deleting slides
def delete_slide(self, index):
# index指的是PPT页面在整个PPT中位置,比如第一页PPT的index就是0
slides = list(self.xml_slides)
self.xml_slides.remove(slides[index])
python-pptx中没有提供删除PPT页面的方法,原因是删除页面的操作比较复杂,容易出错。上述的代码也只是不让这个页面在PPT中的展示,而非真正删除了这一页PPT。当然,这个已经足够满足我的需要了。
以上就是今天要讲的内容,本文给出了生成PPT的一个解决方案,可以通过替换元素数据内容的方式生成一个PPT。