import locale
locale.setlocale(locale.LC_ALL, 'chinese')
import tkinter as tk
from tkinter import filedialog, messagebox, ttk, scrolledtext
import csv
from datetime import datetime
import logging
import os
from collections import defaultdict
class CSVProcessorApp:
def __init__(self, root):
self.root = root
self.root.title("CSV_ProcessPro")
self.root.geometry("800x600") # 调整窗口大小
self.root.resizable(False, False) # 禁止调整窗口大小
# 初始化变量
self.file_path = tk.StringVar()
self.csv_data = []
self.headers = []
self.setup_variables()
# 初始化日志
self.setup_logging()
# 创建UI
self.create_widgets()
# 设置全局样式
self.setup_styles()
def setup_styles(self):
"""设置全局样式"""
self.style = ttk.Style()
self.style.configure("TFrame", background="#f0f0f0")
self.style.configure("TLabel", background="#f0f0f0", font=('Arial', 9))
self.style.configure("TButton", font=('Arial', 9, 'bold'))
self.style.configure("Accent.TButton", foreground="black", font=('Arial', 9, 'bold'),
borderwidth=2, relief="raised")
self.style.map("Accent.TButton",
background=[("active", "#4a90e2"), ("!active", "#d4e6ff")],
bordercolor=[("active", "#4a90e2"), ("!active", "#ffcc00")])
self.style.configure("Remove.TButton", foreground="black", font=('Arial', 8),
background="#ffcccc", borderwidth=1, relief="solid")
self.style.map("Remove.TButton",
background=[("active", "#ff9999"), ("!active", "#ffcccc")])
def setup_variables(self):
"""初始化所有动态变量"""
# 排序相关
self.sort_header = tk.StringVar()
self.sort_order = tk.StringVar(value="升序")
# 去重相关
self.dedupe_header = tk.StringVar()
# 删除行相关
self.delete_keyword = tk.StringVar()
self.delete_column = tk.StringVar()
self.delete_case_sensitive = tk.BooleanVar()
# 合并文件相关
self.merge_file_paths = []
self.merge_column = tk.StringVar()
# 状态变量
self.enable_sort = tk.BooleanVar()
self.enable_dedupe = tk.BooleanVar()
self.enable_custom_letter_sort = tk.BooleanVar()
self.letter_range_start = tk.StringVar(value="A")
self.letter_range_end = tk.StringVar(value="Z")
def setup_logging(self):
"""配置日志记录"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('csv_processor.log', encoding='utf-8'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
self.logger.info("===== 程序启动 =====")
def create_widgets(self):
"""创建所有界面组件"""
# 主容器
main_container = ttk.Frame(self.root, padding=5)
main_container.pack(fill=tk.BOTH, expand=True)
# 使用notebook分页组织功能
self.notebook = ttk.Notebook(main_container)
self.notebook.pack(fill=tk.BOTH, expand=True)
# 创建各个标签页
self.create_file_tab()
self.create_process_tab()
self.create_delete_tab()
self.create_merge_tab()
self.create_log_tab()
def create_file_tab(self):
"""创建文件操作标签页"""
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="文件操作")
# 文件选择部分
frame = ttk.LabelFrame(tab, text="CSV文件选择", padding=10)
frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(frame, text="文件路径:").grid(row=0, column=0, sticky=tk.W)
ttk.Entry(frame, textvariable=self.file_path, width=40).grid(row=0, column=1, sticky=tk.EW)
ttk.Button(frame, text="浏览", command=self.select_file).grid(row=0, column=2, padx=5)
# 文件信息显示
self.file_info = scrolledtext.ScrolledText(tab, height=8, width=80)
self.file_info.pack(fill=tk.X, padx=5, pady=5)
def create_process_tab(self):
"""创建数据处理标签页"""
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="排序/去重")
# 排序选项部分
frame = ttk.LabelFrame(tab, text="排序选项", padding=10)
frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Checkbutton(frame, text="启用排序", variable=self.enable_sort,
command=self.toggle_sort).grid(row=0, column=0, sticky=tk.W)
ttk.Label(frame, text="排序表头:").grid(row=1, column=0, sticky=tk.W)
self.sort_header_combobox = ttk.Combobox(frame, textvariable=self.sort_header, state="readonly")
self.sort_header_combobox.grid(row=1, column=1, sticky=tk.EW)
ttk.Label(frame, text="排序方式:").grid(row=2, column=0, sticky=tk.W)
self.sort_order_combobox = ttk.Combobox(frame, textvariable=self.sort_order,
values=["升序", "降序", "自定义字母排序"])
self.sort_order_combobox.grid(row=2, column=1, sticky=tk.W)
# 自定义字母排序范围
ttk.Checkbutton(frame, text="启用字母范围过滤",
variable=self.enable_custom_letter_sort,
command=self.toggle_letter_sort).grid(row=3, column=0, sticky=tk.W)
ttk.Label(frame, text="字母范围:").grid(row=4, column=0, sticky=tk.W)
ttk.Entry(frame, textvariable=self.letter_range_start, width=5).grid(row=4, column=1, sticky=tk.W)
ttk.Label(frame, text="到").grid(row=4, column=2)
ttk.Entry(frame, textvariable=self.letter_range_end, width=5).grid(row=4, column=3, sticky=tk.W)
# 去重选项部分
frame = ttk.LabelFrame(tab, text="去重选项", padding=10)
frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Checkbutton(frame, text="启用去重", variable=self.enable_dedupe,
command=self.toggle_dedupe).grid(row=0, column=0, sticky=tk.W)
ttk.Label(frame, text="去重表头:").grid(row=1, column=0, sticky=tk.W)
self.dedupe_header_combobox = ttk.Combobox(frame, textvariable=self.dedupe_header, state="readonly")
self.dedupe_header_combobox.grid(row=1, column=1, sticky=tk.EW)
# 处理按钮
btn_frame = ttk.Frame(tab)
btn_frame.pack(pady=10)
ttk.Button(btn_frame, text="处理并保存到桌面", command=self.process_csv,
style="Accent.TButton").pack()
def create_delete_tab(self):
"""创建删除行标签页"""
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="删除行")
frame = ttk.LabelFrame(tab, text="删除包含指定字符的行", padding=10)
frame.pack(fill=tk.X, padx=5, pady=5)
# 删除条件设置
ttk.Label(frame, text="搜索列:").grid(row=0, column=0, sticky=tk.W)
self.delete_column_combobox = ttk.Combobox(frame, textvariable=self.delete_column, state="readonly")
self.delete_column_combobox.grid(row=0, column=1, sticky=tk.EW)
ttk.Label(frame, text="关键字:").grid(row=1, column=0, sticky=tk.W)
ttk.Entry(frame, textvariable=self.delete_keyword).grid(row=1, column=1, sticky=tk.EW)
ttk.Checkbutton(frame, text="区分大小写", variable=self.delete_case_sensitive).grid(row=2, column=0, sticky=tk.W)
# 执行按钮
btn_frame = ttk.Frame(tab)
btn_frame.pack(pady=10)
ttk.Button(btn_frame, text="执行删除并保存到桌面", command=self.delete_rows_with_keyword,
style="Accent.TButton").pack()
def create_merge_tab(self):
"""创建文件合并标签页"""
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="文件合并")
# 合并文件部分
frame = ttk.LabelFrame(tab, text="合并CSV文件", padding=10)
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 文件列表容器
list_frame = ttk.Frame(frame)
list_frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(list_frame, text="已选择文件:").grid(row=0, column=0, sticky=tk.W)
# 文件列表和滚动条
self.merge_file_canvas = tk.Canvas(list_frame, height=150)
self.merge_file_canvas.grid(row=1, column=0, sticky=tk.EW)
scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=self.merge_file_canvas.yview)
scrollbar.grid(row=1, column=1, sticky=tk.NS)
self.merge_file_canvas.configure(yscrollcommand=scrollbar.set)
self.merge_file_frame = ttk.Frame(self.merge_file_canvas)
self.merge_file_canvas.create_window((0, 0), window=self.merge_file_frame, anchor="nw")
# 按钮区域
btn_frame = ttk.Frame(frame)
btn_frame.pack(fill=tk.X, pady=5)
ttk.Button(btn_frame, text="添加文件", command=self.add_merge_file).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="清空列表", command=self.clear_merge_list).pack(side=tk.LEFT, padx=5)
# 合并选项
opt_frame = ttk.Frame(frame)
opt_frame.pack(fill=tk.X, pady=5)
ttk.Label(opt_frame, text="合并依据列(可选):").grid(row=0, column=0, sticky=tk.W)
self.merge_column_combo = ttk.Combobox(opt_frame, textvariable=self.merge_column, state="readonly")
self.merge_column_combo.grid(row=0, column=1, sticky=tk.EW)
# 合并按钮
btn_frame = ttk.Frame(tab)
btn_frame.pack(pady=10)
ttk.Button(btn_frame, text="执行合并并保存到桌面", command=self.merge_csv_files,
style="Accent.TButton").pack()
def create_log_tab(self):
"""创建日志标签页"""
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="运行日志")
self.log_text = scrolledtext.ScrolledText(tab, height=15, width=80)
self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
def log_message(self, message, level="info"):
"""记录日志并显示在GUI中"""
log_methods = {
"info": self.logger.info,
"error": self.logger.error,
"warning": self.logger.warning
}
# 记录到日志文件
log_methods.get(level, self.logger.info)(message)
# 显示在GUI日志标签页
timestamp = datetime.now().strftime("%H:%M:%S")
tagged_msg = f"[{timestamp}] {message}"
self.log_text.insert(tk.END, tagged_msg + "\n")
self.log_text.see(tk.END)
# 同时在文件信息标签页显示重要信息
if level in ["error", "warning"]:
self.file_info.config(state=tk.NORMAL)
self.file_info.insert(tk.END, tagged_msg + "\n")
self.file_info.config(state=tk.DISABLED)
self.file_info.see(tk.END)
def select_file(self):
"""选择CSV文件"""
file_path = filedialog.askopenfilename(
title="选择CSV文件",
filetypes=[("CSV文件", "*.csv"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_path:
self.file_path.set(file_path)
self.log_message(f"已选择文件: {file_path}")
self.load_csv(file_path)
def load_csv(self, file_path):
"""加载CSV文件内容"""
try:
with open(file_path, 'r', encoding='utf-8-sig') as file:
reader = csv.reader(file)
self.csv_data = list(reader)
if len(self.csv_data) > 0:
self.headers = self.csv_data[0]
self.update_ui_with_headers()
# 显示文件信息
self.show_file_info(file_path)
self.log_message(f"文件加载成功,共 {len(self.csv_data)} 行")
else:
raise ValueError("文件为空或格式不正确")
except Exception as e:
error_msg = f"读取CSV文件失败: {str(e)}"
self.log_message(error_msg, "error")
messagebox.showerror("错误", error_msg)
def show_file_info(self, file_path):
"""显示文件信息"""
self.file_info.config(state=tk.NORMAL)
self.file_info.delete(1.0, tk.END)
info = [
f"文件路径: {file_path}",
f"总行数: {len(self.csv_data)}",
f"列数: {len(self.headers)}",
f"表头: {', '.join(self.headers)}",
"="*40,
"前5行数据预览:"
]
self.file_info.insert(tk.END, "\n".join(info) + "\n")
# 显示前5行数据
for i, row in enumerate(self.csv_data[:5], 1):
self.file_info.insert(tk.END, f"{i}. {', '.join(row)}\n")
self.file_info.config(state=tk.DISABLED)
def update_ui_with_headers(self):
"""根据加载的CSV更新UI元素"""
# 更新所有下拉框
for combo in [
self.sort_header_combobox,
self.dedupe_header_combobox,
self.delete_column_combobox,
self.merge_column_combo
]:
combo['values'] = self.headers
# 设置默认值
if self.headers:
self.sort_header.set(self.headers[0])
self.dedupe_header.set(self.headers[0])
self.delete_column.set(self.headers[0])
self.merge_column.set("")
def toggle_sort(self):
"""切换排序功能的启用状态"""
state = "normal" if self.enable_sort.get() else "disabled"
self.sort_header_combobox['state'] = state
self.sort_order_combobox['state'] = state
self.toggle_letter_sort()
self.log_message(f"排序功能 {'启用' if self.enable_sort.get() else '禁用'}")
def toggle_dedupe(self):
"""切换去重功能的启用状态"""
state = "normal" if self.enable_dedupe.get() else "disabled"
self.dedupe_header_combobox['state'] = state
self.log_message(f"去重功能 {'启用' if self.enable_dedupe.get() else '禁用'}")
def toggle_letter_sort(self):
"""控制字母范围输入框的启用状态"""
if not self.enable_sort.get():
return
state = "normal" if self.enable_custom_letter_sort.get() else "disabled"
self.letter_range_start_entry['state'] = state
self.letter_range_end_entry['state'] = state
self.log_message(f"字母范围过滤 {'启用' if self.enable_custom_letter_sort.get() else '禁用'}")
def add_merge_file(self):
"""添加要合并的文件"""
file_paths = filedialog.askopenfilenames(
title="选择要合并的CSV文件",
filetypes=[("CSV文件", "*.csv"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_paths:
for path in file_paths:
if path not in self.merge_file_paths:
self.merge_file_paths.append(path)
self.update_merge_file_list()
def clear_merge_list(self):
"""清空合并文件列表"""
if self.merge_file_paths:
self.merge_file_paths = []
self.update_merge_file_list()
self.log_message("已清空合并文件列表")
def update_merge_file_list(self):
"""更新合并文件列表显示"""
# 清除现有内容
for widget in self.merge_file_frame.winfo_children():
widget.destroy()
if not self.merge_file_paths:
ttk.Label(self.merge_file_frame, text="尚未选择任何文件").pack()
self.merge_file_canvas.configure(scrollregion=self.merge_file_canvas.bbox("all"))
return
# 添加文件列表
for i, path in enumerate(self.merge_file_paths):
row_frame = ttk.Frame(self.merge_file_frame)
row_frame.pack(fill=tk.X, pady=2)
ttk.Label(row_frame, text=f"{i+1}. {os.path.basename(path)}", width=40, anchor="w").pack(side=tk.LEFT)
ttk.Button(row_frame, text="移除", command=lambda p=path: self.remove_merge_file(p),
style="Remove.TButton").pack(side=tk.LEFT, padx=2)
# 更新滚动区域
self.merge_file_frame.update_idletasks()
self.merge_file_canvas.configure(scrollregion=self.merge_file_canvas.bbox("all"))
def remove_merge_file(self, file_path):
"""移除指定的合并文件"""
if file_path in self.merge_file_paths:
self.merge_file_paths.remove(file_path)
self.update_merge_file_list()
self.log_message(f"已移除文件: {file_path}")
def delete_rows_with_keyword(self):
"""删除包含关键字的行并保存到桌面"""
if not self.file_path.get():
messagebox.showwarning("警告", "请先选择CSV文件")
return
column = self.delete_column.get()
keyword = self.delete_keyword.get()
if not column:
messagebox.showwarning("警告", "请选择要搜索的列")
return
if not keyword:
messagebox.showwarning("警告", "请输入要搜索的关键字")
return
try:
col_index = self.headers.index(column)
case_sensitive = self.delete_case_sensitive.get()
if not case_sensitive:
keyword = keyword.lower()
new_data = [self.headers]
deleted_count = 0
for row in self.csv_data[1:]:
if len(row) > col_index:
value = row[col_index]
compare_value = value if case_sensitive else value.lower()
if keyword not in compare_value:
new_data.append(row)
else:
deleted_count += 1
if deleted_count == 0:
messagebox.showinfo("信息", "没有找到包含关键字的行")
return
# 生成保存路径
operation = f"deleted_{keyword}"
save_path = self.generate_filename(self.file_path.get(), operation)
# 保存文件
self.save_csv_file(new_data, save_path)
# 更新当前数据
self.csv_data = new_data
self.log_message(f"已删除 {deleted_count} 行包含关键字 '{keyword}' 的数据")
messagebox.showinfo("成功", f"已删除 {deleted_count} 行,结果已保存到桌面:\n{os.path.basename(save_path)}")
except Exception as e:
error_msg = f"删除行时出错: {str(e)}"
self.log_message(error_msg, "error")
messagebox.showerror("错误", error_msg)
def get_desktop_path(self):
"""获取桌面路径"""
try:
desktop = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')
if os.path.exists(desktop):
return desktop
except KeyError:
pass
# 如果上面的方法失败,尝试其他方法
desktop = os.path.join(os.path.expanduser('~'), 'Desktop')
if os.path.exists(desktop):
return desktop
# 如果还是失败,返回当前目录
return os.getcwd()
def generate_filename(self, original_name, operation):
"""生成新的文件名"""
if not original_name:
original_name = "processed"
base = os.path.basename(original_name)
name, ext = os.path.splitext(base)
# 清理操作名称中的特殊字符
clean_op = "".join(c if c.isalnum() else "_" for c in operation)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
new_name = f"{name}_{clean_op}_{timestamp}{ext}"
return os.path.join(self.get_desktop_path(), new_name)
def save_csv_file(self, data, save_path):
"""保存CSV文件到指定路径"""
try:
with open(save_path, 'w', encoding='utf-8-sig', newline='') as file:
writer = csv.writer(file)
writer.writerows(data)
# 更新文件信息显示
self.show_file_info(save_path)
self.log_message(f"文件已保存到: {save_path}")
return True
except Exception as e:
error_msg = f"保存文件时出错: {str(e)}"
self.log_message(error_msg, "error")
messagebox.showerror("错误", error_msg)
return False
def process_csv(self):
"""处理CSV文件(排序、去重等)并保存到桌面"""
if not self.file_path.get():
messagebox.showwarning("警告", "请先选择CSV文件")
return
if not self.csv_data:
messagebox.showwarning("警告", "CSV文件没有数据")
return
self.log_message("开始处理CSV文件...")
new_data = [self.headers.copy()]
data_rows = self.csv_data[1:]
# 去重处理
if self.enable_dedupe.get():
try:
dedupe_index = self.headers.index(self.dedupe_header.get())
seen = set()
unique_rows = []
for row in data_rows:
if len(row) > dedupe_index:
key = row[dedupe_index]
if key not in seen:
seen.add(key)
unique_rows.append(row)
data_rows = unique_rows
self.log_message(f"去重完成,根据表头 '{self.dedupe_header.get()}' 删除重复项")
except Exception as e:
self.log_message(f"去重时出错: {str(e)}", "error")
messagebox.showerror("错误", f"去重时出错: {str(e)}")
return
# 排序处理
if self.enable_sort.get():
try:
sort_index = self.headers.index(self.sort_header.get())
order = self.sort_order.get()
reverse = (order == "降序")
# 字母范围过滤
if self.enable_custom_letter_sort.get():
try:
letter_start = self.letter_range_start.get().upper()
letter_end = self.letter_range_end.get().upper()
if not (len(letter_start) == 1 and len(letter_end) == 1 and
letter_start.isalpha() and letter_end.isalpha()):
raise ValueError("字母范围必须是单个字母(如A-Z)")
filtered_rows = []
for row in data_rows:
if len(row) > sort_index:
value = str(row[sort_index]).strip().upper()
if value and letter_start <= value[0] <= letter_end:
filtered_rows.append(row)
data_rows = filtered_rows
self.log_message(f"字母范围过滤完成:{letter_start} 到 {letter_end}")
except Exception as e:
self.log_message(f"字母范围过滤失败: {str(e)}", "error")
messagebox.showerror("错误", f"字母范围过滤失败: {str(e)}")
return
# 排序逻辑
def sort_key(row):
if len(row) > sort_index:
value = row[sort_index]
# 尝试解析为日期
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d", "%m/%d/%Y", "%Y.%m.%d"):
try:
return datetime.strptime(value, fmt)
except ValueError:
continue
# 尝试解析为数字
try:
return float(value)
except ValueError:
pass
return value.lower() # 默认按字符串排序
return ""
try:
if order == "自定义字母排序":
data_rows.sort(key=lambda x: str(sort_key(x)).lower() if len(x) > sort_index else "",
reverse=False)
else:
data_rows.sort(key=sort_key, reverse=reverse)
self.log_message(f"排序完成,表头 '{self.sort_header.get()}',顺序: {order}")
except Exception as e:
self.log_message(f"排序时出错: {str(e)}", "error")
messagebox.showerror("错误", f"排序时出错: {str(e)}")
return
except Exception as e:
self.log_message(f"排序处理时出错: {str(e)}", "error")
messagebox.showerror("错误", f"排序处理时出错: {str(e)}")
return
# 组合最终数据
new_data.extend(data_rows)
# 生成操作描述
operations = []
if self.enable_sort.get():
operations.append(f"sorted_{self.sort_header.get()}_{self.sort_order.get()}")
if self.enable_dedupe.get():
operations.append(f"deduped_{self.dedupe_header.get()}")
operation = "_".join(operations) if operations else "processed"
# 生成保存路径
save_path = self.generate_filename(self.file_path.get(), operation)
# 保存文件
if self.save_csv_file(new_data, save_path):
# 更新当前数据
self.csv_data = new_data
messagebox.showinfo("成功", f"文件处理完成,已保存到桌面:\n{os.path.basename(save_path)}")
def merge_csv_files(self):
"""合并多个CSV文件并保存到桌面"""
if not self.merge_file_paths:
messagebox.showwarning("警告", "请先添加要合并的文件")
return
try:
# 检查所有文件是否存在
missing_files = [f for f in self.merge_file_paths if not os.path.exists(f)]
if missing_files:
raise FileNotFoundError(f"以下文件不存在: {', '.join(missing_files)}")
merge_column = self.merge_column.get()
common_headers = None
all_data = []
# 收集所有文件的表头和数据
header_sets = []
for file_path in self.merge_file_paths:
with open(file_path, 'r', encoding='utf-8-sig') as file:
reader = csv.reader(file)
data = list(reader)
if data:
header_sets.append(set(data[0]))
all_data.append(data)
# 找出共同表头
if header_sets:
common_headers = set(header_sets[0])
for headers in header_sets[1:]:
common_headers.intersection_update(headers)
common_headers = sorted(common_headers)
if not common_headers:
raise ValueError("选中的文件没有共同的列,无法合并")
# 如果没有指定合并依据列,使用所有共同列
merge_indices = None
if merge_column:
if merge_column not in common_headers:
raise ValueError(f"合并依据列 '{merge_column}' 不在共同列中")
merge_indices = [i for i, h in enumerate(common_headers) if h == merge_column]
# 合并数据
merged_data = [common_headers.copy()]
key_counter = defaultdict(int)
for data in all_data:
if not data:
continue
headers = data[0]
header_map = {h: i for i, h in enumerate(headers)}
for row in data[1:]:
# 如果指定了合并列,检查是否已存在相同键
if merge_indices:
merge_values = [row[header_map[h]] for h in common_headers if h == merge_column]
if merge_values:
key = tuple(merge_values)
key_counter[key] += 1
if key_counter[key] > 1:
continue # 跳过重复键的行
# 构建新行,只保留共同列
new_row = []
for col in common_headers:
if col in header_map and len(row) > header_map[col]:
new_row.append(row[header_map[col]])
else:
new_row.append("")
merged_data.append(new_row)
# 生成操作描述
operation = "merged"
if merge_column:
operation += f"_by_{merge_column}"
# 生成保存路径
first_file = os.path.basename(self.merge_file_paths[0])
save_path = self.generate_filename(first_file, operation)
# 保存文件
if self.save_csv_file(merged_data, save_path):
messagebox.showinfo("成功", f"文件合并完成,已保存到桌面:\n{os.path.basename(save_path)}")
except Exception as e:
error_msg = f"合并文件时出错: {str(e)}"
self.log_message(error_msg, "error")
messagebox.showerror("错误", error_msg)
if __name__ == "__main__":
root = tk.Tk()
app = CSVProcessorApp(root)
root.mainloop()
优化这个代码,并新增以下功能
1.排序和去重功能中增加自定义指定某行为表头的功能
2.优化功能处理顺序,新增一个可以依次执行删除行,再排序,在去重的功能,比如处理一个csv数据,设置数据第二行为表头,然后删除某个关键字的所在行,在对这个数据进行指定列排序,比如升序,即整份数据都按照升序进行排列,最后最后在执行删除指定表头的重复项的功能,最后在桌面上输出一份处理后的新的csv数据。
3.并保留现在这个代码也可以单一执行某种功能的功能