【问题标题】:Python Pptx not capturing Slide Master Name properlyPython Pptx 未正确捕获幻灯片母版名称
【发布时间】:2020-10-29 14:19:08
【问题描述】:

正在玩 Python PPTX,它似乎没有正确读取幻灯片母版名称。

你可以在这里看到我重命名幻灯片母版

它甚至会在尝试添加幻灯片时出现

但是,当我通过 pptx 加载演示文稿时,名称是 ''

In [14]: import pptx
In [15]: pres = pptx.Presentation("adsf.pptx")
In [16]: pres.slide_master.name
Out[17]: ''

也许我在 powerpoint 方面做错了什么。我很想知道。我正在使用 Office 2016。作为旁注,我正在挖掘 xml,看起来 slidemaster xml 没有包含与ooxml 中的名称属性相关的任何属性。但是主题 xml 确实如此。让我感到困惑。

感谢您的时间和努力

编辑:

毕竟我找到了我的解决方案并感谢@Scanny

import pptx
import re
from lxml import etree

# This causes pres.slide_master.layout.placeholder.name to be passed to pres.slide.placeholder
@property
def placeholder_name(self):
    """Name of the placeholder inherited from slide master"""
    return self._inherited_value("name")

def Presentation(powerpoint=None):
    """
    Return a |Presentation| object loaded from *pptx*, where *pptx* can be
    either a path to a ``.pptx`` file (a string) or a file-like object. If
    *pptx* is missing or ``None``, the built-in default presentation
    "template" is loaded.
    """
    if powerpoint is None:
        powerpoint = pptx.api._default_pptx_path()

    # get the package and not just the presentation package
    package = pptx.package.Package.open(powerpoint)

    # now extract the document
    presentation_part = package.main_document_part

    if not pptx.api._is_pptx_package(presentation_part):
        tmpl = "file '%s' is not a PowerPoint file, content type is '%s'"
        raise ValueError(tmpl % (powerpoint, presentation_part.content_type))

    # the theme names are the slide master names
    themes = (part for part in package.parts if re.search("^/ppt/theme/theme\d+\.xml$",part.partname))
    theme_names = [etree.fromstring(theme.blob).get("name") for theme in themes]


    # now get the presentation
    presentation = presentation_part.presentation

    # change the slide master names
    for idx,sld_mstr in enumerate(presentation.slide_masters):
        sld_mstr.name = theme_names[idx]


    return presentation

pptx.Presentation = Presentation
pptx.shapes.placeholder._InheritsDimensions.placeholder_name = placeholder_name
In [2]: pres = pptx.Presentation("adsf.pptx")
In [3]: for sm in pres.slide_masters: print(sm.name)
my master
number 3
my second
In [4]: layout = pres.slide_masters[0].slide_layouts[0]
In [5]: layout.name
Out[5]: 'my master title slide layout'
In [6]: new_slide = pres.slides.add_slide(layout)
In [7]: new_slide.placeholders[0].name
Out[7]: 'Title 1'
In [8]: new_slide.placeholders[0].placeholder_name
Out[8]: 'Main Title'
In [9]: pres.save("test.pptx")                             

【问题讨论】:

    标签: python python-pptx


    【解决方案1】:

    是的,这就是我将继续进行的方式,在 XML 中查找字符串“my master”并查看它的显示位置。

    PowerPoint 主题包括母版和布局,因此 UI 可以通过此 rename 操作来重命名主题,这从 UI 的角度来看是有意义的,就像您想要保存此主题一样然后通过从列表中选择此名称稍后使用它。

    反向操作可能有意义,这意味着使用 python-pptx 设置主名称,将其读回以确认,然后查看它在 UI 中的显示位置(如果有的话)。


    您可以直接从演示文稿中进入演示文稿部分:

    prs = Presentation(...)
    presentation_part = prs._part
    

    所有部分都有对包的引用,不用自己加载:

    package = presentation_part._package
    

    但您可能不需要它,因为您可以直接从演示部分进入演示主题(而不是 Notes-pages 主题等):

    from pptx.opc.constants import RELATIONSHIP_TYPE as RT
    
    theme_rels = [rel for rel in presentation_part.rels if rel.reltype == RT.THEME]
    theme_parts = [presentation_part.related_parts[rel.rId] for rel in theme_rels]
    

    然后只需将每个主题作为 XmlPart 加载,例如:

    theme_parts = [
        XmlPart.load(
            part._partname,
            part._content_type,
            part._blob,
            part._package,
        )
        for part in theme_parts
    ]
    

    然后您可以在 theme_part._element 上获取主题 XML 文档的根,然后您可以在其上使用 lxml.etree._Element 方法遍历该树,或者仅使用 theme_part._element.attribs["name"] 获取根元素名称。

    这只是记忆中的空气代码,但希望能给你足够的继续,一旦你到达那里你就可以发布工作代码。

    如果你有什么可以完成这项工作,那么一定要使用它,但这可能更直接,并且使用已经存在的实现,因此你可能不必担心会出现意外错误:)

    几乎所有正在执行的代码都在pptx/opc/package.py 中。

    【讨论】:

    • 我添加了我在theme1.xml 中找到它的屏幕截图。你认为我可以做一些猴子补丁来得到我想要的东西吗?我们希望能够在 Powerpoint 中手动填写和命名所有幻灯片母版、布局和占位符,然后使用 pptx 返回用于创建幻灯片、应用布局和填充占位符的参考。我通过猴子修补pptx..shapes.placeholder._InheritsDimensions.placeholder_name = lambda self: self._inherited_value("name") 处理了占位符名称。我也想解决这个问题,但没有看到。
    • 我想我会得到一个对“原始”主题部分的引用,然后访问它的 XML 以使用 XPath 表达式提取这个值。您需要从类似于 document._part.package 的包对象开始,它为您提供了一个带有检索特定部分的方法的 OpcPackage 对象:github.com/scanny/python-pptx/blob/master/pptx/opc/…,可能是.part_related_by() 方法。查看 XmlPart 以加载原始部分并至少将其解析为 XML。
    • 我明白你在说什么。您如何确定要提取多少个 xml 文件?您正在为多个relationships/slideMaster 建立关系,但只使用一个relationships/theme。它发生在Unmarshaller 的某个地方。我正在查看pptx.opc.package.unmarshalparts 拥有所有theme xml 文件,但出于某种原因仅收集第一个theme
    • 查看 OP 进行编辑,我现在看到了,它是由 powerpoint 在 XML 中定义的——这很不幸。
    • 只有一个主题应该与演示文稿相关,除非他们有多个母版,其他主题可以与笔记幻灯片或其他内容相关。我已经详细说明了我的答案,以便为您提供更多信息。如果你的方式可行,我相信这很好,但我提到的方式更直接,应该让你直接找到你需要的主题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多