Python-pptx教程之二操作已有PPT模板文件

文章目录

  • 简单的案例
    • 找到要修改的元素
    • 修改幻灯片中的文本
      • 代码
      • 使用示例
      • 修改幻灯片的图片
        • 代码
        • 使用示例
        • 删除幻灯片
          • 代码
          • 使用示例
          • 获取PPT中所有的文本内容
          • 获取PPT中所有的图片
          • 总结

            在上一篇中我们已经学会了如何从零开始生成PPT文件,从零开始生成较为复杂的PPT是非常消耗精力的一件事,各种shape位置的摆放坐标填写过于繁琐,而且很多样式诸如添加入场动画、特殊字体指定等功能,原有的python-pptx框架并不支持

            所以这一篇来了解如何修改一个已经设计好的PPT模板文件

            简单的案例

            我们先通过一个简单的案例来讲解基本的PPT操作

            这里已经设计好了一张奖状样式的PPT模板,只需要修改特定的文字,这种重复劳动交给python-pptx就好

            PPT模板如下

            修改单张幻灯片

            prs = Presentation('G:/simple_ppt/奖状模板.pptx')
            slide_index = 0
            slide = prs.slides[slide_index]
            for shape in slide.shapes:
                print("shape=", shape.name)
                if shape.name == 'student_name':
                    shape.text = '孙悟空'
                if shape.name == 'student_school':
                    shape.text = '花果山水帘洞'
                if shape.name == 'cert_date':
                    current_date = datetime.now()
                    date_string = current_date.strftime("%Y年%m月%d日")
                    shape.text = date_string
            save_ppt = "G:/simple_ppt/test/blog_test_template.pptx"
            prs.save(save_ppt)
            

            执行后的效果

            可以发现原来的占位内容已经被替换为我们指定的文本内容了

            找到要修改的元素

            要修改幻灯片中的内容,那么首先就需要找到对应的shape控件,大多数方案是根据匹配字符串内容来查找,但这样的方案无法满足图片、视频等的查找,还可能出现字符串冲突,所以推荐使用“选择窗格”里的ID来查找

            上面代码中的“student_name”、“student_school”、“cert_date”就是占位符,用来定位要修改内容的地方,相当于一个唯一标识

            那么如何设置shape的ID呢?

            以WPS为例,打开选择窗格的方式:点击开始 -> 选择 -> 选择窗格,如下所示

            此时就会在右侧栏目中出现选择窗格,显示当前幻灯片中所有对象元素的ID,点击对应对象ID即可进行修改

            在代码中,通过shape.name进行匹配查找,即可找到我们需要的shape

            修改幻灯片中的文本

            代码

            上面例子中是通过shape.text的方式来修改文本的,但这种方法有一个弊端,就是PPT中原有的文本框格式被擦除,所以这里推荐使用run文本段的方式修改文本

            def replace_text(shape, content):
                if not shape.has_text_frame:    # 判断是否有文本框
                    return
                tf = shape.text_frame
                for paragraph in tf.paragraphs:
                    is_first_run = True
                    for run in paragraph.runs:
                        if is_first_run:
                            run.text = content
                            is_first_run = False
                        else:
                            run.text = ''
            

            这个方法传入一个shape和文本内容,再通过has_text_frame判断shape中是否存在文本框,存在则进行更改文本操作,同时规避了有的文本框中存在多个词组run的问题,一个文本框中若存在多个词组,只需修改第一个词组即可,后续词组置空

            使用示例

            修改上例中的代码,使用replace_text方法修改文本

            prs = Presentation('G:/simple_ppt/奖状模板.pptx')
            slide_index = 0
            slide = prs.slides[slide_index]
            for shape in slide.shapes:
                print("shape=", shape.name)
                if shape.name == 'student_name':
                    replace_text(shape, '孙悟空')
                if shape.name == 'student_school':
                    replace_text(shape, '花果山水帘洞')
                if shape.name == 'cert_date':
                    current_date = datetime.now()
                    date_string = current_date.strftime("%Y年%m月%d日")
                    replace_text(shape, date_string)
            save_ppt = "G:/simple_ppt/test/blog_test_template.pptx"
            prs.save(save_ppt)
            

            生成的效果如下

            可以很明显的看到时间那一栏已经和原始的模板字体效果一模一样了

            修改幻灯片的图片

            代码

            通过以下代码可以替换幻灯片中的图片

            def replace_picture(shape, slide, slide_index, img_path):
                sp_tree = slide.shapes._spTree
                sp_tree.remove(shape._element)
                new_shape = slide.shapes.add_picture(img_path, shape.left, shape.top, shape.width, shape.height)
                sp_tree.insert(slide_index, new_shape._element)
            

            代码中通过删除原有shape中的图片,然后添加一个和原有shape大小位置一样的shape来指定图片,最后通过insert将新图片的shape元素插入到老图片shape的元素中,这样做是为了防止新添加的图片破坏层级关系,导致新添加的图片覆盖掉幻灯片中原来的元素

            使用示例

            比如我们想替换掉背景,可以先给模板中的背景图片指定ID为“slide_bg”,然后调用replace_picture方法,注意slide_index是当前要操作的幻灯片索引

            if shape.name == 'slide_bg':
                img_path = 'G:/simple_ppt/res/picture_bg.png'
                replace_picture(shape, slide, slide_index, img_path)
            

            效果如下

            删除幻灯片

            代码

            通过以下代码可以删除一张幻灯片

            def delete_slide(prs, slide_index):
                slides = list(prs.slides._sldIdLst)
                prs.slides._sldIdLst.remove(slides[slide_index])
            

            传入一个Presentation对象和指定第几张幻灯片,第一张索引从0开始

            使用示例

            prs = Presentation('G:/simple_ppt/奖状模板.pptx')
            delete_slide(prs, 0)	# 删除第一张幻灯片
            save_ppt = "G:/simple_ppt/test/blog_test_template.pptx"
            prs.save(save_ppt)
            

            注意事项:删除幻灯片之后再通过add的方式添加幻灯片会报错,因为原有的幻灯片列表总数已经改变,所以删除幻灯片的操作最好是在pptx文件中所有其它操作都做完了再进行

            获取PPT中所有的文本内容

            有时候我们想取出PPT中所有的文本内容,比如一些教学课件类的PPT,里面的内容要一个一个手动拷贝可就太麻烦了,这个也可以交给python-pptx来做

            通过以下代码,指定要读取的pptx文件路径,打印ppt中含有的所有文本

            prs = Presentation('G:/simple_ppt/test/blog_test_template.pptx')
            text_content = []
            for slide in prs.slides:
                for shape in slide.shapes:
                    if not shape.has_text_frame:
                        continue
                    for paragraph in shape.text_frame.paragraphs:
                        for run in paragraph.runs:
                            text_content.append(run.text)
            print("全部文字:", text_content)
            

            得到的结果

            全部文字: ['在2023-2024学年度第二学期期末考试中成绩优异,特发此状,以资鼓励。', '同学', ':', '学校', '2023年11月16日', '', '', '', '孙悟空', '花果山水帘洞']
            

            获取PPT中所有的图片

            通过python-pptx也可以获取PPT中全部的图片,通过与获取全部文本同样的遍历方法,找到所有图片类型的shape

            可以通过shape.shape_type来判断当前的shape是否是图片类型

            获取PPT中全部图片的代码

            from pptx.enum.shapes import MSO_SHAPE_TYPE
            prs = Presentation('G:/simple_ppt/test/blog_test_template.pptx')
            save_dir = 'G:/simple_ppt/test/images'
            for slide_no, slide in enumerate(prs.slides):
                for shape_no, shape in enumerate(slide.shapes):
                    if shape.shape_type == MSO_SHAPE_TYPE.PICTURE: # 查找图片类型
                        image = shape.image
                        image_bytes = image.blob
                        image_filename = f"{save_dir}/slide_{slide_no}_image_{shape_no}.png"
                        with open(image_filename, "wb") as img_file:
                            img_file.write(image_bytes)
            

            上面的代码中,将会把PPT中所有图片保存到save_dir目录下

            由于我们的模板文件中只有一张图片,所有获取到的也就是一张

            这里还有另一个方法,如果只是想单纯的获取一个PPT文件的图片,可以将文件的.pptx后缀改成.zip,然后解压,找到\ppt\media目录,里面就是所有的图片文件

            总结

            通过阅读本篇文章,可以掌握如何通过ID找到对应的shape控件,了解了如何正确的修改幻灯片中的文本内容和图片资源,以及操作删除幻灯片的方法,还掌握了如何方便的取出PPT文件中所有的文本内容和图片资源

            至此,通过python-pptx框架从零开始生成PPT和操作已有PPT的功能都已介绍完毕,后续文章将介绍使用python-pptx来做一些框架本身没有提供API进行支持的功能该如何实现