吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2484|回复: 73
上一主题 下一主题
收起左侧

[原创工具] 电子表格按关键词拆分工具

  [复制链接]
跳转到指定楼层
楼主
qzwsa 发表于 2024-11-26 15:33 回帖奖励
小小的一个工具,因为工作中需要用到才编写的,没什么技术,分享出来方便有用到的。

电子表格按关键词拆分工具

写界面的代码比核心处理表格的还多

主要功能:根据关键词及指定列查询整个电子表格的多个Sheet,然后将数据写入新文件,保持原来的格式.

有需要的自取下载地址:
https://wwzm.lanzoub.com/iU8122gc1j2d

[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() 

WX20241126-152700@2x.jpg (204.09 KB, 下载次数: 3)

WX20241126-152700@2x.jpg

out_www.yalijuda.com_软件说明222_PJx5Y79aVp.jpg (1.18 MB, 下载次数: 18)

out_www.yalijuda.com_软件说明222_PJx5Y79aVp.jpg

免费评分

参与人数 15吾爱币 +19 热心值 +14 收起 理由
一场荒唐半生梦 + 1 + 1 热心回复!
阿迦南 + 1 + 1 我很赞同!
2300zjh + 1 + 1 谢谢@Thanks!
nizeze + 1 + 1 热心回复!
dmwoaini + 1 我很赞同!
紫气 + 1 + 1 谢谢@Thanks!
老虎爱吃素 + 1 + 1 谢谢@Thanks!
w2719921106 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
jamessteed + 1 + 1 谢谢@Thanks!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yanglinman + 1 谢谢@Thanks!
0jiao0 + 1 + 1 谢谢@Thanks!
zylz9941 + 1 + 1 谢谢@Thanks!
策士 + 1 热心回复!
狐白本白 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
LWY39 发表于 2024-11-26 17:03
感谢楼主的分享!这个软件很实用,提高了我的工作效率。
推荐
 楼主| qzwsa 发表于 2024-11-26 23:54 |楼主
本帖最后由 qzwsa 于 2024-11-27 00:02 编辑
cesz123 发表于 2024-11-26 21:27
经测试拆分后格式变了,另外如果能够先预览可以了再拆分就好了。

表头是不变的,下面的内容是变的,因为是整个原文件复制过来,再删除多余的把查询到的数据填充过来。如果完整保持的可能我得修改成就是删除不包含的就数据也是可以的,我再修改一下。
3#
jun269 发表于 2024-11-26 15:45
本帖最后由 jun269 于 2024-11-26 15:48 编辑

会python就是好啊,要啥都可以搞出来,牛人
楼主想的真周到,MAC下的都搞了
4#
wuloveyou 发表于 2024-11-26 15:45
特殊情况下处理表格需要使用,感谢楼主分享,非常棒~~
5#
 楼主| qzwsa 发表于 2024-11-26 15:50 |楼主
jun269 发表于 2024-11-26 15:45
会python就是好啊,要啥都可以搞出来,牛人
楼主想的真周到,MAC下的都搞了

我原本是是用mac写的,后面打包widnows的
6#
jun269 发表于 2024-11-26 15:51
qzwsa 发表于 2024-11-26 15:50
我原本是是用mac写的,后面打包widnows的

腻害啊,,楼主这 python功力
7#
pangbai 发表于 2024-11-26 16:05
很实用的工具,
8#
kisller 发表于 2024-11-26 16:11
厉害,,感谢大神。
9#
Lyaj 发表于 2024-11-26 16:16
很实用的工具
10#
wto007 发表于 2024-11-26 16:56
这个给工作带来便利很多,有时间喝茶了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-23 01:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表