python-docx - 如何重启列表编号
我正在用python-docx模块自动生成一个Word文档。具体来说,我在制作一个多项选择题的测试,问题编号是1.、2.、3.……每个问题下面有4个答案,分别标记为A.、B.、C.和D.。我使用了一种样式来创建这些编号和字母列表。不过,我不知道怎么把字母重新开始。比如,第二个问题的答案应该是E.、F.、G.、H.。有没有人知道怎么把字母重新设置回A呢?我可以手动在答案字符串里指定字母,但我想知道怎么通过样式表来实现。谢谢。
2 个回答
我创建了一个拉取请求(#582),这个请求是为了处理一个底层的问题。我所做的就是定义了实现WML中numbering
子系统所需的XML类型。@scanny创建了一个叫xmlchemy
的子模块,它提供了一种半抽象的XML表示方式,这样如果你对标准有了解,就可以处理多级列表和其他编号任务。所以如果你构建我的分支,下面的代码就可以正常工作:
#!/usr/bin/python
from docx import Document
from docx import oxml
d = Document()
"""
1. Create an abstract numbering definition for a multi-level numbering style.
"""
numXML = d.part.numbering_part.numbering_definitions._numbering
nextAbstractId = max([ J.abstractNumId for J in numXML.abstractNum_lst ] ) + 1
l = numXML.add_abstractNum()
l.abstractNumId = nextAbstractId
m = l.add_multiLevelType()
m.val = 'multiLevel'
"""
2. Define numbering formats for each (zero-indexed)
level. N.B. The formatting text is one-indexed.
The user agent will accept up to nine levels.
"""
formats = {0: "decimal", 1: "upperLetter" }
textFmts = {0: '%1.', 1: '%2.' }
for i in range(2):
lvl = l.add_lvl()
lvl.ilvl = i
n = lvl.add_numFmt()
n.val = formats[i]
lt = lvl.add_lvlText()
lt.val = textFmts[i]
"""
3. Link the abstract numbering definition to a numbering definition.
"""
n = numXML.add_num(nextAbstractId)
"""
4. Define a function to set the (0-indexed) numbering level of a paragraph.
"""
def set_ilvl(p,ilvl):
pr = p._element._add_pPr()
np = pr.get_or_add_numPr()
il = np.get_or_add_ilvl()
il.val = ilvl
ni = np.get_or_add_numId()
ni.val = n.numId
return(p)
"""
5. Create some content
"""
for x in [1,2,3]:
p = d.add_paragraph()
set_ilvl(p,0)
p.add_run("Question %i" % x)
for y in [1,2,3,4]:
p2 = d.add_paragraph()
set_ilvl(p2,1)
p2.add_run("Choice %i" % y)
d.save('test.docx')
简单来说,现在的python-docx还不支持这个功能,不过如果你在这个项目的Github问题列表上提问,我们可能能给你提供一些解决办法:https://github.com/python-openxml/python-docx/issues/25
这个操作在Word中比大家想象的要复杂得多。我觉得这和Word在过去几十年中需要兼容很多版本有关。
简单来说,样式会引用一个编号定义,而这个编号定义又引用一个抽象编号定义。每个使用这个样式的段落都会得到一个序列中的下一个数字或字母。如果你想重新开始这个序列,就需要创建一个新的编号定义,它要引用和之前一样的抽象编号序列。然后在你想重新开始序列的段落上引用这个新的编号定义。接下来使用这个样式的段落会得到重新开始的序列中的下一个数字或字母。
为了实现这个,你需要:
- 找到样式的编号定义
- 找到它指向的抽象编号定义
- 创建一个新的编号定义,并设置重新开始的选项
- 调整段落元素,使其引用新的编号定义
总之,虽然我说了这么多,但我可以告诉你,我们之前其实是成功实现过这个功能的。我们还没有把它加入API,可能是因为还不太清楚API应该是什么样子,而且这个问题还没有排到优先处理的列表上。不过,如果你真的很想要,可能有几个替代的方法可以帮你解决这个问题。
在你的情况下,我觉得可能需要权衡一下。我会考虑直接把这些内容放在段落里,但最终决定还是要看你自己。