python-docx中的项目符号列表

Viz*_*zag 4 python python-docx

我试图让它在工作python-docx

在此处输入图片说明

我可以使用的项目符号列表:

from docx import Document
doc = Document()
p = doc.add_paragraph()
p.style = 'List Bullet'

r = p.add_run()
r.add_text("Item 1")
# Something's gotta come here to get the Sub-Item 1
r = p.add_run()
r.add_text("Item 2")    
# Something's gotta come here to get the Sub-Item 2
Run Code Online (Sandbox Code Playgroud)

我认为,在中间添加另一段无济于事,因为从本质上讲,这意味着我将List Bullet使用与其父级相同的格式而不是我想要的类似于孩子的格式来制作另一段。另外,run在同一段中添加另一个也无济于事(我尝试过此操作,弄乱了整个过程..)。有办法吗?

Mad*_*ist 7

有一种方法可以做到,但是您需要做一些额外的工作。目前,在python-docx中没有“本机”接口可以执行此操作。每个项目符号必须是一个单独的段落。运行仅适用于文本字符。

这个想法是列表项目符号或编号是由具体的项目符号或编号样式(指的是抽象样式)控制的。抽象样式确定受灾段落的样式,而具体编号确定抽象序列中的编号/项目符号。这意味着您可以将没有项目符号和编号的段落插入到各项目符号的段落中。同时,您可以随时通过创建新的具体样式来重新开始编号/项目符号排序。

发行#25中将所有这些信息散列(详细但未成功)。我现在没有时间或资源来解决这个问题,但是我确实编写了一个函数,该函数在讨论线程中的注释中保留。此函数将根据所需的缩进级别和段落样式查找抽象样式。然后,它将基于该抽象样式创建或检索具体样式,并将其分配给您的段落对象:

def list_number(doc, par, prev=None, level=None, num=True):
    """
    Makes a paragraph into a list item with a specific level and
    optional restart.

    An attempt will be made to retreive an abstract numbering style that
    corresponds to the style of the paragraph. If that is not possible,
    the default numbering or bullet style will be used based on the
    ``num`` parameter.

    Parameters
    ----------
    doc : docx.document.Document
        The document to add the list into.
    par : docx.paragraph.Paragraph
        The paragraph to turn into a list item.
    prev : docx.paragraph.Paragraph or None
        The previous paragraph in the list. If specified, the numbering
        and styles will be taken as a continuation of this paragraph.
        If omitted, a new numbering scheme will be started.
    level : int or None
        The level of the paragraph within the outline. If ``prev`` is
        set, defaults to the same level as in ``prev``. Otherwise,
        defaults to zero.
    num : bool
        If ``prev`` is :py:obj:`None` and the style of the paragraph
        does not correspond to an existing numbering style, this will
        determine wether or not the list will be numbered or bulleted.
        The result is not guaranteed, but is fairly safe for most Word
        templates.
    """
    xpath_options = {
        True: {'single': 'count(w:lvl)=1 and ', 'level': 0},
        False: {'single': '', 'level': level},
    }

    def style_xpath(prefer_single=True):
        """
        The style comes from the outer-scope variable ``par.style.name``.
        """
        style = par.style.style_id
        return (
            'w:abstractNum['
                '{single}w:lvl[@w:ilvl="{level}"]/w:pStyle[@w:val="{style}"]'
            ']/@w:abstractNumId'
        ).format(style=style, **xpath_options[prefer_single])

    def type_xpath(prefer_single=True):
        """
        The type is from the outer-scope variable ``num``.
        """
        type = 'decimal' if num else 'bullet'
        return (
            'w:abstractNum['
                '{single}w:lvl[@w:ilvl="{level}"]/w:numFmt[@w:val="{type}"]'
            ']/@w:abstractNumId'
        ).format(type=type, **xpath_options[prefer_single])

    def get_abstract_id():
        """
        Select as follows:

            1. Match single-level by style (get min ID)
            2. Match exact style and level (get min ID)
            3. Match single-level decimal/bullet types (get min ID)
            4. Match decimal/bullet in requested level (get min ID)
            3. 0
        """
        for fn in (style_xpath, type_xpath):
            for prefer_single in (True, False):
                xpath = fn(prefer_single)
                ids = numbering.xpath(xpath)
                if ids:
                    return min(int(x) for x in ids)
        return 0

    if (prev is None or
            prev._p.pPr is None or
            prev._p.pPr.numPr is None or
            prev._p.pPr.numPr.numId is None):
        if level is None:
            level = 0
        numbering = doc.part.numbering_part.numbering_definitions._numbering
        # Compute the abstract ID first by style, then by num
        anum = get_abstract_id()
        # Set the concrete numbering based on the abstract numbering ID
        num = numbering.add_num(anum)
        # Make sure to override the abstract continuation property
        num.add_lvlOverride(ilvl=level).add_startOverride(1)
        # Extract the newly-allocated concrete numbering ID
        num = num.numId
    else:
        if level is None:
            level = prev._p.pPr.numPr.ilvl.val
        # Get the previous concrete numbering ID
        num = prev._p.pPr.numPr.numId.val
    par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_numId().val = num
    par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_ilvl().val = level
Run Code Online (Sandbox Code Playgroud)

使用默认的内置文档存根中的样式,您可以执行以下操作:

d = docx.Document()
p0 = d.add_paragraph('Item 1', style='List Bullet')
list_number(d, p0, level=0, num=False)
p1 = d.add_paragraph('Item A', style='List Bullet 2')
list_number(d, p1, p0, level=1)
p2 = d.add_paragraph('Item 2', style='List Bullet')
list_number(d, p2, p1, level=0)
p3 = d.add_paragraph('Item B', style='List Bullet 2')
list_number(d, p3, p2, level=1)
Run Code Online (Sandbox Code Playgroud)

该样式不仅会影响制表符停顿和该段落的其他显示特性,还将有助于查找适当的抽象编号方案。prev=None在对的调用中隐式设置时p0,该函数将创建一个新的具体编号方案。其余所有段落都将继承相同的方案,因为它们获得了prev参数。只要在调用之前设置了所使用段落的编号,对的调用就list_number不必与之类的调用交织在一起。add_paragraphprev

  • @MadPhysicist 我用 PyPI 上提供的最新版本的 python-docx 尝试了这个,但它对我不起作用。所有四个项目都出现在同一级别,而不是预期的缩进。知道为什么会出现这种情况吗? (3认同)
  • 这就是为什么我们应该使用 LaTeX。 (2认同)
  • @杰西奈特。我向你保证,如果不是为了钱,我不会使用微软的任何东西。即使这样我也很挣扎,但钱就是钱:) (2认同)