在日常工作中,我们经常需要批量创建和修改Word文档,比如制作合同、通知书、邀请函、报告等。有时候,可能必须逐个打开Word文档以查找或更改,当Word文档数量比较多时,传统的手动操作,不仅繁琐且容易出错或者遗漏掉。Python作为一门强大而灵活的编程语言,是实现文档自动化处理的理想工具,能够轻松地实现自动化生成Word文档的功能。本节教程将介绍如何通过Python的Tkinter图形界面库,实现从Excel文件中读取合同参数,自动填充到Word模板中,批量生成Word格式的劳动合同文档,大幅提高合同制作效率。
1、批量生成Word格式的劳动合同文档程序介绍
该程序通过Python脚本实现批量生成Word格式的劳动合同文档,主要利用Python的第三方库pandas和python-docx库从Excel文件中读取已保存的员工数据,根据不同的员工信息自动填充到Word模板中,批量生成劳动合同。
在开始编写Python脚本之前,先创建一个基本的Word模板,可以点击程序中的“下载Word模板”按钮,参考自动生成的Word模板,这个劳动合同模板包含所有静态文本和动态内容,动态内容就是在需要替换的地方设置占位符(例如“{name}”来表示合同中的名字),Word模板中的占位符格式为“{....}”,如下图所示:
接着,创建一个Excel文档,可以点击程序中的“下载Excel数据”按钮,参考自动生成的Excel数据文件。在Excel数据文件中将“id”、“name”、“Start”、“End”、“position”、“salary”信息作为标题,或者可以自定义更改这些标题,这些标题分别对应Word模板中的占位符,比如Excel数据中的标题“id”对应Word模板中的“{id}”,以此类推。注意:Excel数据文件中第一行标题不能为空,否则会报错。Excel数据文件中从第二行开始每一行代表一个员工的合同信息。批量生成合同过程,程序会读取Word模板中的所有内容,凡是遇到“{....}”的占位符,就用Excel文件中对应标题下的员工信息进行替换,Word模板中的“{....}”和Excel中的标题必须一一对应,且必须是全中文或全英文字符,如下图所示:
2、批量生成Word格式的劳动合同界面演示
3、代码实现
(1)安装pandas和python-docx库
首先,确保你已经安装了pandas库和python-docx库。如果还没有安装,可以通过pip安装:
pip install pandas
pip install python-docx
其中python-docx库是Python中用于创建和更新Microsoft Word (.docx) 文件的库,它允许程序员以编程方式处理Word文档,而无需依赖Microsoft Office。Pandas库是Python中用于数据分析的库,提供了许多强大的数据操作功能,使用pandas库可以方便地读取和生成Excel文件。
(2)批量生成Word格式的劳动合同文档,完整代码如下所示:
动手练一练:
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext, font
import os, webbrowser
import pandas as pd
from docx import Document
from docx.enum.text import WD_UNDERLINE
# 定义导出Word文档类
class ExportWord:
def __init__(self, path):
self.file_path = path
self.result = None
self.create_contract_template()
# 创建合同模板,向Word文件中插入默认数据
def create_contract_template(self):
# 创建员工Word文档
doc = Document()
# 创建合同标题
title = doc.add_heading('xx有限责任公司劳动合同', 0)
title.alignment = 1 # 标题居中显示
# 添加合同段落内容
doc.add_paragraph('甲方(用人单位):xx有限责任公司')
doc.add_paragraph('乙方(劳动者):').add_run('__{name}__').font.underline = True
doc.add_paragraph('工号:').add_run('__{id}__').font.underline = True
doc.add_paragraph('根据《中华人民共和国劳动合同法》以及有关法律、法规和政策的规定,经双方平等协商,订立本用工合同。')
doc.add_heading('第一条、协议期限', level=2)
p1 = doc.add_paragraph()
p1.add_run('协议期限为自')
p1.add_run('__{Start}__').underline = WD_UNDERLINE.SINGLE
p1.add_run('起至')
p1.add_run('__{End}__').underline = WD_UNDERLINE.SINGLE
p1.add_run('为止。')
doc.add_heading('第二条、工作内容', level=2)
p2 = doc.add_paragraph()
p2.add_run('乙方同意按甲方工作需要,在')
p2.add_run('__{position}__').underline = WD_UNDERLINE.SINGLE
p2.add_run('岗位工作,履行职责,完成任务。乙方应遵守甲方依法制定的管理制度。')
doc.add_heading('第三条、劳动保护和工作条件', level=2)
doc.add_paragraph('1、甲方为乙方提供生产所需的劳动保护用品。乙方应严格遵守各项安全操作规程。')
doc.add_paragraph('2、乙方在工作期间自身发生意外(包括死亡、突发病等)甲方不担负任何责任,工伤除外。')
doc.add_heading('第四条、劳动报酬', level=2)
p3 = doc.add_paragraph()
p3.add_run('1、按双方商定的计月工资标准定时发放,每月')
p3.add_run('__{salary}__').underline = WD_UNDERLINE.SINGLE
p3.add_run('元,享受其它福利待遇及费用。')
doc.add_paragraph('2、合同终止时,甲方为乙方提供的厂证、工作服、等等全部归还甲方后,乙方才可以结算工资,若有遗失或不归还,乙方将照价赔偿甲方,在工资中扣除。')
# 签名区域
doc.add_paragraph('\n')
# 甲方签名
p1 = doc.add_paragraph()
p1.add_run('甲方(盖章):')
blank1 = p1.add_run('_' * 20)
blank1.underline = WD_UNDERLINE.SINGLE
p1.add_run(' 日期:')
date1 = p1.add_run('_' * 20)
date1.underline = WD_UNDERLINE.SINGLE
# 乙方签名
p2 = doc.add_paragraph()
p2.add_run('乙方(签字):')
blank2 = p2.add_run('_' * 20)
blank2.underline = WD_UNDERLINE.SINGLE
p2.add_run(' 日期:')
date2 = p2.add_run('_' * 20)
date2.underline = WD_UNDERLINE.SINGLE
# 处理包含下划线的行
paragraph = doc.add_paragraph()
if '__' in paragraph.text:
parts = paragraph.text.split('__')
for i, part in enumerate(parts):
if i > 0:
# 添加下划线空白
blank_run = paragraph.add_run(' ' * 3)
blank_run.underline = WD_UNDERLINE.SINGLE
paragraph.add_run(part)
name = '/劳动合同Word文档模板.docx'
file_path = self.file_path + name
try:
# 保存Word模板文件到本地电脑上
doc.save(file_path)
self.result = True
except PermissionError:
self.result = False
messagebox.showerror("错误", "Word文件被占用,请关闭文件后重试")
# 定义导出Excel类
class ExportExcel:
def __init__(self, path):
self.excelpath = path
self.result = None
self.example_data()
# 向Excel中插入默认数据
def example_data(self):
name = '/劳动合同Excel数据.xlsx'
excelpath = self.excelpath + name
data = {
'id': ['1690', '1691', '1692', '1693'],
'name': ['张三', '李四', '王五', '赵六'],
'Start': ['2025年10月08日', '2025年10月05日', '2025年10月06日', '2025年10月07日'],
'End': ['2027年10月08日', '2027年10月05日', '2027年10月06日', '2027年10月07日'],
'position': ['搬运工', '电工', '会计', '保安',],
'salary': ['5000', '6000', '7000','8000',],
}
# 创建一个DataFrame
df = pd.DataFrame(data)
try:
# 将DataFrame写入Excel文件
df.to_excel(excelpath, index=False) # index=False表示不将行索引写入Excel文件
self.result = True
except PermissionError:
self.result = False
messagebox.showerror("错误", "Excel文件被占用,请关闭文件后重试")
# 创建主界面控件
class BatchMakeWord:
def __init__(self, root):
self.root = root
self.root.title("批量生成Word格式的劳动合同文档工具")
# 设置窗口居中
window_width = 950
window_height = 700
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
x = (screen_width - window_width) / 2
y = (screen_height - window_height) / 2
self.root.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
# 创建窗口“帮助”菜单栏
self.menu_bar = tk.Menu(self.root)
self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
self.file_menu.add_command(label="关于程序", command=self.about_contracts)
self.file_menu.add_command(label="程序讲解", command=self.explanation_window)
self.file_menu.add_separator()
self.file_menu.add_command(label="退出", command=self.window_close)
self.menu_bar.add_cascade(label="帮助", menu=self.file_menu)
self.root.config(menu=self.menu_bar)
# 定义标题标签
title_label = ttk.Label(self.root, text="批量生成Word格式的劳动合同文档工具", font=('宋体', 20, 'bold'))
title_label.place(x=260, y=20)
# 标签组件
label = tk.Label(self.root, text='选择Word:', font=('黑体', 15))
label.place(x=45, y=80)
# 标签组件
label2 = tk.Label(self.root, text='选择Excel:', font=('黑体', 15))
label2.place(x=45, y=155)
# 标签组件
label3 = tk.Label(self.root, text='选择文件夹:', font=('黑体', 15))
label3.place(x=45, y=235)
# 备注信息
Word_text = '备注:如果不熟悉Word模板内容,可以点击“下载Word模板”按钮,参考自动生成的Word模板。'
ttk.Label(self.root, text=Word_text, font=('宋体', 11), foreground="#1192f4").place(x=45, y=120)
# 备注信息
Excel_text = '备注:如果不了解Excel数据内容,可以点击“下载Excel数据”按钮,参考自动生成的Excel数据。'
ttk.Label(self.root, text=Excel_text, font=('宋体', 11), foreground="#1192f4").place(x=45, y=200)
# 输入框控件
self.entry_text = tk.StringVar()
entry = tk.Entry(self.root, textvariable=self.entry_text, font=('黑体', 12), width=56, state='readonly')
entry.place(x=165, y=85)
# 输入框控件
self.entry_text2 = tk.StringVar()
entry2 = tk.Entry(self.root, textvariable=self.entry_text2, font=('黑体', 12), width=56, state='readonly')
entry2.place(x=165, y=155)
# 输入框控件
self.entry_text3 = tk.StringVar()
entry3 = tk.Entry(self.root, textvariable=self.entry_text3, font=('黑体', 12), width=56, state='readonly')
entry3.place(x=165, y=235)
# 选择或下载Word模板文件
button = tk.Button(self.root, text='选择Word模板', font=('黑体', 13), bg="#94D8F6", command=self.select_word)
button.place(x=630, y=75)
button1 = tk.Button(self.root, text='下载Word模板', font=('黑体', 13), bg="#94D8F6", command=self.export_word)
button1.place(x=770, y=75)
# 选择或下载Excel数据文件
button2 = tk.Button(self.root, text='选择Excel数据', font=('黑体', 13), bg="#94D8F6", command=self.select_excel)
button2.place(x=630, y=155)
button1 = tk.Button(self.root, text='下载Excel数据', font=('黑体', 13), bg="#94D8F6", command=self.export_excel)
button1.place(x=780, y=155)
# 选择输出目录
button3 = tk.Button(self.root, text='选择输出路径', font=('黑体', 13), bg="#94D8F6", command=self.select_output)
button3.place(x=630, y=230)
# 开始生成按钮
start_make = tk.Button(self.root, text="开始生成合同", font=('黑体', 13), fg="#138535", command=self.batch_generation)
start_make.place(x=300, y=280)
# 取消按钮
all_clear = tk.Button(self.root, text="取消", font=('黑体', 13), fg="#ED1C24", command=self.entry_clear)
all_clear.place(x=500, y=280)
# 显示日志区域
report_frame = ttk.LabelFrame(self.root, text="日志输出", padding=5)
report_frame.place(x=45, y=330)
self.log_area = scrolledtext.ScrolledText(report_frame, width=118, height=18, state='disabled')
self.log_area.pack()
# 标签组件
label4 = tk.Label(self.root, text='生成进度', fg='green', font=('黑体', 15, font.BOLD))
label4.place(x=400, y=610)
# 进度条
self.ShowProgressbar = ttk.Progressbar(self.root)
self.ShowProgressbar.place(x=45, y=650)
self.ShowProgressbar['length']=800
# 将窗口关闭事件与window_close函数关联
self.root.protocol("WM_DELETE_WINDOW", self.window_close)
# 选择word文件导出路径
def export_word(self):
file = filedialog.askdirectory(title="选择保存的文件夹")
word1 = ExportWord(file)
if file and word1.result == True:
messagebox.showinfo('提示', '保存成功')
else:
messagebox.showinfo('提示', '保存失败')
# 选择Excel文件导出路径
def export_excel(self):
file = filedialog.askdirectory(title="选择保存的文件夹")
excel1 = ExportExcel(file)
if file and excel1.result == True:
messagebox.showinfo('提示', '保存成功')
else:
messagebox.showinfo('提示', '保存失败')
# 清除已选择文件
def entry_clear(self):
self.entry_text.set('')
self.entry_text2.set('')
self.entry_text3.set('')
# 输出日志
def show_info(self, text):
self.log_area.config(state='normal')
self.log_area.insert(tk.END, text + "\n")
self.log_area.config(state='disabled')
self.log_area.see(tk.END)
# 选择Word模板
def select_word(self):
path = filedialog.askopenfilename(title="选择Word模板", filetypes=[("Word文件", "*.docx")])
if path:
self.entry_text.set(path)
self.template_path = path
self.show_info(f"模板文件: {path}")
# 选择Excel文件
def select_excel(self):
path = filedialog.askopenfilename(title="选择Excel", filetypes=[("Excel文件", "*.xlsx")])
if path:
self.entry_text2.set(path)
self.data_path = path
self.show_info(f"数据文件: {path}")
# 选择保存后输出的目录
def select_output(self):
path = filedialog.askdirectory(title="选择输出目录")
if path:
self.output_path = path
self.entry_text3.set(path)
self.show_info(f"输出目录: {path}")
# 替换包含“{}”的关键字
def replace_keywords(self, paragraph, data_row):
# 遍历段落中的每一个运行对象(文本块)
for run in paragraph.runs:
for col in data_row.index:
search_word = "{" + col + "}"
# 检查对象中是否包含要查找的关键字
if search_word in run.text:
inline = paragraph.runs
for i in inline:
if search_word in i.text:
# 根据“{}”内的关键字替换成指定文本内容
text = i.text.replace(search_word, str(data_row[col]))
# 将替换后的文本重新赋值给当前运行对象
i.text = text
# 生成单份合同
def single_contract(self, data_row):
# 打开指定路径的Word文档
doc = Document(self.template_path)
# 遍历文档中的每一个段落
for p in doc.paragraphs:
self.replace_keywords(p, data_row)
# 替换表格部分
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for p in cell.paragraphs:
self.replace_keywords(p, data_row)
# 获取Excel第一列的标题,如果标题为空则提示错误
df = pd.read_excel(self.data_path)
first_column_title = df.columns[0]
if not first_column_title:
messagebox.showerror("错误", "数据文件第一列标题不能为空")
return
# 获取第一列的值
invoice_number = data_row[first_column_title]
# 根据第一列的工号值保存为指定的Word文档
out_file = os.path.join(self.output_path, f"工号_{invoice_number}.docx")
doc.save(out_file)
self.show_info(f"生成: {out_file}")
# 批量生成合同
def batch_generation(self):
if self.entry_text.get() == "" or self.entry_text2.get() == "" or self.entry_text3.get() == "":
messagebox.showerror("错误", "请先选择模板文件、数据文件和输出目录")
return
try:
# 读取Excel数据文件
if self.data_path.endswith(".xlsx"):
df = pd.read_excel(self.data_path)
self.ShowProgressbar['maximum'] = df.shape[0]
except Exception as e:
messagebox.showerror("错误", f"读取数据文件失败: {e}")
return
self.show_info("开始生成合同...")
for idx, row in df.iterrows():
try:
self.single_contract(row)
self.ShowProgressbar['value'] = idx + 1
self.root.update()
except Exception as e:
self.show_info(f"合同生成失败 : {e}")
self.show_info("全部合同生成完成!")
messagebox.showinfo("提示", "全部合同生成完成!")
# 将进度设置为0
self.ShowProgressbar['value'] = 0
# 关于程序页面
def about_contracts(self):
about = tk.Tk()
about.title('关于程序')
# 设置窗口居中
window_width = 860
window_height = 500
screen_width = about.winfo_screenwidth()
screen_height = about.winfo_screenheight()
x = (screen_width - window_width) / 2
y = (screen_height - window_height) / 2
about.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
about.resizable(width=False, height=False)
about_frame = tk.Frame(about, width=860, height=500)
about_frame.pack()
tk.Label(about_frame, text='批量生成Word格式的劳动合同文档工具', font=("宋体", 16)).place(x=260, y=20)
tk.Label(about_frame, text='使用编程语言:Python', font=("宋体", 14)).place(x=50, y=90)
tk.Label(about_frame, text='如果不熟悉Word模板内容,可以点击“下载Word模板”按钮,参考自动生成的Word模板', font=("宋体", 14)).place(x=50, y=150)
tk.Label(about_frame, text='如果不了解Excel数据内容,可以点击“下载Excel数据”按钮,参考自动生成的Excel数据', font=("宋体", 14)).place(x=50, y=210)
tk.Label(about_frame, text='Excel表头必须是第一行,并且不能为空', font=("宋体", 14)).place(x=50, y=270)
tk.Label(about_frame, text='Word模板中的“{....}”和Excel中的标题必须一一对应', font=("宋体", 14)).place(x=50, y=330)
tk.Label(about_frame, text='Word模板只支持.docx格式,Excel只支持.xlsx格式', font=("宋体", 14)).place(x=50, y=390)
tk.Label(about_frame, text='创作者:www.pyhint.com', font=("宋体", 14)).place(x=50, y=450)
about.mainloop()
# 程序讲解页面
def explanation_window(self):
webbrowser.open("https://www.pyhint.com/article/165.html")
# 点击关闭窗口触发函数
def window_close(self):
# 弹出确认对话框
if messagebox.askokcancel("退出", "你确定要退出吗?"):
# 点击确定后关闭窗口
self.root.destroy()
# 当前模块直接被执行
if __name__ == "__main__":
# 创建主窗口
root = tk.Tk()
app = BatchMakeWord(root)
# 开启主循环,让窗口处于显示状态
root.mainloop()