'''
档案归档是否希望自动根据页数分盒归档?本文讲述能自动分卷来实现,分盒后序号、页号都自动从1开始。能自动单元行加粗。能自动分页。下一步计划完善自动插入分页符的功能。
目前的档案归档是大概250页手工分盒,然后手写页号后生成目录,本程序的目的不是替换手工,而是和手工分卷进行相互印证,也可提前规划分卷情况。
本程序功能的简单描述:
根据提供的表头、行内容,实现自动分页、分卷,自动编序号、自动写入页号。并且,通过在A列是否填入序号来选择是否输入某些行。
程序具体要求(地址可以自己修改):
项目是通过tkinter在C:\\Users\\1\\Desktop\\T1\\目录下实现一个商业级python办公软件"目录生成",目的是只要做好模板,就能根据待复制文件生成结果文件。注意界面要美观,默认字体仿宋(已确定字体可用),字号12号,调整为更漂亮的布局,颜色为黄色系。打开文件框的默认打开位置:"/home/huanghe/Desktop/T1/"或者"C:\\Users\\1\\Desktop\\T1\\"在界面下部显示提示:"自动查找文件夹内"1"为开头的xlsx文件作为模板文件,"档案目录"为开头的xlsx文件作为待复制文件。界面只有打开位置地址栏、打开文件夹(深黄色)、开始(绿色)两个按钮。如果点击打开文件夹按钮可以选择要处理的文件夹。如果不点击打开文件夹按钮,则直接点击开始按钮时自动选择默认打开位置。
逻辑描述:总要求点击开始按钮后,
把模板文件的内容套进待复制文件中生成新的结果文件,保持待复制文件原格式。并保存在当前文件夹下的新建文件夹中,文件夹的名称取自模板文件表中D2单元格的名字。要求:按照A4的尺寸自动分页,表头是模板文件的前6行,每页都有表头,表头中的共几卷、第几卷根据实际情况改,每页的最后一行是"第*页"这样的页码。模板文件的H列是每行内容的页数,结果文件的F列是据此叠加的页号,页号超过250自动分卷,分卷后序号、页号重新计。如果模板文件的D列是包含"单元"内容,则这一行不写入A列序号和F列页号。自动调整行高。
开始总的主程序,顺序完成模板操作和写入程序,具体如下:
以下要求不可遗漏,如果逻辑有冲突则提示。
模板操作:
首先找模板文件,即自动查找文件夹内"1"为开头的xlsx文件中第1个工作表中"C2、B3、D3、F3、B4、D4、F4、F5"单元格的内容逐个写入表头数组。
从模板文件的第7行H列开始向下读取该列所有内容,作为对应行的页数数组。
变量"xh"初始值为1(就是序号),变量"ym"初始值为1(就是目录的第几页),变量"卷号"初始值为1(就是卷号),变量"yy"初始值250(就是每卷的最大允许页号),变量"HGhj"初始值为0(就是每页的行高合计)。变量"jh"(就是卷号)。变量"yhhj"初始值为1(就是档案在当前卷的第几页就是页号),变量"yhj"初始值为510(就是每页允许磅值)变量"ymhg"初始值22(就是第几页所在行的行高)
执行"模板操作"后执行写入程序的循环操作。
注意:其中结果文件在多次循环操作中多次修改,最后才通过输出程序正式保存。
写入程序:
每个执行写入程序包含多次"页写入"循环,每个"页写入"的循环包括1次复制复制页、1次表头写入和循环行写入的操作。页写入程序的整体运行逻辑依据:该程序是为了将模板内容分页输出,每页程序都是进行先复制复制页操作,来复制一个新的"复制页"写一次表头然后看是说明行还是序号行按要求逐行写入数据,调整行高,如果在该行吸入后判断该分卷则分卷,该分页则分页,然后重新执行写入程序,还是先复制一个新的"复制页"写一次表头再继续延续之前行从已写入内容的下一行继续执行,直至执行完毕,如果判断已执行则结束写入程序,接着执行输出程序得到结果文件即可。以此实现模板文件的内容全部写入结果文件。
复制复制页:其次找到待复制文件,即自动查找文件夹内"档案目录"为开头的xlsx文件作为待复制文件,其第一个工作表中有内容的表格整体复制后作为"复制页"作为临时文件,等待循环替换并按照原格式输出。注意:复制单元格样式时应保留原格式,例如字体、字号、是否加粗、行距、边框、居中、自动换行、合并单元格信息。
表头写入:将表头数组的值写入"复制页"的对应位置单元格,注意保留待复制文件原格式,例如字体、字号、是否加粗、行距、边框、居中、是否自动换行、合并单元格等信息。
循环行写入:从模板文件得到的第A列至E列从第7行开始逐行往下开始读取内容,直至把每行读完后入完毕,并记住读取模板文件的第几行,以便下一个写入程序循环。首先每行都执行该行的D列单元判断、A列判断、行高判断、分离判断,不断按照从上到下的顺序将B列至E列逐行写入"复制页"B列至E列从第7行开始逐行往下判断后写入,以此类推把以下每行单元格内容写入。(注意延续上一个"页写入"循环最后写入的行继续向下)
D列单元判断:每行D列单元格的写入内容如果字数小于10个字符且包含"单元"则该行作为一个"说明行",该行不再进行A列判断,只是仅仅写入D列单元内容,该单元格格式设置为宋体12号加粗、左右居中、垂直居中。其对应行的F列不写入,其对应的A列也不写入。注意:该行不参与序号、页码的合计及写入,不参与"序号行"的序号、页码计数。
A列判断:如果模板文件中该行D列单元判断不是"说明行",判断A列的单元格如果是数值,就作为"序号行"进行序号写入。每个"序号行"的A列均写入序号变量xh,每写入一行xh自动加1。
每个"序号行"的F列写入的页号就是变量yhhj,每个序号行写入结束变量yhhj就叠加本行的页数,得到的新变量yhhj。
例如:如果第2卷的第1个"序号行"页数是2,它的页号就是yhhj的初始值1(yhhj)因此其F列写入1,并且变量yhhj增加2等于3,因此下一个"序号行"写入的页号就是新的yhhj,也就是3以此类推。
例如:如果A列的单元格不是数字则自动跳过该行寻找下一行,例如:第1卷模板文件的第7行经D列单元判断为"说明行"则按"说明行"要求写入"复制页"第7行。模板文件的第8行经D列单元判断不是为"说明行"且A列内容是1,判断是数字并且此行H列的页数2,因此将xh的值1正常写入"复制页"第8行A列,B列至E列对照写入,F列写入页号值为1(即yhhj初始值为1)然后yhhj增加2=3,接着判断出模板文件第9、10行的内容不为数字且不是"说明行",则不写入,继续进行下一行判断,找到模板文件的第11行同样不为数字但是符合D列单元判断,则为"说明行"写入"复制页"第9行D列、其中A列和F列为空,继续向下判断,找到模板文件的第12行数字且不是"说明行"时,将其B列至E列作为"序号行"写入到"复制页"的第10行,其A列写入了序号xh值2(xh=xh+1,即不断累加),其页号在F列写入3(因为在上一个"序号行"写入后yhhj已变为3)(注意维持原格文字式),以此类推。
分离判断:首先,每行判断是否模板文件的B列至E列内容的所有行均写入了,如果写入完了,则执行结尾操作,之后跳出该行的分离判断不再进行下一步判断,结束写入程序,开始输出程序。否则,每行判断当页码合计yhhj大于yy,或"1"为开头的xlsx文件中第1个工作表中I列(此列为强制分卷标记)的该行有内容为"分卷"时,则执行分卷操作之后跳出该行分离判断不再进行下一步判断,保留读取模板文件的行号继续循环运行写入程序。否则,如果行高合计HGhj≥yhg磅时或I列(此列为强制分卷标记)的该行有内容为"分页"时,执行分页操作,保留读取模板文件的行号继续循环运行写入程序。
结尾操作:将当前写入行的向下保留wc行,之下的所有行内容删除,并在写入行的向下wc+1行的F列单元格写入"第ym页",替换文中ym为变量"ym"的值,格式为宋体12居中,该行行高25。wc的值为:(yhj-HGhj)/25的结果四舍五入取整数。将此"复制页"写入结果文件的x+1行(按从上到下顺序放入结果文件的第一个工作表)。接着将结果文件中所有的jh、zjh内容都替换为变量"jh"的值(即最后的卷号就是总卷号)。然后结束写入程序执行输出程序。
分卷操作:将当前写入行的向下保留wc行,之下的所有行内容删除,并在写入行的向下wc+1行的F列单元格写入"第ym页",替换文中ym为变量"ym"的值,格式为宋体12居中,该行行高为变量ymhg。wc的值为:(yhj-HGhj)/25的结果四舍五入取整数。然后将"复制页"中卷号jh的替换为变量"jh"的值,然后将序号"xh"、页号合计yhhj的值和页码"ym"重置为1,重置行高合计HGhj的值为0,卷号变量"jh"值加1。将修改后的"复制页"写入结果文件中原有内容向下一行的位置(按从上到下顺序放入结果文件的第一个工作表)。如此完整一页同时完成了卷号增加和页码页重置。接着进入写入程序的下一次循环。
分页操作:清除最后一个写入行以下的表格内容,包括边框,并在写入行的向下一行的F列单元格写入"第ym页",格式为宋体12居中,该行行高为变量ymhg。替换文中ym为变量"ym"的值,随后页码ym值加1,重置行高合计HGhj的值为0。如此完成一页。每页按从上到下顺序放入结果文件的第一个工作表,(意思是一页放不下了,新生成一页)之后的每一页写入程序的循环应在x+1行开始写入。将此"复制页"写入结果文件的x+1行。即如果之前有写入页则从已写入内容的下一行继续写入。接着进入写入程序的下一次循环。
行高判断:行高DHG,用于通过每行的D列单元格确定行高,算法为每15个字一个级别,英文、数字占半个字,标点占一个字的位置。如该行D单元格字数≤15,行高DHG=15*1+10=25,16≤字数≤30算两行,行高DHG=15*2+10=40,,31≤字数≤45算三行,行高DHG=15*3+10=55。以此类推,单位为磅。
行高BHG用于通过每行的B列单元格确定行高,算法为每6个字一个级别,英文、数字占半个字,标点占一个字的位置。如该行B单元格字数≤12,行高DHG=10*1+10=25,13≤字数≤17算两行,行高DHG=15*2+10=40,,31≤字数≤45算三行,行高DHG=15*3+10=55。以此类推,单位为磅。
该行的行高HG为DHG、BHG的最大值,具体调整该行行高,行高合计HGhj为本页内逐行叠加行高HG进行累加的结果。
输出程序:写入程序结束后,无需手动操作,程序自动在选中的文件夹即当前文件夹下自动新建一个文件夹(注意是一个文件夹不是多个文件夹)。该文件夹名称是取自"1"为开头的xlsx模板文件中第1个工作表中"C2"单元格内容。将处理结束的结果文件命名为:待复制文件名称(不要扩展名,例如"档案目录")+(+"1"为开头的xlsx模板文件中第1个工作表中"C2"单元格内容+)。然后另存进中新建的文件夹,如果有名字重复的文件夹,则在文件夹名后边增加序号。然后,对保存的结果文件插入分页符,除了最后一个第几页之外,在所有的第几页下插入分页符,
程序生成后逐条核对要求是否符合。
'''
导入必要的库
import os # 文件路径操作
import tkinter as tk # GUI框架
from tkinter import ttk, filedialog, messagebox, scrolledtext # GUI组件
from openpyxl import load_workbook, Workbook # Excel操作
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill # Excel样式
from openpyxl.worksheet.pagebreak import Break # Excel分页符
from openpyxl.utils import get_column_letter # Excel列字母转换
import copy # 深拷贝
import re # 正则表达式
import threading # 多线程处理
from datetime import datetime # 时间处理
import time # 时间相关
import math # 数学计算
定义专业目录生成器类
class ProfessionalDirectoryGenerator:
"""专业级目录生成器 - 商业级办公软件"""
def init(self, root):
"""初始化目录生成器"""
设置根窗口属性
self.root = root
self.root.title("📚 专业目录生成系统 v6.0")
self.root.geometry("1000x800")
定义黄色系配色方案
self.colors = {
'primary_bg': '#FFFDE7', # 主背景色
'secondary_bg': '#FFF9C4', # 次背景色
'card_bg': '#FFF59D', # 卡片背景
'accent_yellow': '#FFD54F', # 强调黄色
'accent_orange': '#FFB74D', # 橙色
'success_green': '#8BC34A', # 成功绿色
'warning_orange': '#FF9800', # 警告橙色
'error_red': '#F44336', # 错误红色
'text_dark': '#5D4037', # 深色文字
'text_light': '#FFFFFF', # 浅色文字
'entry_bg': '#FFFFFF', # 输入框背景
'border_color': '#FFB300', # 边框色
'button_yellow': '#FFC107', # 黄色按钮
'button_green': '#4CAF50', # 绿色按钮
'status_bar': '#FFF176', # 状态栏
}
定义字体配置
self.fonts = {
'title': ("Fangsong Ti", 24, "bold"), # 标题字体
'heading': ("Fangsong Ti", 16, "bold"), # 标题字体
'normal': ("Fangsong Ti", 12), # 普通字体
'small': ("Fangsong Ti", 10), # 小字体
'mono': ("Consolas", 10), # 等宽字体
}
定义状态变量
self.is_processing = False # 是否正在处理
self.cancel_requested = False # 是否请求取消
设置默认路径
if os.name == 'posix':
self.default_path = "/home/huanghe/Desktop/T1/" # Linux路径
else:
self.default_path = "C:\\Users\\1\\Desktop\\T1\\" # Windows路径
如果默认路径不存在,使用桌面路径
if not os.path.exists(self.default_path):
if os.name == 'posix':
self.default_path = os.path.expanduser("~/Desktop") # Linux桌面
else:
self.default_path = os.path.join(os.path.expanduser("~"), "Desktop") # Windows桌面
设置用户界面
self.setup_ui()
def setup_ui(self):
"""设置用户界面"""
创建主容器框架
main_container = tk.Frame(self.root, bg=self.colors['primary_bg'])
main_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
创建标题区域
title_frame = tk.Frame(main_container, bg=self.colors['accent_yellow'])
title_frame.pack(fill=tk.X, pady=(0, 20))
创建标题标签
title_label = tk.Label(
title_frame,
text="📚 专业目录生成系统",
font=self.fonts['title'],
bg=self.colors['accent_yellow'],
fg=self.colors['text_dark'],
pady=20
)
title_label.pack()
创建主内容区域
content_frame = tk.Frame(main_container, bg=self.colors['primary_bg'])
content_frame.pack(fill=tk.BOTH, expand=True)
创建左侧控制面板
left_panel = tk.Frame(content_frame, bg=self.colors['primary_bg'])
left_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 15))
创建工作目录设置框架
dir_frame = self.create_styled_frame(left_panel, "📂 工作目录设置")
创建路径输入框架
path_input_frame = tk.Frame(dir_frame, bg=self.colors['secondary_bg'])
path_input_frame.pack(fill=tk.X, padx=20, pady=20)
创建路径标签
tk.Label(
path_input_frame,
text="工作目录:",
font=self.fonts['normal'],
bg=self.colors['secondary_bg'],
fg=self.colors['text_dark']
).pack(side=tk.LEFT, padx=(0, 10))
创建路径变量和输入框
self.path_var = tk.StringVar(value=self.default_path)
self.path_entry = tk.Entry(
path_input_frame,
textvariable=self.path_var,
font=self.fonts['normal'],
bg=self.colors['entry_bg'],
fg='#000000',
width=45
)
self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
创建浏览按钮(深黄色)
self.browse_btn = tk.Button(
path_input_frame,
text="📂 浏览文件夹",
command=self.browse_folder,
font=self.fonts['normal'],
bg='#FFB300', # 深黄色
fg=self.colors['text_dark'],
activebackground='#FFA000',
activeforeground=self.colors['text_dark'],
relief=tk.RAISED,
bd=2,
padx=20,
pady=5,
cursor="hand2"
)
self.browse_btn.pack(side=tk.RIGHT)
创建操作控制区
control_frame = self.create_styled_frame(left_panel, "🎮 操作控制")
创建按钮容器
button_container = tk.Frame(control_frame, bg=self.colors['card_bg'])
button_container.pack(pady=30)
创建开始按钮(绿色)
self.start_btn = tk.Button(
button_container,
text="开始",
command=self.start_processing,
font=self.fonts['normal'],
bg=self.colors['button_green'],
fg=self.colors['text_light'],
activebackground='#45a049',
activeforeground=self.colors['text_light'],
relief=tk.RAISED,
bd=2,
padx=30,
pady=10,
cursor="hand2"
)
self.start_btn.pack(pady=10)
创建底部信息栏
bottom_frame = tk.Frame(main_container, bg=self.colors['status_bar'], height=60)
bottom_frame.pack(fill=tk.X, pady=(20, 0))
bottom_frame.pack_propagate(False)
info_text = "提示:自动查找文件夹内'1'开头的.xlsx文件作为模板文件,'档案目录'开头的.xlsx文件作为待复制文件"
info_label = tk.Label(
bottom_frame,
text=info_text,
font=self.fonts['small'],
bg=self.colors['status_bar'],
fg=self.colors['text_dark'],
pady=20,
wraplength=900
)
info_label.pack()
绑定快捷键
self.root.bind('<Return>', lambda e: self.start_processing())
self.root.bind('<Escape>', lambda e: self.stop_processing())
记录初始日志
self.log_message("系统初始化完成", "info")
self.log_message(f"默认工作目录: {self.default_path}", "info")
def create_styled_frame(self, parent, title):
"""创建带标题的装饰框"""
创建框架
frame = tk.Frame(parent, bg=self.colors['card_bg'], relief=tk.RAISED, bd=2)
frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
创建标题标签
title_label = tk.Label(
frame,
text=title,
font=self.fonts['heading'],
bg=self.colors['accent_yellow'],
fg=self.colors['text_dark'],
padx=20,
pady=10
)
title_label.pack(anchor=tk.W, fill=tk.X)
return frame
def browse_folder(self):
"""浏览文件夹"""
打开文件夹选择对话框
folder_path = filedialog.askdirectory(
initialdir=self.path_var.get(),
title="选择工作文件夹"
)
如果选择了路径,则更新路径变量并记录日志
if folder_path:
self.path_var.set(folder_path)
self.log_message(f"工作目录已设置为: {folder_path}", "info")
def clear_log(self):
"""清空日志(简化版,移除日志文本框)"""
简化版本,什么都不做
pass
def log_message(self, message, level="info"):
"""记录日志消息(简化版,移除日志文本框)"""
简化版本,只打印到控制台
timestamp = datetime.now().strftime("%H:%M:%S")
level_icons = {
"info": "ℹ️",
"success": "✅",
"warning": "⚠️",
"error": "❌",
"debug": "🔍"
}
icon = level_icons.get(level, "•")
print(f"[{timestamp}] {icon} {message}")
def update_status(self, message, progress=None):
"""更新状态(简化版,移除状态标签和进度条)"""
简化版本,只打印消息
print(f"状态更新: {message}")
def update_stats(self, files=0, rows=0, pages=0, volumes=0):
"""更新统计信息(简化版,移除统计信息框架)"""
简化版本,什么都不做
pass
def start_processing(self):
"""开始处理"""
如果已经在处理中,显示警告
if self.is_processing:
messagebox.showwarning("警告", "已经在处理中,请等待当前任务完成。")
return
获取工作目录
work_dir = self.path_var.get().strip()
如果没有指定工作目录,使用默认路径
if not work_dir:
work_dir = self.default_path
self.path_var.set(work_dir)
检查工作目录是否存在
if not os.path.exists(work_dir):
messagebox.showerror("错误", f"指定的文件夹不存在!\n{work_dir}")
return
重置状态变量
self.is_processing = True
self.cancel_requested = False
禁用开始按钮
self.start_btn.config(state=tk.DISABLED)
记录开始处理的日志
self.log_message("开始目录生成处理", "info")
self.update_status("正在查找文件...", 10)
在工作线程中启动处理
thread = threading.Thread(target=self.process_directory, args=(work_dir,))
thread.daemon = True
thread.start()
def stop_processing(self):
"""停止处理"""
如果正在处理,设置取消标志
if self.is_processing:
self.cancel_requested = True
self.log_message("用户请求停止处理", "warning")
def complete_processing(self, success=True):
"""完成处理"""
def update():
重置处理状态
self.is_processing = False
启用开始按钮
self.start_btn.config(state=tk.NORMAL)
根据处理结果更新状态
if success and not self.cancel_requested:
self.log_message("处理完成", "success")
elif self.cancel_requested:
self.log_message("用户请求停止处理", "warning")
else:
self.log_message("处理失败", "error")
在主线程中完成处理
self.root.after(0, update)
def process_directory(self, work_dir):
"""处理目录"""
记录开始时间
start_time = time.time()
try:
记录处理开始日志
self.log_message(f"开始处理目录: {work_dir}", "info")
self.update_status("正在查找文件...", 10)
查找模板文件和源文件
template_files = []
source_files = []
遍历工作目录中的文件
for file in os.listdir(work_dir):
只处理.xlsx文件
if file.lower().endswith('.xlsx'):
file_path = os.path.join(work_dir, file)
如果文件名以'1'开头,则为模板文件
if file.startswith('1'):
template_files.append(file_path)
如果文件名以'档案目录'开头,则为源文件
elif file.startswith('档案目录'):
source_files.append(file_path)
记录找到的文件数量
self.log_message(f"找到模板文件: {len(template_files)}个", "success")
self.log_message(f"找到待复制文件: {len(source_files)}个", "success")
检查是否有模板文件
if not template_files:
self.log_message("错误: 未找到以'1'开头的模板文件!", "error")
self.complete_processing(False)
return
检查是否有源文件
if not source_files:
self.log_message("错误: 未找到以'档案目录'开头的待复制文件!", "error")
self.complete_processing(False)
return
处理每个文件组合
success_count = 0
total_combinations = len(template_files) * len(source_files)
遍历所有模板文件和源文件的组合
for i, template_file in enumerate(template_files):
for j, source_file in enumerate(source_files):
检查是否请求取消
if self.cancel_requested:
break
计算当前处理进度
current = i * len(source_files) + j + 1
progress = 20 + (current / total_combinations * 70)
更新状态和日志
self.update_status(f"处理文件组合 {current}/{total_combinations}...", progress)
self.log_message(
f"处理组合 {current}: {os.path.basename(template_file)} + {os.path.basename(source_file)}",
"info")
try:
处理单个文件对
result_file = self.process_file_pair(template_file, source_file, work_dir)
if result_file:
success_count += 1
self.log_message(f"✓ 成功生成: {os.path.basename(result_file)}", "success")
except Exception as e:
self.log_message(f"✗ 处理失败: {str(e)}", "error")
if self.cancel_requested:
break
计算处理耗时
elapsed_time = int(time.time() - start_time)
根据处理结果记录日志
if self.cancel_requested:
self.log_message(f"处理取消,用时 {elapsed_time}秒,成功生成 {success_count} 个文件", "warning")
else:
self.log_message(f"处理完成,用时 {elapsed_time}秒,成功生成 {success_count} 个文件", "success")
self.update_stats(files=success_count)
完成处理
self.complete_processing(success_count > 0)
except Exception as e:
记录错误日志
self.log_message(f"处理过程中出现未预期错误: {str(e)}", "error")
self.complete_processing(False)
def process_file_pair(self, template_file, source_file, work_dir):
"""处理单个文件对"""
try:
1. 模板操作:加载模板文件
template_wb = load_workbook(template_file, data_only=True)
template_ws = template_wb.active
读取表头:从特定单元格读取内容
header_cells = ['C2', 'B3', 'D3', 'F3', 'B4', 'D4', 'F4', 'F5']
header_array = []
for cell_addr in header_cells:
cell = template_ws[cell_addr]
header_array.append(cell.value if cell.value is not None else "")
读取页数数组:从H列第7行开始读取,确保每个模板行都有对应的页数
page_numbers = []
for row in range(7, template_ws.max_row + 1):
cell_value = template_ws[f'H{row}'].value
try:
page_num = float(cell_value) if cell_value is not None else 0
page_numbers.append(page_num)
except:
page_numbers.append(0)
读取强制分卷标记:从I列读取
force_breaks = {}
for r in range(7, template_ws.max_row + 1):
cell_value = template_ws.cell(row=r, column=9).value # I列
if cell_value is not None and str(cell_value).strip():
force_breaks[r] = str(cell_value).strip()
获取项目名称:从C2单元格读取(用于结果文件名)
project_name = str(template_ws['C2'].value or "项目目录").strip()
获取文件夹名称:从D2单元格读取
folder_name = str(template_ws['D2'].value or project_name).strip()
加载源文件
source_wb = load_workbook(source_file)
source_ws = source_wb.active
创建结果工作簿
result_wb = Workbook()
result_ws = result_wb.active
result_ws.title = "目录"
执行写入程序
output_path = self.execute_writing_program(
template_ws=template_ws,
source_ws=source_ws,
result_ws=result_ws,
header_array=header_array,
page_numbers=page_numbers,
force_breaks=force_breaks,
project_name=project_name,
folder_name=folder_name,
source_file=source_file,
work_dir=work_dir,
result_wb=result_wb
)
return output_path
except Exception as e:
raise Exception(f"处理文件对失败: {str(e)}")
def execute_writing_program(self, template_ws, source_ws, result_ws, header_array,
page_numbers, force_breaks, project_name, folder_name, source_file,
work_dir, result_wb):
"""执行写入程序 - 核心逻辑(修正版)"""
try:
初始化变量
xh = 1 # 序号
ym = 1 # 目录的第几页
jh = 1 # 当前卷号
zjh = 1 # 总卷号
yy = 250 # 每卷的最大允许页号
HGhj = 0 # 每页的行高合计
yhhj = 1 # 档案在当前卷的第几页就是页号
yhj = 468 # 每页允许磅值
准备模板数据
template_data = []
for row in range(7, template_ws.max_row + 1):
row_data = []
for col in range(1, 6): # A到E列
cell_value = template_ws.cell(row=row, column=col).value
row_data.append(cell_value)
template_data.append(row_data)
total_rows = len(template_data)
self.log_message(f"读取模板数据完成,共 {total_rows} 行", "info")
写入程序主循环
template_index = 0 # 模板数据索引
result_start_row = 1 # 结果文件开始行
rows_written = 0 # 已写入行数
pages_written = 0 # 已写入页数
volumes_written = 1 # 已写入卷数
主循环:处理所有模板数据
while template_index < len(template_data):
if self.cancel_requested:
break
self.log_message(f"开始第 {pages_written + 1} 页写入", "info")
1. 复制复制页
current_page_ws = self.create_copy_page(source_ws)
2. 表头写入
self.write_header_to_page(current_page_ws, header_array)
3. 初始化当前页的写入位置
current_page_row = 7 # 从第7行开始写入
当前页的变量
current_page_HGhj = 0
循环行写入
while template_index < len(template_data):
if self.cancel_requested:
break
row_data = template_data[template_index]
template_row_num = template_index + 7
D列单元判断
d_content = str(row_data[3] or "").strip()
is_unit_row = self.is_unit_row(d_content)
if is_unit_row:
说明行处理
self.write_unit_row(current_page_ws, current_page_row, d_content)
计算行高并累计(参与行高判断)
row_height = self.calculate_row_height(row_data)
current_page_ws.row_dimensions[current_page_row].height = row_height
current_page_HGhj += row_height
HGhj += row_height
分离判断(参与分离判断)
force_break = force_breaks.get(template_row_num, "")
检查是否写完所有数据(结尾操作)
if template_index == len(template_data) - 1:
结尾操作
wc = self.calculate_wc(yhj, HGhj)
self.perform_end_operation(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
template_index += 1
break
检查是否需要分卷
elif force_break or yhhj > yy:
分卷操作
wc = self.calculate_wc(yhj, HGhj)
self.perform_volume_break(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
volumes_written += 1
重置变量
xh = 1
yhhj = 1
ym = 1
zjh = jh # 更新总卷号
jh += 1
HGhj = 0
template_index += 1
break
检查是否需要分页
elif HGhj >= yhj:
计算wc值
wc = self.calculate_wc(yhj, HGhj)
分页操作 - 传递计算后的wc值
self.perform_page_break(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
更新变量
ym += 1
HGhj = 0
template_index += 1
break
继续当前页
current_page_row += 1
rows_written += 1
template_index += 1
else:
A列判断
a_content = row_data[0]
is_number_row = self.is_number_row(a_content)
if is_number_row:
获取当前行对应的页数 - 使用template_index作为索引
page_increment = 0
if template_index < len(page_numbers):
page_increment = page_numbers[template_index]
计算实际应该显示的页号(当前累计值)
actual_page_number = yhhj
序号行处理
self.write_number_row(
current_page_ws, current_page_row, row_data,
xh, actual_page_number # 传递累加后的页号
)
更新序号
xh += 1
rows_written += 1
计算行高
row_height = self.calculate_row_height(row_data)
设置行高并累计
current_page_ws.row_dimensions[current_page_row].height = row_height
current_page_HGhj += row_height
HGhj += row_height
累加页号到累计值中 - 直接使用template_index作为索引
if template_index < len(page_numbers):
yhhj += page_increment
分离判断
force_break = force_breaks.get(template_row_num, "")
检查是否写完所有数据
if template_index == len(template_data) - 1:
结尾操作
wc = self.calculate_wc(yhj, HGhj)
self.perform_end_operation(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
template_index += 1
break
检查是否需要分卷
elif force_break or yhhj > yy:
分卷操作
wc = self.calculate_wc(yhj, HGhj)
self.perform_volume_break(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
volumes_written += 1
重置变量
xh = 1
yhhj = 1
ym = 1
zjh = jh # 更新总卷号
jh += 1
HGhj = 0
template_index += 1
break
检查是否需要分页
elif HGhj >= yhj:
计算wc值
wc = self.calculate_wc(yhj, HGhj)
分页操作 - 传递计算后的wc值
self.perform_page_break(
current_page_ws, current_page_row,
wc, ym, jh
)
复制当前页到结果文件
self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
result_start_row += self.get_page_height(current_page_ws)
pages_written += 1
更新变量
ym += 1
HGhj = 0
template_index += 1
break
else:
继续当前页
current_page_row += 1
template_index += 1
else:
非数字行,跳过
template_index += 1
更新进度
if template_index % 10 == 0:
progress = 30 + (template_index / total_rows * 60)
self.update_status(f"写入数据: {template_index}/{total_rows}", progress)
self.update_stats(0, rows_written, pages_written, volumes_written)
替换总卷号(在所有数据处理完成后)
遍历整个结果工作表,替换所有zjh占位符为总卷号
for row in result_ws.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
new_value = str(cell.value)
替换zjh(总卷号,不区分大小写)
if 'zjh' in new_value.lower():
new_value = new_value.replace('zjh', str(zjh))
new_value = new_value.replace('ZJH', str(zjh))
new_value = new_value.replace('Zjh', str(zjh))
if new_value != str(cell.value):
cell.value = new_value
更新统计
self.update_stats(0, rows_written, pages_written, volumes_written)
保存文件
output_path = self.save_output_file(work_dir, folder_name, project_name, source_file, result_wb)
self.log_message(f"写入完成: {rows_written}行,{pages_written}页,{volumes_written}卷", "success")
return output_path
except Exception as e:
raise Exception(f"写入程序执行失败: {str(e)}")
def replace_volume_numbers_fixed(self, worksheet, current_jh, total_jh):
"""替换卷号 - 修正版(确保zjh被替换)"""
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
替换jh和zjh(不区分大小写)
new_value = str(cell.value)
先替换zjh(总卷号)
if 'zjh' in new_value.lower():
new_value = new_value.replace('zjh', str(total_jh))
new_value = new_value.replace('ZJH', str(total_jh))
new_value = new_value.replace('Zjh', str(total_jh))
然后替换jh(当前卷号)- 注意避免替换已替换的zjh部分
if 'jh' in new_value.lower():
使用正则表达式确保只替换单独的jh
import re
new_value = re.sub(r'(?<!z)(?<!Z)jh', str(current_jh), new_value, flags=re.IGNORECASE)
if new_value != str(cell.value):
cell.value = new_value
def is_unit_row(self, d_content):
"""判断是否为说明行"""
if not d_content:
return False
字数小于10个字符且包含"单元"
char_count = len(d_content)
if char_count < 10 and "单元" in d_content:
return True
return False
def is_number_row(self, a_content):
"""判断是否为数字行"""
if a_content is None:
return False
try:
float(str(a_content))
return True
except:
return False
def create_copy_page(self, source_ws):
"""创建复制页(安全复制样式和列宽)"""
创建新的工作簿和工作表
copy_wb = Workbook()
copy_ws = copy_wb.active
复制单元格内容和样式
for row in source_ws.iter_rows():
for source_cell in row:
target_cell = copy_ws.cell(
row=source_cell.row,
column=source_cell.column,
value=source_cell.value
)
安全复制样式(避免StyleProxy问题)
self.safe_copy_style(source_cell, target_cell)
复制合并单元格
for merged_range in source_ws.merged_cells.ranges:
copy_ws.merge_cells(str(merged_range))
复制列宽(修正版)
for col in range(1, source_ws.max_column + 1):
col_letter = get_column_letter(col)
if col_letter in source_ws.column_dimensions:
source_col_dim = source_ws.column_dimensions[col_letter]
直接设置目标列的宽度
copy_ws.column_dimensions[col_letter].width = source_col_dim.width
复制行高
for row in range(1, source_ws.max_row + 1):
if row in source_ws.row_dimensions:
source_row_dim = source_ws.row_dimensions[row]
if source_row_dim.height is not None:
copy_ws.row_dimensions[row].height = source_row_dim.height
return copy_ws
def safe_copy_style(self, source_cell, target_cell):
"""安全复制样式(避免StyleProxy和MergedCell问题)"""
try:
检查是否为合并单元格
if hasattr(source_cell, 'merged') and source_cell.merged:
return
复制字体
if hasattr(source_cell, 'font') and source_cell.font:
try:
target_cell.font = Font(
name=source_cell.font.name,
size=source_cell.font.size,
bold=source_cell.font.bold,
italic=source_cell.font.italic,
vertAlign=source_cell.font.vertAlign,
underline=source_cell.font.underline,
strike=source_cell.font.strike,
color=source_cell.font.color
)
except:
如果复制失败,使用默认字体
target_cell.font = Font(name='宋体', size=11)
复制对齐方式
if hasattr(source_cell, 'alignment') and source_cell.alignment:
try:
target_cell.alignment = Alignment(
horizontal=source_cell.alignment.horizontal,
vertical=source_cell.alignment.vertical,
text_rotation=source_cell.alignment.textRotation,
wrap_text=source_cell.alignment.wrapText,
shrink_to_fit=source_cell.alignment.shrinkToFit,
indent=source_cell.alignment.indent
)
except:
target_cell.alignment = Alignment(horizontal='left', vertical='center')
复制边框
if hasattr(source_cell, 'border') and source_cell.border:
try:
border = Border(
left=Side(style=source_cell.border.left.style,
color=source_cell.border.left.color),
right=Side(style=source_cell.border.right.style,
color=source_cell.border.right.color),
top=Side(style=source_cell.border.top.style,
color=source_cell.border.top.color),
bottom=Side(style=source_cell.border.bottom.style,
color=source_cell.border.bottom.color)
)
target_cell.border = border
except:
pass
复制填充
if hasattr(source_cell, 'fill') and source_cell.fill:
try:
target_cell.fill = PatternFill(
fill_type=source_cell.fill.fill_type,
start_color=source_cell.fill.start_color,
end_color=source_cell.fill.end_color
)
except:
pass
except Exception as e:
捕获所有异常,确保即使遇到合并单元格也能继续执行
pass
def write_header_to_page(self, worksheet, header_array):
"""将表头写入复制页"""
header_mapping = [
('C2', 0), ('B3', 1), ('D3', 2), ('F3', 3),
('B4', 4), ('D4', 5), ('F4', 6), ('F5', 7)
]
for cell_addr, idx in header_mapping:
if idx < len(header_array):
cell = worksheet[cell_addr]
保持原格式,只更新值
cell.value = header_array[idx]
def write_unit_row(self, worksheet, row, d_content):
"""写入说明行"""
D列内容
cell_d = worksheet.cell(row=row, column=4)
cell_d.value = d_content
cell_d.font = Font(name='宋体', size=12, bold=True)
cell_d.alignment = Alignment(horizontal='center', vertical='center')
A列和F列为空
worksheet.cell(row=row, column=1, value=None)
worksheet.cell(row=row, column=6, value=None)
行高由主循环计算并设置,这里不再设置
def write_number_row(self, worksheet, row, row_data, xh, actual_page_number):
"""写入序号行 - 修正版"""
A列:序号
cell_a = worksheet.cell(row=row, column=1)
cell_a.value = xh
B-E列:内容
for col in range(2, 6): # B(2)到E(5)
value = row_data[col - 1]
if value is not None:
worksheet.cell(row=row, column=col, value=value)
F列:页号 - 使用实际累加的页号
cell_f = worksheet.cell(row=row, column=6)
cell_f.value = actual_page_number # 关键修复:使用实际页号而不是起始页号
保持原格式(从第7行F列获取参考格式)
ref_cell = worksheet.cell(row=7, column=6)
if ref_cell.alignment:
cell_f.alignment = Alignment(
horizontal=ref_cell.alignment.horizontal,
vertical=ref_cell.alignment.vertical,
wrap_text=ref_cell.alignment.wrapText
)
def calculate_row_height(self, row_data):
"""计算行高"""
D列内容
d_content = str(row_data[3] or "")
d_char_count = self.calculate_char_count(d_content)
B列内容
b_content = str(row_data[1] or "")
b_char_count = self.calculate_char_count(b_content)
计算D列行高
if d_char_count <= 15:
d_height = 25
elif d_char_count <= 30:
d_height = 40
elif d_char_count <= 45:
d_height = 55
else:
levels = math.ceil(d_char_count / 15)
d_height = 15 * levels + 10
计算B列行高
if b_char_count <= 12:
b_height = 25
elif b_char_count <= 17:
b_height = 40
elif b_char_count <= 22:
b_height = 55
else:
levels = math.ceil(b_char_count / 6)
b_height = 10 * levels + 15
取最大值,至少25磅
return max(d_height, b_height, 25)
def calculate_char_count(self, text):
"""计算有效字符数"""
if not text:
return 0
char_count = 0
for char in str(text):
if char.isascii() and char.isalnum():
char_count += 0.5
else:
char_count += 1
return char_count
def calculate_wc(self, yhj, HGhj):
"""计算wc值:(yhj-HGhj)/25的结果四舍五入取整数"""
wc = max(0, round((yhj - HGhj) / 25))
return int(wc)
def get_page_height(self, worksheet):
"""获取工作表的高度(实际使用的行数)"""
从最后一行向上查找,找到第一个有内容的行
for row in range(worksheet.max_row, 0, -1):
for col in range(1, worksheet.max_column + 1):
cell = worksheet.cell(row=row, column=col)
if cell.value is not None:
return row
return 0
def perform_end_operation(self, worksheet, current_row, wc, ym, jh):
"""结尾操作"""
保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式
计算保留的范围:当前行 + 1 到 current_row + wc
计算需要清除的起始行:current_row + wc + 1
delete_start_row = current_row + wc + 1
清除delete_start_row及以下的行内容和格式
for row in range(delete_start_row, worksheet.max_row + 1):
for col in range(1, worksheet.max_column + 1):
cell = worksheet.cell(row=row, column=col)
清除值和格式
cell.value = None
cell.font = Font()
cell.alignment = Alignment()
cell.border = Border()
cell.fill = PatternFill()
写入页码(在写入行的向下wc+1行的F列)
page_row = current_row + wc + 1
page_cell = worksheet.cell(row=page_row, column=6)
page_cell.value = f"第{ym}页"
page_cell.font = Font(name='宋体', size=12)
page_cell.alignment = Alignment(horizontal='center')
worksheet.row_dimensions[page_row].height = 20
替换当前卷的卷号(结尾操作也是当前卷的一部分)
self.replace_volume_numbers_in_worksheet(worksheet, jh)
替换文中的yh占位符为当前页码
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
new_value = str(cell.value)
if 'yh' in new_value.lower():
new_value = new_value.replace('yh', str(ym))
new_value = new_value.replace('YH', str(ym))
new_value = new_value.replace('Yh', str(ym))
if new_value != str(cell.value):
cell.value = new_value
def perform_volume_break(self, worksheet, current_row, wc, ym, jh):
"""分卷操作"""
保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式
计算保留的范围:当前行 + 1 到 current_row + wc
计算需要清除的起始行:current_row + wc + 1
delete_start_row = current_row + wc + 1
清除delete_start_row及以下的行内容和格式
for row in range(delete_start_row, worksheet.max_row + 1):
for col in range(1, worksheet.max_column + 1):
cell = worksheet.cell(row=row, column=col)
清除值和格式
cell.value = None
cell.font = Font()
cell.alignment = Alignment()
cell.border = Border()
cell.fill = PatternFill()
写入页码
page_row = current_row + wc + 1
page_cell = worksheet.cell(row=page_row, column=6)
page_cell.value = f"第{ym}页"
page_cell.font = Font(name='宋体', size=12)
page_cell.alignment = Alignment(horizontal='center')
worksheet.row_dimensions[page_row].height = 20
添加分页符
worksheet.row_breaks.append(Break(id=page_row))
替换卷号(在当前页中)
self.replace_volume_numbers_in_worksheet(worksheet, jh)
替换文中的yh占位符为当前页码
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
new_value = str(cell.value)
if 'yh' in new_value.lower():
new_value = new_value.replace('yh', str(ym))
new_value = new_value.replace('YH', str(ym))
new_value = new_value.replace('Yh', str(ym))
if new_value != str(cell.value):
cell.value = new_value
def perform_page_break(self, worksheet, current_row, wc, ym, jh):
"""分页操作"""
保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式
计算保留的范围:当前行 + 1 到 current_row + wc
计算需要清除的起始行:current_row + wc + 1
delete_start_row = current_row + wc + 1
清除delete_start_row及以下的行内容和格式
for row in range(delete_start_row, worksheet.max_row + 1):
for col in range(1, worksheet.max_column + 1):
cell = worksheet.cell(row=row, column=col)
清除值和格式
cell.value = None
cell.font = Font()
cell.alignment = Alignment()
cell.border = Border()
cell.fill = PatternFill()
写入页码
page_row = current_row + wc + 1
page_cell = worksheet.cell(row=page_row, column=6)
page_cell.value = f"第{ym}页"
page_cell.font = Font(name='宋体', size=12)
page_cell.alignment = Alignment(horizontal='center')
worksheet.row_dimensions[page_row].height = 20
添加分页符
worksheet.row_breaks.append(Break(id=page_row))
替换卷号(在当前页中)
self.replace_volume_numbers_in_worksheet(worksheet, jh)
替换文中的yh占位符为当前页码
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
new_value = str(cell.value)
if 'yh' in new_value.lower():
new_value = new_value.replace('yh', str(ym))
new_value = new_value.replace('YH', str(ym))
new_value = new_value.replace('Yh', str(ym))
if new_value != str(cell.value):
cell.value = new_value
def copy_page_to_result(self, source_ws, target_ws, start_row):
"""将复制页复制到结果文件"""
计算源工作表的行数
source_max_row = source_ws.max_row
source_max_col = source_ws.max_column
复制每一行
for row in range(1, source_max_row + 1):
for col in range(1, source_max_col + 1):
source_cell = source_ws.cell(row=row, column=col)
target_cell = target_ws.cell(row=start_row + row - 1, column=col)
复制值和样式
target_cell.value = source_cell.value
self.safe_copy_style(source_cell, target_cell)
复制合并单元格(需要调整行号)
for merged_range in source_ws.merged_cells.ranges:
解析合并范围
min_row, min_col, max_row, max_col = merged_range.min_row, merged_range.min_col, merged_range.max_row, merged_range.max_col
调整行号
new_min_row = min_row + start_row - 1
new_max_row = max_row + start_row - 1
合并单元格
target_ws.merge_cells(start_row=new_min_row, start_column=min_col,
end_row=new_max_row, end_column=max_col)
复制列宽
for col in range(1, source_ws.max_column + 1):
col_letter = get_column_letter(col)
if col_letter in source_ws.column_dimensions:
source_col_dim = source_ws.column_dimensions[col_letter]
if source_col_dim.width is not None:
target_ws.column_dimensions[col_letter].width = source_col_dim.width
复制行高(调整行号)
for row in range(1, source_ws.max_row + 1):
if row in source_ws.row_dimensions:
source_row_dim = source_ws.row_dimensions[row]
if source_row_dim.height is not None:
target_row = start_row + row - 1
target_ws.row_dimensions[target_row].height = source_row_dim.height
复制分页符(调整行号)
try:
for break_info in source_ws.row_breaks:
检查 break_info 的类型
if isinstance(break_info, tuple):
如果是元组,假设第一个元素是行号
target_row = break_info[0] + start_row - 1
elif hasattr(break_info, 'id'):
如果是对象,使用 id 属性
target_row = break_info.id + start_row - 1
else:
其他情况,跳过
continue
target_ws.row_breaks.append(Break(id=target_row))
except Exception as e:
捕获分页符复制错误,不影响整体处理
pass
def replace_volume_numbers(self, worksheet, jh):
"""替换卷号(在整个工作表中)"""
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
替换jh和zjh(不区分大小写)
new_value = str(cell.value)
new_value = re.sub(r'\bjh\b', str(jh), new_value, flags=re.IGNORECASE)
new_value = re.sub(r'\bzjh\b', str(jh), new_value, flags=re.IGNORECASE)
if new_value != str(cell.value):
cell.value = new_value
def replace_volume_numbers_in_worksheet(self, worksheet, current_jh):
"""替换卷号(在单个工作表中)"""
for row in worksheet.iter_rows():
for cell in row:
if cell.value and isinstance(cell.value, str):
替换jh(当前卷号,不区分大小写)
new_value = str(cell.value)
new_value = re.sub(r'\bjh\b', str(current_jh), new_value, flags=re.IGNORECASE)
if new_value != str(cell.value):
cell.value = new_value
def save_output_file(self, work_dir, folder_name, project_name, source_file, result_wb):
"""保存输出文件"""
清理文件夹名称
clean_folder_name = re.sub(r'[<>:"/\\|?*]', '_', folder_name)
clean_folder_name = clean_folder_name.strip()
创建输出文件夹
output_dir = os.path.join(work_dir, clean_folder_name)
counter = 1
original_dir = output_dir
while os.path.exists(output_dir):
output_dir = f"{original_dir}_{counter}"
counter += 1
os.makedirs(output_dir, exist_ok=True)
生成文件名:待复制文件名称(模板C2内容)
source_name = os.path.splitext(os.path.basename(source_file))[0]
result_name = f"{source_name}({project_name}).xlsx"
result_path = os.path.join(output_dir, result_name)
处理文件名冲突
counter = 1
base_name, ext = os.path.splitext(result_name)
while os.path.exists(result_path):
result_path = os.path.join(output_dir, f"{base_name}_{counter}{ext}")
counter += 1
保存文件
result_wb.save(result_path)
self.log_message(f"文件已保存到: {result_path}", "success")
return result_path
def main():
"""主函数"""
root = tk.Tk()
设置窗口居中
window_width = 1000
window_height = 400
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
center_x = int(screen_width / 2 - window_width / 2)
center_y = int(screen_height / 2 - window_height / 2)
root.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')
设置最小尺寸
root.minsize(800, 600)
创建应用
app = ProfessionalDirectoryGenerator(root)
运行主循环
root.mainloop()
if name == "main":
main()