主要功能:根据关键词及指定列查询整个电子表格的多个Sheet,然后将数据写入新文件,保持原来的格式。
Python代码如下:
import tkinter as tk from tkinter import ttk, filedialog, messagebox import pandas as pd import os from pathlib import Path import time import shutil from openpyxl import load_workbook from openpyxl.utils import get_column_letter from copy import copy import traceback import sys def resource_path(relative_path): """ 获取资源的绝对路径 """ try: # PyInstaller创建临时文件夹,将路径存储在_MEIPASS中 base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) class ExcelQueryApp: def __init__(self, root): self.root = root self.root.title("Excel查询工具-志伟呀@_@qzw") self.root.geometry("1200x800") # 配置根窗口的网格权重 self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) # 数据相关变量 self.df = None self.default_save_path = os.path.join(os.path.expanduser("~"), "Desktop") self.create_widgets() def create_widgets(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)) # 创建左右分栏 left_frame = ttk.Frame(main_frame) left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10)) right_frame = ttk.Frame(main_frame) right_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置左侧框架的网格权重 left_frame.columnconfigure(0, weight=1) left_frame.rowconfigure(1, weight=1) # 让工作表设置区域可以扩展 # 左侧内容 - 顶部文件选择 file_frame = ttk.Frame(left_frame) file_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) ttk.Label(file_frame, text="选择Excel文件:").pack(side=tk.LEFT) self.file_path = tk.StringVar() ttk.Entry(file_frame, textvariable=self.file_path, width=50).pack(side=tk.LEFT, padx=5) ttk.Button(file_frame, text="浏览", command=self.browse_file).pack(side=tk.LEFT) # Sheet信息显示和设置(使用滚动框架) sheet_container = ttk.LabelFrame(left_frame, text="工作表设置", padding="5") sheet_container.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置sheet_container的网格权重 sheet_container.columnconfigure(0, weight=1) sheet_container.rowconfigure(0, weight=1) # 创建Canvas和Scrollbar canvas = tk.Canvas(sheet_container) scrollbar = ttk.Scrollbar(sheet_container, orient="vertical", command=canvas.yview) # 创建可滚动的框架 self.sheet_frame = ttk.Frame(canvas) self.sheet_frame.bind( "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) # 配置canvas的网格权重 canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S)) # 在Canvas中创建窗口 canvas.create_window((0, 0), window=self.sheet_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) # 绑定鼠标滚轮事件 def _on_mousewheel(event): canvas.yview_scroll(int(-1*(event.delta/120)), "units") canvas.bind_all("<MouseWheel>", _on_mousewheel) # 统一设置选项 self.unified_settings = tk.BooleanVar(value=True) ttk.Checkbutton(self.sheet_frame, text="统一设置所有工作表", variable=self.unified_settings, command=self.toggle_settings).grid(row=0, column=0, columnspan=2, sticky=tk.W) # 统一设置框架 self.unified_frame = ttk.Frame(self.sheet_frame) self.unified_frame.grid(row=1, column=0, columnspan=2, pady=5, sticky=(tk.W, tk.E)) ttk.Label(self.unified_frame, text="表头行号:").grid(row=0, column=0, sticky=tk.W) self.unified_header_row = tk.StringVar(value="1") ttk.Entry(self.unified_frame, textvariable=self.unified_header_row, width=10).grid(row=0, column=1) ttk.Label(self.unified_frame, text="关键词列号:").grid(row=0, column=2, sticky=tk.W, padx=(20,0)) self.unified_keyword_column = tk.StringVar(value="1") ttk.Entry(self.unified_frame, textvariable=self.unified_keyword_column, width=10).grid(row=0, column=3) # 独立设置框架(初始隐藏) self.individual_frame = ttk.Frame(self.sheet_frame) self.sheet_settings = {} # 左侧底部控件 bottom_frame = ttk.Frame(left_frame) bottom_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=10) # 关键词输入 keyword_frame = ttk.Frame(bottom_frame) keyword_frame.pack(fill=tk.X, pady=(0, 5)) ttk.Label(keyword_frame, text="关键词(用逗号分隔):").pack(side=tk.LEFT) self.keywords = tk.StringVar() ttk.Entry(keyword_frame, textvariable=self.keywords, width=50).pack(side=tk.LEFT, fill=tk.X, expand=True) # 进度条 self.progress = ttk.Progressbar(bottom_frame, length=300, mode='determinate') self.progress.pack(fill=tk.X, pady=5) # 操作按钮 button_frame = ttk.Frame(bottom_frame) button_frame.pack(fill=tk.X) ttk.Button(button_frame, text="查询并保存", command=self.search_and_save).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="保存原始数据", command=self.save_original_data).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="设置默认保存路径", command=self.set_default_save_path).pack(side=tk.LEFT, padx=5) # 右侧内容 - 日志显示 ttk.Label(right_frame, text="操作日志:").grid(row=0, column=0, sticky=tk.W) self.log_text = tk.Text(right_frame, width=50, height=40) self.log_text.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 日志滚动条 log_scrollbar = ttk.Scrollbar(right_frame, orient="vertical", command=self.log_text.yview) log_scrollbar.grid(row=1, column=1, sticky=(tk.N, tk.S)) self.log_text.configure(yscrollcommand=log_scrollbar.set) # 配置网格权重,使得窗口可以正确调整大小 main_frame.columnconfigure(0, weight=3) # 左侧占比更大 main_frame.columnconfigure(1, weight=2) # 右侧占比较小 right_frame.columnconfigure(0, weight=1) right_frame.rowconfigure(1, weight=1) def toggle_settings(self): """切换统一设置和独立设置""" if self.unified_settings.get(): self.unified_frame.grid() self.individual_frame.grid_remove() else: self.unified_frame.grid_remove() self.individual_frame.grid() def browse_file(self): file_path = filedialog.askopenfilename( filetypes=[("Excel files", "*.xlsx *.xls")] ) if file_path: self.file_path.set(file_path) self.update_sheet_info(file_path) def update_sheet_info(self, file_path): """更新工作表信息""" try: # 读取Excel文件中的sheet信息 wb = load_workbook(file_path, read_only=True) sheet_names = wb.sheetnames wb.close() self.log_message(f"发现 {len(sheet_names)} 个工作表") # 清除现有的sheet设置 for widget in self.individual_frame.winfo_children(): widget.destroy() self.sheet_settings.clear() # 为每个sheet创建设置控件 for idx, sheet_name in enumerate(sheet_names): frame = ttk.LabelFrame(self.individual_frame, text=f"工作表: {sheet_name}", padding="5") frame.grid(row=idx, column=0, sticky=(tk.W, tk.E), pady=2) ttk.Label(frame, text="表头行号:").grid(row=0, column=0, sticky=tk.W) header_var = tk.StringVar(value="1") ttk.Entry(frame, textvariable=header_var, width=10).grid(row=0, column=1) ttk.Label(frame, text="关键词列号:").grid(row=0, column=2, sticky=tk.W, padx=(20,0)) keyword_var = tk.StringVar(value="1") ttk.Entry(frame, textvariable=keyword_var, width=10).grid(row=0, column=3) self.sheet_settings[sheet_name] = { 'header_row': header_var, 'keyword_column': keyword_var } self.individual_frame.grid(row=2, column=0, columnspan=2, pady=5, sticky=(tk.W, tk.E)) if self.unified_settings.get(): self.individual_frame.grid_remove() except Exception as e: self.log_message(f"读取工作表信息时发生错误:{str(e)}", "ERROR") def set_default_save_path(self): path = filedialog.askdirectory() if path: self.default_save_path = path messagebox.showinfo("成功", f"默认保存路径已设置为:{path}") def log_message(self, message, level="INFO"): """添加日志消息到日志窗口并立即更新显示""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") formatted_message = f"[{timestamp}] [{level}] {message}\n" self.log_text.insert(tk.END, formatted_message) self.log_text.see(tk.END) # 自动滚动到最新消息 self.root.update() # 使用update()而不是update_idletasks()来强制更新界面 def search_and_save(self): try: # 清空之前的日志 self.log_text.delete(1.0, tk.END) # 获取输入参数 file_path = self.file_path.get() keywords = [str(k.strip()) for k in self.keywords.get().split(",") if k.strip()] if not all([file_path, keywords]): self.log_message("错误:请填写所有必要信息", "ERROR") messagebox.showerror("错误", "请填写所有必要信息") return # 获取工作表设置 sheet_configs = {} if self.unified_settings.get(): # 使用统一设置 header_row = int(self.unified_header_row.get()) - 1 keyword_col = int(self.unified_keyword_column.get()) - 1 wb = load_workbook(file_path, read_only=True) for sheet_name in wb.sheetnames: sheet_configs[sheet_name] = { 'header_row': header_row, 'keyword_col': keyword_col } wb.close() else: # 使用独立设置 for sheet_name, settings in self.sheet_settings.items(): sheet_configs[sheet_name] = { 'header_row': int(settings['header_row'].get()) - 1, 'keyword_col': int(settings['keyword_column'].get()) - 1 } self.log_message(f"开始处理文件: {file_path}") self.log_message(f"搜索关键词: {keywords}") total_steps = len(keywords) * 2 current_step = 0 # 为每个关键词处理 for keyword in keywords: self.log_message(f"\n开始处理关键词: {keyword}") # 创建安全的文件名 safe_keyword = "".join(c for c in keyword if c.isalnum() or c in (' ', '-', '_')) save_path = os.path.join(self.default_save_path, f"查询结果_{safe_keyword}.xlsx") # 复制整个文件 self.log_message(f"正在复制文件...") shutil.copy2(file_path, save_path) self.log_message(f"已创建文件副本: {save_path}") current_step += 1 self.progress['value'] = (current_step / total_steps) * 100 # 打开复制的文件 wb = load_workbook(save_path) total_sheets = len(wb.sheetnames) # 处理每个sheet for sheet_idx, sheet_name in enumerate(wb.sheetnames, 1): config = sheet_configs[sheet_name] self.log_message(f"\n正在处理工作表 ({sheet_idx}/{total_sheets}): {sheet_name}") self.log_message(f"使用设置 - 表头行号: {config['header_row'] + 1}, 关键词列号: {config['keyword_col'] + 1}") # 读取原始数据用于查询 df = pd.read_excel(file_path, sheet_name=sheet_name, header=config['header_row']) # 过滤数据 filtered_df = df[df.iloc[:, config['keyword_col']].astype(str) == keyword] # 获当前sheet sheet = wb[sheet_name] # 保留表头行,删除其他所有行 if sheet.max_row > config['header_row'] + 1: sheet.delete_rows(config['header_row'] + 2, sheet.max_row - (config['header_row'] + 1)) if not filtered_df.empty: self.log_message(f"找到 {len(filtered_df)} 行匹配数据") # 添加过滤后的数据 for row_idx, (_, row) in enumerate(filtered_df.iterrows(), 1): if row_idx % 100 == 0: self.log_message(f"已写入 {row_idx}/{len(filtered_df)} 行...") sheet.append(list(row)) else: self.log_message(f"未找到匹配数据", "WARNING") # 更新进度条 sheet_progress = (sheet_idx / total_sheets) * (100 / total_steps) self.progress['value'] = ((current_step - 1) / total_steps * 100) + sheet_progress self.root.update() # 保存文件 wb.save(save_path) self.log_message(f"关键词 '{keyword}' 的结果文件处理完成!", "SUCCESS") current_step += 1 self.progress['value'] = (current_step / total_steps) * 100 self.log_message("\n所有处理完成!", "SUCCESS") messagebox.showinfo("成功", "所有关键词的查询结果已保存完成!") except Exception as e: error_msg = f"发生错误:{str(e)}" self.log_message(error_msg, "ERROR") self.log_message(traceback.format_exc(), "ERROR") messagebox.showerror("错误", error_msg) finally: self.progress['value'] = 0 def save_original_data(self): try: file_path = self.file_path.get() if not file_path: self.log_message("错误:请先选择Excel文件", "ERROR") messagebox.showerror("错误", "请先选择Excel文件") return save_path = os.path.join(self.default_save_path, "原始数据备份.xlsx") self.log_message(f"正在保存原始数据到: {save_path}") import xlwings as xw wb = xw.Book(file_path) wb.save(save_path) wb.close() self.log_message("原始数据保存完成!", "SUCCESS") messagebox.showinfo("成功", f"原始数据已保存至:{save_path}") except Exception as e: error_msg = f"保存原始数据时发生错误:{str(e)}" self.log_message(error_msg, "ERROR") self.log_message(traceback.format_exc(), "ERROR") messagebox.showerror("错误", error_msg) if __name__ == "__main__": root = tk.Tk() app = ExcelQueryApp(root) root.mainloop()
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论0+