如何使用python-pptx复制幻灯片?

15

如何复制幻灯片?

我创建了一个模板幻灯片,需要复制它并分别编辑每个副本的形状。

或者我该如何将我的模板幻灯片添加到 presentation.slide_layouts 中?


有图片的幻灯片,请参见相关文章:https://dev59.com/8rzpa4cB1Zd3GeqPFCNk - Gino Mempin
7个回答

10

这是我在GitHub上找到的,对我很有效。我为我的项目做了一些修改。您需要导入six和copy。我正在使用pptx-6.10。

def duplicate_slide(pres, index):
    template = pres.slides[index]
    try:
        blank_slide_layout = pres.slide_layouts[12]
    except:
        blank_slide_layout = pres.slide_layouts[len(pres.slide_layouts)]

    copied_slide = pres.slides.add_slide(blank_slide_layout)

    for shp in template.shapes:
        el = shp.element
        newel = copy.deepcopy(el)
        copied_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')

    for _, value in six.iteritems(template.part.rels):
        # Make sure we don't copy a notesSlide relation as that won't exist
        if "notesSlide" not in value.reltype:
            copied_slide.part.rels.add_relationship(
                value.reltype,
                value._target,
                value.rId
            )

    return copied_slide

然后,您可以通过传入您的演示文稿和模板幻灯片索引来创建副本:
copied_slide = duplicate_slide(pres, 4)

是的,抱歉回复晚了。我已经在下面发布了另一个答案。 - d_bergeron

8
我使用一个模板PPT并填充内容。在填充幻灯片之前,我知道需要复制哪些模板的幻灯片以及需要复制多少次。然后,我会复制这些幻灯片,并将其保存为新的PPT文档。保存完成后,我可以打开带有复制幻灯片的PPT文档,然后使用pptx来填充幻灯片。
import win32com.client
ppt_instance = win32com.client.Dispatch('PowerPoint.Application')
#open the powerpoint presentation headless in background
read_only = True
has_title = False
window    = False
prs = ppt_instance.Presentations.open('path/ppt.pptx',read_only,has_title,window)

nr_slide = 1
insert_index = 1
prs.Slides(nr_slide).Copy()
prs.Slides.Paste(Index=insert_index)

prs.SaveAs('path/new_ppt.pptx')
prs.Close()

#kills ppt_instance
ppt_instance.Quit()
del ppt_instance

在这种情况下,第一张幻灯片将被复制并插入到同一演示文稿的第一张幻灯片之后。

3
这是一种非常有趣的方法,感谢您将其添加到本帖。我一直在寻找一种使用python-pptx复制幻灯片的好方法,但这种方法做得更好。 - MalteHerrmann
1
非常聪明和优雅的解决方案!它像魔法一样运行。 - Ken Grimes

6

我发现了@d_bergeron分享的代码的另一个用例,我想将另一个演示文稿中的幻灯片复制到我使用python-pptx生成的演示文稿中:

作为参数,我传递了使用python-pptx创建的Presentation()对象(prs = Presentation())。

from pptx import Presentation
import copy

def copy_slide_from_external_prs(prs):

    # copy from external presentation all objects into the existing presentation
    external_pres = Presentation("PATH/TO/PRES/TO/IMPORT/from.pptx")

    # specify the slide you want to copy the contents from
    ext_slide = external_pres.slides[0]

    # Define the layout you want to use from your generated pptx
    SLD_LAYOUT = 5
    slide_layout = prs.slide_layouts[SLD_LAYOUT]

    # create now slide, to copy contents to 
    curr_slide = prs.slides.add_slide(slide_layout)

    # now copy contents from external slide, but do not copy slide properties
    # e.g. slide layouts, etc., because these would produce errors, as diplicate
    # entries might be generated

    for shp in ext_slide.shapes:
        el = shp.element
        newel = copy.deepcopy(el)
        curr_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')

    return prs

3

我编辑了@n00by0815的解决方案,得到了非常优雅的代码,还可以无误地复制图像:

# ATTENTNION: PPTX PACKAGE RUNS ONLY ON CERTAINS VERSION OF PYTHON (https://python-pptx.readthedocs.io/en/latest/user/install.html)

from pptx import Presentation
from pptx.util import Pt
from pptx.enum.text import PP_ALIGN
import copy
import os

DIR_PATH = os.path.dirname(os.path.realpath(__file__))

#modeled on https://dev59.com/OlUL5IYBdhLWcg3wFEvw#56074651 and https://dev59.com/OlUL5IYBdhLWcg3wFEvw#62921848
#this for some reason doesnt copy text properties (font size, alignment etc.)
def SlideCopyFromPasteInto(copyFromPres, slideIndex,  pasteIntoPres):

    # specify the slide you want to copy the contents from
    slide_to_copy = copyFromPres.slides[slideIndex]

    # Define the layout you want to use from your generated pptx

    slide_layout = pasteIntoPres.slide_layouts.get_by_name("Blank") # names of layouts can be found here under step 3: https://www.geeksforgeeks.org/how-to-change-slide-layout-in-ms-powerpoint/
    # it is important for slide_layout to be blank since you dont want these "Write your title here" or something like that textboxes
    # alternative: slide_layout = pasteIntoPres.slide_layouts[copyFromPres.slide_layouts.index(slide_to_copy.slide_layout)]
    
    # create now slide, to copy contents to 
    new_slide = pasteIntoPres.slides.add_slide(slide_layout)

    # create images dict
    imgDict = {}

    # now copy contents from external slide, but do not copy slide properties
    # e.g. slide layouts, etc., because these would produce errors, as diplicate
    # entries might be generated
    for shp in slide_to_copy.shapes:
        if 'Picture' in shp.name:
            # save image
            with open(shp.name+'.jpg', 'wb') as f:
                f.write(shp.image.blob)

            # add image to dict
            imgDict[shp.name+'.jpg'] = [shp.left, shp.top, shp.width, shp.height]
        else:
            # create copy of elem
            el = shp.element
            newel = copy.deepcopy(el)

            # add elem to shape tree
            new_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
    
    # things added first will be covered by things added last => since I want pictures to be in foreground, I will add them after others elements
    # you can change this if you want
    # add pictures
    for k, v in imgDict.items():
        new_slide.shapes.add_picture(k, v[0], v[1], v[2], v[3])
        os.remove(k)

    return new_slide # this returns slide so you can instantly work with it when it is pasted in presentation



templatePres = Presentation(f"{DIR_PATH}/template.pptx")

outputPres = Presentation() 
outputPres.slide_height, outputPres.slide_width = templatePres.slide_height, templatePres.slide_width
# this can sometimes cause problems. Alternative:
# outputPres = Presentation(f"{DIR_PATH}/template.pptx") and now delete all slides to have empty presentation

# if you just want to copy and paste slide:
SlideCopyFromPasteInto(templatePres,0,outputPres)

# if you want to edit slide that was just pasted in presentation:
pastedSlide = SlideCopyFromPasteInto(templatePres,0,outputPres)
pastedSlide.shapes.title.text = "My very cool title"

for shape in pastedSlide.shapes:

    if not(shape.has_text_frame): continue

    # easiest ways to edit text fields is to put some identifying text in them
    if shape.text_frame.text == "personName": # there is a text field with "personName" written into it
        shape.text_frame.text = "Brian"

    if shape.text_frame.text == "personSalary":
        shape.text_frame.text = str(brianSalary)

    # stylizing text need to be done after you change it
    shape.text_frame.paragraphs[0].font.size = Pt(80) 
    shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER

outputPres.save(f'{DIR_PATH}/output.pptx')

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
我尝试将其与从SSRS生成的幻灯片一起使用。这些幻灯片有一个包含所有内容的图像,但图片没有名称。我可以建议检查图像属性而不是检查名称中的图片?如果 hasattr(shp,'image'):然后我更改了保存到文件的图像名称以包括每个循环递增的变量。 - jimmiesrustled

3

我之前使用了n00by0815的答案,这个方法一直很好用,但当我需要复制图片时就不行了。这里是我修改后的版本,可以处理图片。这段代码会创建一个本地副本然后将其添加到幻灯片中。虽然可能有更简单的方式,但这种方法可行。


1

这里有另一种方法,可以将整个演示文稿中的每张幻灯片复制到单个PPTX幻灯片中,然后您可以使用LibreOffice将每个单独的PowerPoint转换为图像:

def get_slide_count(prs):
""" Get the number of slides in PPTX presentation """
    slidecount = 0
    for slide in prs.slides:
        slidecount += 1
    return slidecount


def delete_slide(prs, slide):
    """ Delete a slide out of a powerpoint presentation"""
    id_dict = { slide.id: [i, slide.rId] for i,slide in enumerate(prs.slides._sldIdLst) }
    slide_id = slide.slide_id
    prs.part.drop_rel(id_dict[slide_id][1])
    del prs.slides._sldIdLst[id_dict[slide_id][0]]


def get_single_slide_pres(prs, slidetokeep):
    for idx, slide in enumerate(prs.slides):
        if idx < slidetokeep:
            delete_slide(prs, slide)
        elif (idx > slidetokeep):
            delete_slide(prs, slide)
    prs.save(str(slidetokeep + 1) + ".pptx")


pptxfilepath = "test.pptx"
prs = Presentation(pptxfilepath)
slidecount = get_slide_count(prs)
for i in range(slidecount):
    prs_backup = Presentation(pptxfilepath)
    get_single_slide_pres(prs_backup, i)
    prs_backup = None

0

这是早前回答的扩展。

我能够使用多个模板幻灯片并复制它们来完成我的PPT项目。在建立演示文稿的最后,我会删除模板。要获取形状,您需要遍历slide.shapes并找到您要查找的形状的名称。一旦您返回了这个名称,您就可以根据需要编辑形状。我添加了一个版本的add_text函数,用于填充shape.text_frame

def find_shape_by_name(shapes, name):
    for shape in shapes:
        if shape.name == name:
            return shape
    return None

def add_text(shape, text, alignment=None):
    
    if alignment:
        shape.vertical_anchor = alignment

    tf = shape.text_frame
    tf.clear()
    run = tf.paragraphs[0].add_run()
    run.text = text if text else ''

查找形状“slide_title”。

slide_title = find_shape_by_name(slide.shapes,'slide_title')

在形状中添加文本。

add_text(slide_title,'TEST SLIDE')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接