Skip to main content
 首页 » 编程设计

python之如何使用 python-docx 将复选框表单插入到 .docx 文件中

2025年05月04日14kevingrace

我一直在使用 python 来实现自定义解析器,并使用解析后的数据来格式化要在内部分发的 word 文档。到目前为止,所有格式设置都非常简单明了,但我完全不知道如何将复选框插入到各个表格单元格中。

我试过在 python-docx 中使用 python 对象函数(使用 get_or_add_tcPr() 等),当我尝试打开文件时,这会导致 MS Word 抛出以下错误,"无法打开文件 xxxx,因为内容有问题详细信息:文件已损坏,无法打开”。

在为此苦苦挣扎了一段时间之后,我转向了第二种方法,涉及为输出文档操作 word/document.xml 文件。我已经检索到我认为是保存为 replacementXML 的复选框的正确 xml,并将填充文本插入到单元格中以充当可以搜索和替换的标签,searchXML。以下似乎在 linux (Fedora 25) 环境中使用 python 运行,但是当我尝试打开文档时,word 文档显示相同的错误,但是这次文档是可恢复的并恢复为填充文本。我已经能够让它与手动制作的文档一起使用并使用一个空的表格单元格,所以我相信这应该是可能的。注意:我已将表单元格的整个 xml 元素包含在 searchXML 变量中,但我已尝试使用正则表达式并缩短字符串。不只是使用完全匹配,因为我知道这可能会因细胞而异。

searchXML = r'<w:tc><w:tcPr><w:tcW w:type="dxa" w:w="4320"/><w:gridSpan w:val="2"/></w:tcPr><w:p><w:pPr><w:jc w:val="right"/></w:pPr><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:t>IN_CHECKB</w:t></w:r></w:p></w:tc>' 
 
def addCheckboxes():  
    os.system("mkdir unzipped") 
    os.system("unzip tempdoc.docx -d unzipped/") 
 
    with open('unzipped/word/document.xml', encoding="ISO-8859-1") as file: 
        filedata = file.read() 
 
    rep_count = 0 
    while re.search(searchXML, filedata): 
        filedata = replaceXML(filedata, rep_count) 
        rep_count += 1 
 
    with open('unzipped/word/document.xml', 'w') as file: 
        file.write(filedata) 
 
    os.system("zip -r ../buildcfg/tempdoc.docx unzipped/*") 
    os.system("rm -rf unzipped") 
 
def replaceXML(filedata, rep_count): 
    replacementXML = r'<w:tc><w:tcPr><w:tcW w:w="4320" w:type="dxa"/><w:gridSpan w:val="2"/></w:tcPr><w:p w:rsidR="00D2569D" w:rsidRDefault="00FD6FDF"><w:pPr><w:jc w:val="right"/></w:pPr><w:r><w:rPr><w:sz w:val="16"/> 
                       </w:rPr><w:fldChar w:fldCharType="begin"><w:ffData><w:name w:val="Check1"/><w:enabled/><w:calcOnExit w:val="0"/><w:checkBox><w:sizeAuto/><w:default w:val="0"/></w:checkBox></w:ffData></w:fldChar> 
                       </w:r><w:bookmarkStart w:id="' + rep_count + '" w:name="Check' + rep_count + '"/><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:instrText xml:space="preserve"> FORMCHECKBOX </w:instrText></w:r><w:r> 
                       <w:rPr><w:sz w:val="16"/></w:rPr></w:r><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:fldChar w:fldCharType="end"/></w:r><w:bookmarkEnd w:id="' + rep_count + '"/></w:p></w:tc>' 
    filedata = re.sub(searchXML, replacementXML, filedata, 1) 
 
    rerturn filedata 

我有一种强烈的感觉,即通过 python-docx 库有一种更简单(而且正确!)的方法来执行此操作,但出于某种原因,我似乎无法做到正确。

有没有办法轻松地将复选框字段插入到 MS Word 文档的表格单元格中?如果是,我该怎么做?如果不是,是否有比操作 .xml 文件更好的方法?

更新:我已经能够使用 python-docx 成功地将 XML 注入(inject)到文档中,但是没有出现复选框和添加的 XML。

我已将以下 XML 添加到表格单元格中:

<w:tc> 
  <w:tcPr> 
    <w:tcW w:type="dxa" w:w="4320"/> 
    <w:gridSpan w:val="2"/> 
  </w:tcPr> 
  <w:p> 
    <w:r> 
      <w:bookmarkStart w:id="0" w:name="testName"> 
        <w:complexType w:name="CT_FFCheckBox"> 
          <w:sequence> 
            <w:choice> 
              <w:element w:name="size" w:type="CT_HpsMeasure"/> 
              <w:element w:name="sizeAuto" w:type="CT_OnOff"/> 
            </w:choice> 
            <w:element w:name="default" w:type="CT_OnOff" w:minOccurs="0"/> 
            <w:element w:name="checked" w:type="CT_OnOff" w:minOccurs="0"/> 
          </w:sequence> 
        </w:complexType> 
      </w:bookmarkStart> 
      <w:bookmarkEnd w:id="0" w:name="testName"/> 
    </w:r> 
  </w:p> 
</w:tc> 

通过使用以下 python-docx 代码:

run = p.add_run() 
tag = run._r 
start = docx.oxml.shared.OxmlElement('w:bookmarkStart') 
start.set(docx.oxml.ns.qn('w:id'), '0') 
start.set(docx.oxml.ns.qn('w:name'), n) 
tag.append(start) 
 
ctype = docx.oxml.OxmlElement('w:complexType') 
ctype.set(docx.oxml.ns.qn('w:name'), 'CT_FFCheckBox') 
seq = docx.oxml.OxmlElement('w:sequence') 
choice = docx.oxml.OxmlElement('w:choice') 
el = docx.oxml.OxmlElement('w:element') 
el.set(docx.oxml.ns.qn('w:name'), 'size') 
el.set(docx.oxml.ns.qn('w:type'), 'CT_HpsMeasure') 
el2 = docx.oxml.OxmlElement('w:element') 
el2.set(docx.oxml.ns.qn('w:name'), 'sizeAuto') 
el2.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff') 
 
choice.append(el) 
choice.append(el2) 
 
el3 = docx.oxml.OxmlElement('w:element') 
el3.set(docx.oxml.ns.qn('w:name'), 'default') 
el3.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff') 
el3.set(docx.oxml.ns.qn('w:minOccurs'), '0') 
el4 = docx.oxml.OxmlElement('w:element') 
el4.set(docx.oxml.ns.qn('w:name'), 'checked') 
el4.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff') 
el4.set(docx.oxml.ns.qn('w:minOccurs'), '0') 
 
seq.append(choice) 
seq.append(el3) 
seq.append(el4) 
 
ctype.append(seq) 
start.append(ctype) 
 
end = docx.oxml.shared.OxmlElement('w:bookmarkEnd') 
end.set(docx.oxml.ns.qn('w:id'), '0') 
end.set(docx.oxml.ns.qn('w:name'), n) 
tag.append(end) 

似乎无法找到 XML 未反射(reflect)在输出文档中的原因,但会根据我找到的内容进行更新。

请您参考如下方法:

在@scanny 的大量挖掘和帮助之后,我终于能够完成这项工作。

可以使用以下函数将复选框插入 python-docx 中的任何段落。我正在将一个复选框插入表格中的特定单元格。

def addCheckbox(para, box_id, name, checked): 
 
  run = para.add_run() 
  tag = run._r 
  fldchar = docx.oxml.shared.OxmlElement('w:fldChar') 
  fldchar.set(docx.oxml.ns.qn('w:fldCharType'), 'begin') 
 
  ffdata = docx.oxml.shared.OxmlElement('w:ffData') 
  name = docx.oxml.shared.OxmlElement('w:name') 
  name.set(docx.oxml.ns.qn('w:val'), cb_name) 
  enabled = docx.oxml.shared.OxmlElement('w:enabled') 
  calconexit = docx.oxml.shared.OxmlElement('w:calcOnExit') 
  calconexit.set(docx.oxml.ns.qn('w:val'), '0') 
 
  checkbox = docx.oxml.shared.OxmlElement('w:checkBox') 
  sizeauto = docx.oxml.shared.OxmlElement('w:sizeAuto') 
  default = docx.oxml.shared.OxmlElement('w:default') 
 
  if checked: 
    default.set(docx.oxml.ns.qn('w:val'), '1') 
  else: 
    default.set(docx.oxml.ns.qn('w:val'), '0') 
 
  checkbox.append(sizeauto) 
  checkbox.append(default) 
  ffdata.append(name) 
  ffdata.append(enabled) 
  ffdata.append(calconexit) 
  ffdata.append(checkbox) 
  fldchar.append(ffdata) 
  tag.append(fldchar) 
 
  run2 = para.add_run() 
  tag2 = run2._r 
  start = docx.oxml.shared.OxmlElement('w:bookmarkStart') 
  start.set(docx.oxml.ns.qn('w:id'), str(box_id)) 
  start.set(docx.oxml.ns.qn('w:name'), name) 
  tag2.append(start) 
 
  run3 = para.add_run() 
  tag3 = run3._r 
  instr = docx.oxml.OxmlElement('w:instrText') 
  instr.text = 'FORMCHECKBOX' 
  tag3.append(instr) 
 
  run4 = para.add_run() 
  tag4 = run4._r 
  fld2 = docx.oxml.shared.OxmlElement('w:fldChar') 
  fld2.set(docx.oxml.ns.qn('w:fldCharType'), 'end') 
  tag4.append(fld2) 
 
  run5 = para.add_run() 
  tag5 = run5._r 
  end = docx.oxml.shared.OxmlElement('w:bookmarkEnd') 
  end.set(docx.oxml.ns.qn('w:id'), str(box_id)) 
  end.set(docx.oxml.ns.qn('w:name'), name) 
  tag5.append(end) 
 
  return 

fldData.text 对象看起来是随机的,但它是从生成的 XML 表单中提取的,它是一个带有现有复选框的 word 文档。如果不设置此文本,该函数将失败。我还没有确认,但我听说过这样一种情况:开发人员任意更改字符串,但一旦保存,它就会恢复为原始生成的值。