以下是一个完整的Python Tkinter应用程序,用于批量将ZIP文件中的CSV文件转换为Excel格式:
![图片[1]_Python Tkinter实现将ZIP中的CSV批量转换为Excel_知途无界](https://zhituwujie.com/wp-content/uploads/2026/01/d2b5ca33bd20260121105641.png)
完整代码实现
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import zipfile
import os
import pandas as pd
from pathlib import Path
import threading
import tempfile
import shutil
class CSVtoExcelConverter:
def __init__(self, root):
self.root = root
self.root.title("ZIP CSV批量转Excel工具")
self.root.geometry("800x600")
self.root.resizable(True, True)
# 变量初始化
self.zip_files = []
self.output_dir = ""
self.is_converting = False
self.setup_ui()
def setup_ui(self):
"""设置用户界面"""
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 配置网格权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
# 标题
title_label = ttk.Label(main_frame, text="ZIP CSV批量转Excel工具",
font=("Arial", 16, "bold"))
title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
# ZIP文件选择区域
ttk.Label(main_frame, text="ZIP文件:", font=("Arial", 10, "bold")).grid(
row=1, column=0, sticky=tk.W, pady=5)
# ZIP文件列表框
self.zip_listbox = tk.Listbox(main_frame, height=6, selectmode=tk.EXTENDED)
self.zip_listbox.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S),
padx=(0, 10), pady=5)
# 滚动条
zip_scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.zip_listbox.yview)
zip_scrollbar.grid(row=2, column=2, sticky=(tk.N, tk.S), pady=5)
self.zip_listbox.configure(yscrollcommand=zip_scrollbar.set)
# 按钮区域
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=3, column=0, columnspan=3, pady=10)
ttk.Button(button_frame, text="添加ZIP文件", command=self.add_zip_files).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="移除选中", command=self.remove_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="清空列表", command=self.clear_all).pack(side=tk.LEFT, padx=5)
# 输出目录选择
ttk.Label(main_frame, text="输出目录:", font=("Arial", 10, "bold")).grid(
row=4, column=0, sticky=tk.W, pady=(20, 5))
self.output_var = tk.StringVar()
output_entry = ttk.Entry(main_frame, textvariable=self.output_var, width=50)
output_entry.grid(row=5, column=0, sticky=(tk.W, tk.E), padx=(0, 10), pady=5)
ttk.Button(main_frame, text="浏览...", command=self.select_output_dir).grid(
row=5, column=1, sticky=tk.W, pady=5)
# 选项设置
options_frame = ttk.LabelFrame(main_frame, text="转换选项", padding="10")
options_frame.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=20)
options_frame.columnconfigure(1, weight=1)
# Excel引擎选择
ttk.Label(options_frame, text="Excel引擎:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
self.engine_var = tk.StringVar(value="openpyxl")
engine_combo = ttk.Combobox(options_frame, textvariable=self.engine_var,
values=["openpyxl", "xlsxwriter"], state="readonly", width=15)
engine_combo.grid(row=0, column=1, sticky=tk.W, padx=(0, 20))
# 编码选择
ttk.Label(options_frame, text="CSV编码:").grid(row=0, column=2, sticky=tk.W, padx=(0, 10))
self.encoding_var = tk.StringVar(value="utf-8")
encoding_combo = ttk.Combobox(options_frame, textvariable=self.encoding_var,
values=["utf-8", "gbk", "gb2312", "latin-1"],
state="readonly", width=15)
encoding_combo.grid(row=0, column=3, sticky=tk.W)
# 包含表头选项
self.header_var = tk.BooleanVar(value=True)
ttk.Checkbutton(options_frame, text="包含表头", variable=self.header_var).grid(
row=1, column=0, sticky=tk.W, pady=(10, 0))
# 分隔符选项
ttk.Label(options_frame, text="分隔符:").grid(row=1, column=1, sticky=tk.W, padx=(20, 10))
self.separator_var = tk.StringVar(value=",")
separator_combo = ttk.Combobox(options_frame, textvariable=self.separator_var,
values=[",", ";", "\t", "|"], state="readonly", width=5)
separator_combo.grid(row=1, column=2, sticky=tk.W)
# 进度条
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
self.progress_bar.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
# 状态标签
self.status_var = tk.StringVar(value="就绪")
status_label = ttk.Label(main_frame, textvariable=self.status_var, foreground="blue")
status_label.grid(row=8, column=0, columnspan=3, pady=5)
# 日志区域
ttk.Label(main_frame, text="转换日志:", font=("Arial", 10, "bold")).grid(
row=9, column=0, columnspan=3, sticky=tk.W, pady=(20, 5))
self.log_text = scrolledtext.ScrolledText(main_frame, height=8, width=70)
self.log_text.grid(row=10, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
# 操作按钮
action_frame = ttk.Frame(main_frame)
action_frame.grid(row=11, column=0, columnspan=3, pady=20)
self.convert_btn = ttk.Button(action_frame, text="开始转换", command=self.start_conversion)
self.convert_btn.pack(side=tk.LEFT, padx=10)
ttk.Button(action_frame, text="退出", command=self.root.quit).pack(side=tk.LEFT, padx=10)
# 配置行权重
main_frame.rowconfigure(2, weight=1)
main_frame.rowconfigure(10, weight=1)
def add_zip_files(self):
"""添加ZIP文件"""
files = filedialog.askopenfilenames(
title="选择ZIP文件",
filetypes=[("ZIP files", "*.zip"), ("All files", "*.*")]
)
if files:
for file in files:
if file not in self.zip_files:
self.zip_files.append(file)
self.zip_listbox.insert(tk.END, os.path.basename(file))
self.log(f"添加了 {len(files)} 个ZIP文件")
def remove_selected(self):
"""移除选中的ZIP文件"""
selected = self.zip_listbox.curselection()
if selected:
for index in reversed(selected):
self.zip_files.pop(index)
self.zip_listbox.delete(index)
self.log(f"移除了 {len(selected)} 个文件")
def clear_all(self):
"""清空所有ZIP文件"""
self.zip_files.clear()
self.zip_listbox.delete(0, tk.END)
self.log("已清空文件列表")
def select_output_dir(self):
"""选择输出目录"""
directory = filedialog.askdirectory(title="选择输出目录")
if directory:
self.output_dir = directory
self.output_var.set(directory)
self.log(f"输出目录设置为: {directory}")
def log(self, message):
"""添加日志消息"""
self.log_text.insert(tk.END, f"{message}\n")
self.log_text.see(tk.END)
self.root.update_idletasks()
def update_status(self, message):
"""更新状态"""
self.status_var.set(message)
self.root.update_idletasks()
def start_conversion(self):
"""开始转换(在单独线程中执行)"""
if self.is_converting:
return
if not self.zip_files:
messagebox.showerror("错误", "请先添加ZIP文件!")
return
if not self.output_dir:
messagebox.showerror("错误", "请先选择输出目录!")
return
# 在新线程中执行转换
thread = threading.Thread(target=self.convert_files)
thread.daemon = True
thread.start()
def convert_files(self):
"""执行文件转换"""
self.is_converting = True
self.convert_btn.config(state='disabled')
self.progress_var.set(0)
try:
total_files = len(self.zip_files)
processed_files = 0
for zip_path in self.zip_files:
if not self.is_converting: # 检查是否被取消
break
zip_name = os.path.basename(zip_path)
self.log(f"\n处理文件: {zip_name}")
self.update_status(f"正在处理: {zip_name}")
try:
# 创建临时目录
with tempfile.TemporaryDirectory() as temp_dir:
# 解压ZIP文件
extracted_count = self.extract_and_convert(zip_path, temp_dir)
if extracted_count > 0:
self.log(f"✓ 成功转换 {extracted_count} 个CSV文件")
else:
self.log(f"⚠ 未在ZIP中找到CSV文件")
except Exception as e:
self.log(f"✗ 处理失败: {str(e)}")
# 更新进度
processed_files += 1
progress = (processed_files / total_files) * 100
self.progress_var.set(progress)
if self.is_converting:
self.log(f"\n🎉 转换完成!共处理 {total_files} 个ZIP文件")
self.update_status("转换完成")
messagebox.showinfo("完成", "所有文件转换完成!")
else:
self.log("\n❌ 转换已取消")
self.update_status("转换已取消")
except Exception as e:
self.log(f"❌ 转换过程出错: {str(e)}")
self.update_status("转换失败")
messagebox.showerror("错误", f"转换过程中发生错误:\n{str(e)}")
finally:
self.is_converting = False
self.convert_btn.config(state='normal')
self.progress_var.set(0)
def extract_and_convert(self, zip_path, temp_dir):
"""解压ZIP并转换其中的CSV文件"""
converted_count = 0
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
# 获取所有CSV文件
csv_files = [f for f in zip_ref.namelist() if f.lower().endswith('.csv')]
if not csv_files:
return converted_count
# 解压所有CSV文件
zip_ref.extractall(temp_dir, csv_files)
# 转换每个CSV文件
for csv_file in csv_files:
csv_path = os.path.join(temp_dir, csv_file)
# 保持目录结构
relative_path = os.path.dirname(csv_file)
if relative_path:
output_subdir = os.path.join(self.output_dir, relative_path)
os.makedirs(output_subdir, exist_ok=True)
else:
output_subdir = self.output_dir
# 生成Excel文件名
excel_filename = Path(csv_file).stem + '.xlsx'
excel_path = os.path.join(output_subdir, excel_filename)
try:
# 读取CSV文件
df = pd.read_csv(
csv_path,
encoding=self.encoding_var.get(),
sep=self.separator_var.get(),
header=0 if self.header_var.get() else None
)
# 保存为Excel
engine = self.engine_var.get()
if engine == "openpyxl":
df.to_excel(excel_path, index=False, engine='openpyxl')
else: # xlsxwriter
df.to_excel(excel_path, index=False, engine='xlsxwriter')
converted_count += 1
self.log(f" ✓ {os.path.basename(csv_file)} → {excel_filename}")
except Exception as e:
self.log(f" ✗ 转换失败 {os.path.basename(csv_file)}: {str(e)}")
return converted_count
def main():
root = tk.Tk()
app = CSVtoExcelConverter(root)
# 设置图标(如果有的话)
try:
root.iconbitmap('icon.ico') # 可选:添加应用图标
except:
pass
# 居中显示窗口
root.update_idletasks()
x = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
y = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
root.geometry(f"+{x}+{y}")
root.mainloop()
if __name__ == "__main__":
main()
安装依赖
在运行程序前,需要安装以下Python包:
pip install pandas openpyxl xlsxwriter tkinter
主要功能特点
1. 直观的图形界面
- 文件列表管理(添加、移除、清空)
- 输出目录选择
- 实时进度显示
- 详细的操作日志
2. 灵活的转换选项
- 支持多种Excel引擎(openpyxl/xlsxwriter)
- 可配置CSV编码(UTF-8/GBK/GB2312/Latin-1)
- 自定义分隔符(逗号/分号/制表符/竖线)
- 可选择是否包含表头
3. 强大的处理能力
- 批量处理多个ZIP文件
- 保持原始目录结构
- 自动跳过非CSV文件
- 错误处理和详细日志
4. 用户体验优化
- 多线程处理,界面不卡顿
- 实时状态更新
- 完成后消息提示
- 窗口居中显示
使用步骤
- 添加ZIP文件:点击”添加ZIP文件”按钮选择要处理的压缩包
- 选择输出目录:点击”浏览…”选择转换后的Excel文件保存位置
- 设置选项:根据需要调整编码、分隔符等参数
- 开始转换:点击”开始转换”按钮,程序会自动处理所有文件
- 查看结果:在日志区域查看处理进度和结果
注意事项
- 确保系统中已安装所需依赖包
- 对于大型ZIP文件,转换可能需要一些时间
- 建议选择合适的CSV编码以避免乱码问题
- 程序会自动创建必要的子目录来保持文件组织结构
这个工具特别适合需要处理大量ZIP压缩包中CSV数据的场景,如数据分析、报表整理等工作。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容