from openpyxl import load_workbook
from docx import Document
from docx.shared import Inches
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING # 添加WD_LINE_SPACING
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.table import WD_ALIGN_VERTICAL, WD_ROW_HEIGHT_RULE
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from docxcompose.composer import Composer
import re
import pandas as pd
import tempfile, shutil, sys, os
def insert_entire_document(main_doc_path, insert_doc_path, output_path):
"""
将整个插入文档内容添加到主文档末尾
参数:
main_doc_path: 主文档路径
insert_doc_path: 要插入的文档路径
output_path: 输出文档路径
"""
# 读取主文档
main_doc = Document(main_doc_path)
# 读取要插入的文档
insert_doc = Document(insert_doc_path)
# 创建Composer对象进行文档合并
composer = Composer(main_doc)
# 将插入文档追加到主文档
composer.append(insert_doc)
# 保存合并后的文档
composer.save(output_path)
print(f"文档已成功合并保存至: {output_path}")
def set_cell_margins(cell, left=0.05, right=0.05, top=None, bottom=None):
"""
设置Word表格单元格边距(单位:厘米)
参数:
cell: 要设置的单元格对象
left: 左边距(厘米),默认为0.05
right: 右边距(厘米),默认为0.05
top: 上边距(厘米),可选
bottom: 下边距(厘米),可选
"""
# 确保单位为厘米
left = float(left) if left is not None else None
right = float(right) if right is not None else None
top = float(top) if top is not None else None
bottom = float(bottom) if bottom is not None else None
# 获取单元格属性
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
# 创建或获取单元格边距元素
tcMar = tcPr.first_child_found_in('w:tcMar')
if tcMar is None:
tcMar = OxmlElement('w:tcMar')
tcPr.append(tcMar)
# 创建边距元素并设置值
directions = {
'left': left,
'right': right,
'top': top,
'bottom': bottom
}
for direction, value in directions.items():
if value is None:
continue
# 转换厘米为缇(twips): 1厘米 = 567缇
twips = str(int(value * 567))
# 查找或创建方向元素
dir_element = tcMar.find(qn(f'w:{direction}'))
if dir_element is None:
dir_element = OxmlElement(f'w:{direction}')
dir_element.set(qn('w:w'), twips)
dir_element.set(qn('w:type'), 'dxa') # dxa表示单位为缇
tcMar.append(dir_element)
else:
dir_element.set(qn('w:w'), twips)
def set_table_cell_margins(table, left=0.05, right=0.05, top=None, bottom=None):
"""
设置整个表格所有单元格的边距
参数:
table: 表格对象
left: 左边距(厘米),默认为0.05
right: 右边距(厘米),默认为0.05
top: 上边距(厘米),可选
bottom: 下边距(厘米),可选
"""
for row in table.rows:
for cell in row.cells:
set_cell_margins(cell, left, right, top, bottom)
def add_formatted_text(paragraph, text):
"""
添加带格式的文本到段落中,自动处理斜体、上标、下标和中文双引号
格式规则:
- U 和 k 设置为斜体
- rel 设置为下标
- 科学计数法中的指数部分设置为上标
- 中文双引号单独处理并设置中文字体
"""
# 定义格式标记的正则表达式
patterns = [
(r'U(rel)?', 'u'), # 匹配U或Urel
(r'k=', 'k'), # 匹配k=
(r'rel', 'subscript'), # 单独匹配rel
(r'%', 'normal'), # 百分号
(r'dB', 'normal'), # dB单位
(r'[“”]', 'quote') # 匹配中文双引号
]
# 位置标记数组 (0=普通, 1=斜体, 3=下标, 4=中文引号)
flags = [0] * len(text)
# 应用格式标记
for pattern, flag_type in patterns:
for match in re.finditer(pattern, text):
start, end = match.span()
flag_value = {
'u': 1,
'k': 1,
'subscript': 3,
'normal': 0,
'quote': 4
}[flag_type]
# 特殊处理Urel组合
if flag_type == 'u' and match.group(1):
flags[start] = 1 # U斜体
for i in range(start + 1, end):
flags[i] = 3 # rel下标
else:
for i in range(start, end):
flags[i] = flag_value
# 新增:手动处理科学计数法格式(避免使用后视断言)
for match in re.finditer(r'×10(-\d+)', text):
start, end = match.span()
# 找到×10后面的指数开始位置
exp_start = match.start(1)
# 设置指数部分为上标
for i in range(exp_start, end):
flags[i] = 2 # 上标
# 分割并添加格式化的文本片段
start_idx = 0
for i in range(1, len(flags)):
if flags[i] != flags[i - 1]:
segment = text[start_idx:i]
add_segment(paragraph, segment, flags[i - 1])
start_idx = i
# 添加最后一段
add_segment(paragraph, text[start_idx:], flags[-1])
def add_segment(paragraph, text, flag):
"""添加指定格式的文本片段"""
if not text:
return
run = paragraph.add_run(text)
run.font.size = Pt(12)
# 根据标志设置字体
if flag == 4: # 中文双引号
run.font.name = '宋体' # 设置为中文字体
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
else: # 其他文本
run.font.name = 'Times New Roman'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
# 应用其他格式
if flag == 1: # 斜体 (U/k)
run.italic = True
elif flag == 2: # 上标 (科学计数法指数)
run.font.superscript = True
elif flag == 3: # 下标 (rel)
run.font.subscript = True
def excel_sheets_to_word(excel_path, word_path, lieshu,buquedingdu_excel_path):
# 加载Excel工作簿
wb = None
bqdd = None
try:
# 使用with确保资源释放
with pd.ExcelFile(buquedingdu_excel_path) as xls:
bqdd = pd.read_excel(xls)
# 加载工作簿
wb = load_workbook(excel_path, data_only=True)
try:
doc = Document(word_path)
except:
doc = Document() # 文件不存在时创建新文档
# 遍历所有sheet
sheetn = 0
xuhaon = 0
xuhao = ["二、", "三、", "四、", "五、", "六、", "七、"]
# 创建标题样式对象(设置全局行距)
try:
title_style = doc.styles['TitleStyle']
except KeyError:
# 如果不存在则创建
title_style = doc.styles.add_style('TitleStyle', WD_STYLE_TYPE.PARAGRAPH)
title_style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
title_style.paragraph_format.line_spacing = 1.5 # 设置为1.5倍行距
panju = 0
measurements = []
for sheet_name in wb.sheetnames:
sheet = wb[sheet_name]
max_row = sheet.max_row
max_col = sheet.max_column
# 检测有效数据范围(跳过开头和结尾的空行)
start_row = 1
end_row = max_row
# 查找第一个非空行(从顶部)
for row_idx in range(1, max_row + 1):
if any(sheet.cell(row=row_idx, column=col).value for col in range(1, max_col + 1)):
start_row = row_idx
break
# 查找最后一个非空行(从底部)
for row_idx in range(max_row, 0, -1):
if any(sheet.cell(row=row_idx, column=col).value for col in range(1, max_col + 1)):
end_row = row_idx
break
# print(end_row)
if sheet_name == "频率":
suoyin = "A" + str(end_row)
pinlv = sheet[suoyin].value
measurements.append(bqdd[pinlv][0])
measurements.append(bqdd[pinlv][1])
zd = lieshu[sheetn]
if sheet_name == "相对电平":
xuhaon = xuhaon - 1
else:
# 创建标题段落并指定样式
title = doc.add_paragraph(style='TitleStyle')
title_xu = xuhao[xuhaon] + sheet_name
title_run = title.add_run(title_xu)
title_run.bold = True # 设置加粗
title_run.font.size = Pt(14)
if sheet_name == "调幅深度":
measurements.append(bqdd[pinlv][2])
title_zhu = title.add_run("(功率0dBm,调制速率1kHz,检波器+/-PEAK/2,低通3kHz,高通300Hz)")
elif sheet_name == "调频频偏":
measurements.append(bqdd[pinlv][3])
title_zhu = title.add_run("(功率0dBm,调制速率1kHz,检波器+/-PEAK/2,低通3kHz,高通300Hz)")
elif sheet_name == "调相相偏":
measurements.append(bqdd[pinlv][4])
title_zhu = title.add_run("(功率0dBm,调制速率1kHz,检波器+/-PEAK/2,低通3kHz,高通300Hz)")
elif sheet_name == "频谱纯度":
measurements.append(bqdd[pinlv][5])
else:
title_zhu = title.add_run("")
title_zhu.font.size = Pt(10.5)
title_zhu.bold = False
title.alignment = WD_ALIGN_PARAGRAPH.LEFT
# 确保行距设置应用到段落(双重保证)
title.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
title.paragraph_format.line_spacing = 1.5
table = doc.add_table(rows=1, cols=zd) # 固定4列
table.style = 'Table Grid'
# 添加表头(第一行数据作为标题)
header_cells = table.rows[0].cells
for col in range(1, zd + 1): # 只取前4列
header_cells[col - 1].text = str(sheet.cell(row=1, column=col).value or "")
# 添加数据行(从第二行开始)
for row in range(2, end_row + 1):
row_cells = table.add_row().cells
for col in range(1, zd + 1): # 只取前4列
cell_value = sheet.cell(row=row, column=col).value
data_cell = str(cell_value) if cell_value is not None else ""
# print(data_cell)
if "*" in "9.9999934*":
panju = "不合格"
# print(panju)
else:
pass
data_cell = data_cell.replace("–", "-")
data_cell = data_cell.replace("HZ", "Hz")
row_cells[col - 1].text = data_cell
set_table_cell_margins(table, 0.05, 0.05)
# if zd >= 4: # 确保表格至少有4列
# table.columns[3].width = Cm(7) # 索引3对应第4列
# table.autofit = True
# print(f"已设置表格 '{sheet_name}' 第4列宽度为7cm(列数={zd})")
# else:
# print(f"警告:表格 '{sheet_name}' 仅 {zd} 列,无法设置第4列宽度")
# 设置单元格居中
for row in table.rows:
if zd >= 4:
row.cells[3].width = Cm(7)
row.height_rule = WD_ROW_HEIGHT_RULE.AUTO # 自动调整行高
for cell in row.cells:
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
for paragraph in cell.paragraphs:
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 设置表格字体
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.font.name = 'Times New Roman'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
run.font.size = Pt(12)
sheetn = sheetn + 1
xuhaon = xuhaon + 1
# print(measurements)
if panju == "不合格":
measurements.append("注:本次校准结果中标“*”号项目不符合技术指标要求。")
else:
measurements.append("注:本次校准结果符合技术指标要求。")
# 添加带格式的测量数据
for item in measurements:
p = doc.add_paragraph(style='TitleStyle')
add_formatted_text(p, item)
t1 = doc.add_paragraph(style='TitleStyle')
run_t1 = t1.add_run("以下无内容")
run_t1.font.size = Pt(12)
run_t1.font.name = 'Times New Roman'
# run_t1.font.name = u'黑体' # 中文字体
run_t1._element.rPr.rFonts.set(qn('w:eastAsia'), u'黑体')
run_t1.font.bold = True
doc.save(word_path)
# ... 原函数处理逻辑不变 ...
except Exception as e:
print(f"处理过程中出错: {e}")
finally:
# 确保资源释放
if wb:
wb.close()
# 清理大型对象
del bqdd
# 显式触发垃圾回收
import gc
gc.collect()
# wb = load_workbook(excel_path, data_only=True)
# bqdd=pd.read_excel(buquedingdu_excel_path)
# print(type(bqdd[2000][0]))
# 创建/加载Word文档
# 使用示例
import tkinter as tk
from tkinter import filedialog
# def select_file(file_type, title, extensions):
# """通过文件对话框选择文件"""
# root = tk.Tk()
# root.withdraw() # 隐藏主窗口
# file_path = filedialog.askopenfilename(
# title=title,
# filetypes=[(f"{file_type}文件", extensions), ("所有文件", "*.*")]
# )
# return file_path
def select_file(file_type, title, extensions):
"""通过文件对话框选择文件并确保资源释放"""
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(
title=title,
filetypes=[(f"{file_type}文件", extensions), ("所有文件", "*.*")]
)
# 确保销毁Tk窗口
root.destroy()
return file_path
# 选择Excel文件
if __name__ == "__main__":
# 创建临时目录处理
# temp_dir = tempfile.TemporaryDirectory()
# 优化文件选择
excel_path = select_file("Excel", "选择信号源数据", "*.xlsx")
if not excel_path:
print("未选择Excel文件,退出")
sys.exit()
word_path = select_file("Word", "选择信号源原始记录模板", "*.docx")
if not word_path:
print("未选择Word文件,退出")
sys.exit()
# 处理文件
lieshulist = None
try:
# 使用临时文件副本
# word_path = "信号源原始记录.docx"
# temp_word_path = os.path.join(temp_dir.name, "temp_output.docx")
lieshulist = list(pd.read_excel("信号源数据列数.xlsx")["列数"])
excel_sheets_to_word(excel_path, word_path, lieshulist, "不确定度.xlsx")
# shutil.copy(temp_word_path, "最终输出.docx")
finally:
# 清理资源
del lieshulist
# temp_dir.cleanup()
import gc
gc.collect()
print("处理完成")
input('按回车退出程序...')
# excel_path = select_file("Excel", "选择信号源数据", "*.xlsx")
# if not excel_path: # 用户取消了选择
# print("未选择Excel文件,程序退出")
# exit()
#
# # 选择Word文件
# word_path = select_file("Word", "选择信号源原始记录模板", "*.docx")
# if not word_path: # 用户取消了选择
# print("未选择Word文件,程序退出")
# exit()
# # excel_path = "信号源数据模板.xlsx" # Excel文件路径
# # word_path = "信号源原始记录.docx" # 输出Word路径
# buquedingdu="不确定度.xlsx"
# lieshulist=list(pd.read_excel("信号源数据列数.xlsx")["列数"])
#
# excel_sheets_to_word(excel_path, word_path, lieshulist,buquedingdu)
#
# print("原始记录或证书出具完毕")
# input('输入任意字符退出程序……')程序运行卡顿,点击停止后才弹出选择文件窗口
最新发布