win10程序(十五)归档文件的xlsx目录自动分卷

'''

档案归档是否希望自动根据页数分盒归档?本文讲述能自动分卷来实现,分盒后序号、页号都自动从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()

相关推荐
喵手2 小时前
Python爬虫零基础入门【第九章:实战项目教学·第8节】限速器进阶:令牌桶 + 动态降速(429/5xx)!
爬虫·python·令牌桶·python爬虫工程化实战·python爬虫零基础入门·限速器·动态降速
深度学习lover2 小时前
<项目代码>yolo毛毛虫识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·毛毛虫识别
喵手2 小时前
Python爬虫零基础入门【第九章:实战项目教学·第3节】通用清洗工具包:日期/金额/单位/空值(可复用)!
爬虫·python·python爬虫实战·python爬虫工程化实战·python爬虫零基础入门·通用清洗工具包·爬虫实战项目
b2077212 小时前
Flutter for OpenHarmony 身体健康状况记录App实战 - 体重趋势实现
python·flutter·harmonyos
喵手2 小时前
Python爬虫零基础入门【第九章:实战项目教学·第4节】质量报告自动生成:缺失率/重复率/异常值 TopN!
爬虫·python·爬虫实战·python爬虫工程化实战·零基础python爬虫教学·实战项目教学·质量报告自动生成
b2077212 小时前
Flutter for OpenHarmony 身体健康状况记录App实战 - 个人中心实现
android·java·python·flutter·harmonyos
喵手2 小时前
Python爬虫零基础入门【第九章:实战项目教学·第7节】增量采集:last_time / last_id 两种策略各做一遍!
爬虫·python·爬虫实战·python爬虫工程化实战·零基础python爬虫教学·增量采集·策略采集
子午2 小时前
【2026计算机毕设】水果识别分类系统~python+深度学习+人工智能+算法模型+TensorFlow
人工智能·python·深度学习
No0d1es2 小时前
2023年NOC大赛创客智慧编程赛项Python复赛模拟题(二)
python·青少年编程·noc·复赛·模拟题