Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)

功能说明:以下是一个使用Tkinter和Pandas实现的完整示例,支持Excel数据读取、双表格展示和高级条件筛选功能:

1.文件操作:点击"打开文件"按钮选择Excel文件(支持.xlsx和.xls格式),自动加载数据到左侧表格。输入Pandas兼容的查询条件,点击"执行筛选"查看结果,点击"清空条件"重置筛选结果。

2.数据展示:左侧表格显示原始数据,右侧表格显示筛选结果,自动适应列宽,支持垂直滚动。

3.高级筛选:数值比较:Age >= 25,字符串包含:Name.str.contains("张"),多条件组合:(Salary > 8000) & (Department == "销售部"),日期筛选:Join_Date > "2023-01-01",在条件输入框使用Pandas查询语法,例如:点击"执行筛选"按钮应用条件,点击"清空条件"按钮重置筛选。

4.错误处理:文件读取错误提示,条件语法错误提示,空条件警告。

5.数据库保存功能:原始数据(JSON格式),保存时间戳(精确到秒),新增"保存结果"按钮,自动创建SQLite数据库文件(data_records.db),存储结构包含:每次保存记录当前系统时间。

6.数据库查看功能:点击"查看历史"按钮弹出历史记录窗口,显示保存时间和记录数量,双击条目可查看详细数据,详细数据显示原始保存的表格格式。

7.条件收藏功能:新增收藏条件按钮,支持为当前条件命名保存,下拉选择框可快速调用历史条件,自动同步数据库中的收藏条件。

python 复制代码
# -*- coding: utf-8 -*-
# @Author : 小红牛
# 微信公众号:WdPython
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
import pandas as pd
import sqlite3
from datetime import datetime

class ExcelViewerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Excel数据加载+分析1.0")
        self.df = pd.DataFrame()
        self.filtered_df = pd.DataFrame()
        self.saved_conditions = []

        # 初始化数据库
        self.init_db()
        self.load_saved_conditions()

        # 创建界面组件
        self.create_widgets()
        self.setup_layout()
        self.setup_style()

    def init_db(self):
        """初始化数据库连接和表结构"""
        self.conn = sqlite3.connect('data_records.db')
        self.cursor = self.conn.cursor()

        # 结果表
        self.cursor.execute('''CREATE TABLE IF NOT EXISTS results
                            (id INTEGER PRIMARY KEY AUTOINCREMENT,
                             record_data TEXT,
                             save_time TIMESTAMP)''')

        # 条件表(增加唯一约束)
        self.cursor.execute('''CREATE TABLE IF NOT EXISTS saved_conditions
                            (id INTEGER PRIMARY KEY AUTOINCREMENT,
                             condition_name TEXT UNIQUE,
                             condition_expr TEXT,
                             save_time TIMESTAMP)''')
        self.conn.commit()

    def load_saved_conditions(self):
        """加载收藏条件"""
        self.cursor.execute("SELECT condition_name, condition_expr FROM saved_conditions ORDER BY save_time DESC")
        self.saved_conditions = self.cursor.fetchall()

    def create_widgets(self):
        """创建所有界面组件"""
        # 工具栏
        self.toolbar = ttk.Frame(self.root)
        self.open_btn = ttk.Button(self.toolbar, text="打开文件", command=self.open_file)
        self.save_btn = ttk.Button(self.toolbar, text="保存数据", command=self.save_to_db)
        self.history_btn = ttk.Button(self.toolbar, text="查看数据", command=self.show_history)
        self.manage_btn = ttk.Button(self.toolbar, text="管理条件", command=self.manage_conditions)
        self.clear_btn = ttk.Button(self.toolbar, text="清空条件", command=self.clear_condition)

        # 条件输入区
        self.condition_frame = ttk.LabelFrame(self.root, text="筛选条件")
        self.condition_combo = ttk.Combobox(
            self.condition_frame,
            values=[c[0] for c in self.saved_conditions],
            width=25,
            state="readonly"
        )
        self.condition_combo.bind("<<ComboboxSelected>>", self.select_condition)
        self.condition_entry = ttk.Entry(self.condition_frame, width=50)
        self.save_condition_btn = ttk.Button(self.condition_frame, text="收藏条件", command=self.save_condition)
        self.search_btn = ttk.Button(self.condition_frame, text="执行筛选", command=self.filter_data)

        # 示例条件文本框
        self.example_frame = ttk.LabelFrame(self.root, text="条件命令示例(cv可复制)")
        self.example_text = tk.Text(
            self.example_frame,
            height=3,
            width=60,
            wrap=tk.WORD,
            bg='#F7F7F7',
            relief=tk.FLAT
        )
        self.example_text.insert(tk.END,
                                 "1.数值比较:工资 >= 9000,工资 == 8000,工资.between(9000, 15000)\n"
                                 "2.文本匹配:姓名.str.contains('张'),部门 == '市场部'\n"
                                 "3.多条件:工资 >= 9000 & 入职日期 > '2025-01-18'\n"
                                 )
        self.example_text.configure(state=tk.DISABLED)

        # 数据表格
        self.tree_frame = ttk.Frame(self.root)
        self.original_tree = ttk.Treeview(self.tree_frame, show="headings")
        self.result_tree = ttk.Treeview(self.tree_frame, show="headings")

        # 滚动条
        self.original_scroll = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.original_tree.yview)
        self.result_scroll = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.result_tree.yview)

    def setup_layout(self):
        """布局组件"""
        # 工具栏
        self.toolbar.pack(fill=tk.X, padx=5, pady=5)
        self.open_btn.pack(side=tk.LEFT, padx=2)
        self.save_btn.pack(side=tk.LEFT, padx=2)
        self.history_btn.pack(side=tk.LEFT, padx=2)
        self.manage_btn.pack(side=tk.LEFT, padx=2)
        self.clear_btn.pack(side=tk.LEFT, padx=2)

        # 条件输入区
        self.condition_frame.pack(fill=tk.X, padx=5, pady=5)
        self.condition_combo.pack(side=tk.LEFT, padx=2)
        self.condition_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2, pady=2)
        self.save_condition_btn.pack(side=tk.LEFT, padx=2)
        self.search_btn.pack(side=tk.LEFT, padx=2)

        # 示例文本框
        self.example_frame.pack(fill=tk.X, padx=5, pady=5)
        self.example_text.pack(padx=5, pady=5, fill=tk.BOTH, expand=True)

        # 表格区
        self.tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.original_tree.grid(row=0, column=0, sticky="nsew")
        self.original_scroll.grid(row=0, column=1, sticky="ns")
        self.result_tree.grid(row=0, column=2, sticky="nsew")
        self.result_scroll.grid(row=0, column=3, sticky="ns")

        # 列权重
        self.tree_frame.columnconfigure(0, weight=1)
        self.tree_frame.columnconfigure(2, weight=1)
        self.tree_frame.rowconfigure(0, weight=1)

    def setup_style(self):
        """配置界面样式"""
        style = ttk.Style()
        style.configure("Treeview", rowheight=28, font=('微软雅黑', 10))
        style.configure("Treeview.Heading", font=('微软雅黑', 10, 'bold'))
        style.configure("TButton", padding=6, font=('微软雅黑', 9))
        style.configure("TLabelFrame", font=('微软雅黑', 9, 'bold'))
        style.configure("TEntry", font=('微软雅黑', 10))

    def open_file(self):
        """打开Excel文件并加载数据"""
        file_path = filedialog.askopenfilename(
            filetypes=[("Excel文件", "*.xlsx *.xls"), ("所有文件", "*.*")]
        )
        if file_path:
            try:
                self.df = pd.read_excel(file_path)
                self.update_treeview(self.original_tree, self.df)
                messagebox.showinfo("成功", f"成功加载文件:{file_path}")
            except Exception as e:
                messagebox.showerror("错误", f"文件读取失败:{str(e)}")

    def update_treeview(self, tree, dataframe):
        """更新Treeview组件显示数据"""
        # 清空现有数据
        tree.delete(*tree.get_children())

        # 配置列
        columns = list(dataframe.columns)
        tree["columns"] = columns
        for col in columns:
            tree.heading(col, text=col)
            tree.column(col, width=100, anchor="w", minwidth=50)

        # 插入数据
        for _, row in dataframe.iterrows():
            values = [self.format_value(v) for v in row.values]
            tree.insert("", "end", values=values)

    def format_value(self, value):
        """格式化显示值"""
        if pd.isna(value):
            return ""
        if isinstance(value, (float, int)):
            return round(value, 4)
        if isinstance(value, datetime):
            return value.strftime("%Y-%m-%d")
        return str(value)[:50]  # 截断长字符串

    def filter_data(self):
        """执行数据筛选"""
        condition = self.condition_entry.get().strip()
        if not condition:
            messagebox.showwarning("输入错误", "请输入筛选条件")
            return

        try:
            self.filtered_df = self.df.query(condition, engine='python')
            self.update_treeview(self.result_tree, self.filtered_df)
        except Exception as e:
            messagebox.showerror("条件错误", f"无效的筛选条件:\n{str(e)}")

    def save_to_db(self):
        """保存筛选结果到数据库"""
        if self.filtered_df.empty:
            messagebox.showwarning("保存错误", "没有可保存的筛选结果")
            return

        try:
            # 转换为JSON格式
            json_data = self.filtered_df.to_json(orient='records', force_ascii=False)
            save_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

            # 插入数据库
            self.cursor.execute(
                "INSERT INTO results (record_data, save_time) VALUES (?, ?)",
                (json_data, save_time)
            )
            self.conn.commit()

            messagebox.showinfo("保存成功",
                                f"成功保存 {len(self.filtered_df)} 条记录\n保存时间:{save_time}")
        except Exception as e:
            messagebox.showerror("保存失败", f"数据库操作失败:{str(e)}")

    def show_history(self):
        """显示历史记录窗口"""
        history_win = tk.Toplevel(self.root)
        history_win.title("历史保存记录")
        history_win.geometry("600x400")

        # 创建表格
        tree = ttk.Treeview(history_win, columns=("time", "count"), show="headings")
        tree.heading("time", text="保存时间")
        tree.heading("count", text="记录数")
        tree.column("time", width=200)
        tree.column("count", width=100, anchor="center")

        # 滚动条
        scroll = ttk.Scrollbar(history_win, orient="vertical", command=tree.yview)
        tree.configure(yscrollcommand=scroll.set)

        # 布局
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # 加载数据
        self.cursor.execute("SELECT save_time, record_data FROM results ORDER BY save_time DESC")
        for save_time, data in self.cursor.fetchall():
            count = len(pd.read_json(data))
            display_time = datetime.strptime(save_time, "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d %H:%M")
            tree.insert("", "end", values=(display_time, count))

        # 双击查看详情
        def on_double_click(event):
            selected = tree.selection()
            if selected:
                item = tree.item(selected[0])
                time_str = item["values"][0]
                self.show_history_detail(time_str)

        tree.bind("<Double-1>", on_double_click)

    def show_history_detail(self, time_str):
        """显示历史记录详情"""
        detail_win = tk.Toplevel(self.root)
        detail_win.title(f"记录详情 - {time_str}")
        detail_win.geometry("800x600")

        # 查询数据库
        self.cursor.execute(
            "SELECT record_data FROM results WHERE save_time LIKE ?",
            (f"{time_str}%",)
        )
        result = self.cursor.fetchone()

        if not result:
            messagebox.showerror("错误", "找不到对应的记录数据")
            return

        # 创建表格
        df = pd.read_json(result[0])
        tree = ttk.Treeview(detail_win, show="headings")
        scroll_x = ttk.Scrollbar(detail_win, orient="horizontal", command=tree.xview)
        scroll_y = ttk.Scrollbar(detail_win, orient="vertical", command=tree.yview)
        tree.configure(xscrollcommand=scroll_x.set, yscrollcommand=scroll_y.set)

        # 配置列
        tree["columns"] = list(df.columns)
        for col in df.columns:
            tree.heading(col, text=col)
            tree.column(col, width=120, minwidth=80, anchor="w")

        # 插入数据
        for _, row in df.iterrows():
            values = [self.format_value(v) for v in row.values]
            tree.insert("", "end", values=values)

        # 布局
        tree.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
        scroll_x.pack(side=tk.BOTTOM, fill=tk.X)

    def save_condition(self):
        """保存当前筛选条件"""
        condition = self.condition_entry.get().strip()
        if not condition:
            messagebox.showwarning("输入错误", "当前没有可保存的条件")
            return

        # 获取条件名称
        name = simpledialog.askstring("保存条件", "请输入条件名称:", parent=self.root)
        if not name:
            return

        # 检查重名
        self.cursor.execute("SELECT 1 FROM saved_conditions WHERE condition_name=?", (name,))
        if self.cursor.fetchone():
            messagebox.showerror("保存失败", "该名称已存在,请使用其他名称")
            return

        try:
            # 插入数据库
            save_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.cursor.execute(
                "INSERT INTO saved_conditions (condition_name, condition_expr, save_time) VALUES (?, ?, ?)",
                (name, condition, save_time)
            )
            self.conn.commit()

            # 更新界面
            self.load_saved_conditions()
            self.condition_combo["values"] = [c[0] for c in self.saved_conditions]
            messagebox.showinfo("保存成功", "条件已成功收藏!")
        except Exception as e:
            messagebox.showerror("保存失败", f"数据库错误:{str(e)}")

    def select_condition(self, event):
        """选择已保存的条件"""
        selected_name = self.condition_combo.get()
        for name, expr in self.saved_conditions:
            if name == selected_name:
                self.condition_entry.delete(0, tk.END)
                self.condition_entry.insert(0, expr)
                break

    def manage_conditions(self):
        """打开条件管理窗口"""
        manage_win = tk.Toplevel(self.root)
        manage_win.title("管理收藏条件")
        manage_win.geometry("600x400")

        # 条件列表
        tree = ttk.Treeview(manage_win, columns=("name", "expr"), show="headings", selectmode="browse")
        tree.heading("name", text="条件名称")
        tree.heading("expr", text="条件表达式")
        tree.column("name", width=150)
        tree.column("expr", width=400)

        # 操作按钮
        btn_frame = ttk.Frame(manage_win)
        edit_btn = ttk.Button(btn_frame, text="编辑", command=lambda: self.edit_condition(tree, manage_win))
        delete_btn = ttk.Button(btn_frame, text="删除", command=lambda: self.delete_condition(tree))

        # 布局
        tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        btn_frame.pack(pady=5)
        edit_btn.pack(side=tk.LEFT, padx=5)
        delete_btn.pack(side=tk.LEFT, padx=5)

        # 加载数据
        for name, expr in self.saved_conditions:
            tree.insert("", "end", values=(name, expr))

    def edit_condition(self, tree, parent_win):
        """编辑选中条件"""
        selected = tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一个条件")
            return

        old_name, old_expr = tree.item(selected[0], "values")

        # 创建编辑对话框
        edit_win = tk.Toplevel(parent_win)
        edit_win.title("编辑条件")

        # 输入组件
        ttk.Label(edit_win, text="名称:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
        name_entry = ttk.Entry(edit_win, width=30)
        name_entry.insert(0, old_name)
        name_entry.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(edit_win, text="表达式:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
        expr_entry = ttk.Entry(edit_win, width=50)
        expr_entry.insert(0, old_expr)
        expr_entry.grid(row=1, column=1, padx=5, pady=5)

        def save_changes():
            new_name = name_entry.get().strip()
            new_expr = expr_entry.get().strip()

            if not new_name or not new_expr:
                messagebox.showwarning("输入错误", "名称和表达式不能为空")
                return

            try:
                # 检查名称冲突
                if new_name != old_name:
                    self.cursor.execute("SELECT 1 FROM saved_conditions WHERE condition_name=?", (new_name,))
                    if self.cursor.fetchone():
                        messagebox.showerror("错误", "名称已存在")
                        return

                # 更新数据库
                self.cursor.execute(
                    "UPDATE saved_conditions SET condition_name=?, condition_expr=? WHERE condition_name=?",
                    (new_name, new_expr, old_name)
                )
                self.conn.commit()

                # 更新界面
                self.load_saved_conditions()
                self.condition_combo["values"] = [c[0] for c in self.saved_conditions]
                tree.item(selected[0], values=(new_name, new_expr))
                edit_win.destroy()
                messagebox.showinfo("成功", "条件已更新")
            except Exception as e:
                messagebox.showerror("错误", f"更新失败:{str(e)}")

        ttk.Button(edit_win, text="保存", command=save_changes).grid(row=2, column=1, pady=10)

    def delete_condition(self, tree):
        """删除选中条件"""
        selected = tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一个条件")
            return

        condition_name = tree.item(selected[0], "values")[0]

        if messagebox.askyesno("确认删除", f"确定要删除条件 '{condition_name}' 吗?"):
            try:
                self.cursor.execute("DELETE FROM saved_conditions WHERE condition_name=?", (condition_name,))
                self.conn.commit()
                tree.delete(selected[0])
                self.load_saved_conditions()
                self.condition_combo["values"] = [c[0] for c in self.saved_conditions]
                messagebox.showinfo("成功", "条件已删除")
            except Exception as e:
                messagebox.showerror("错误", f"删除失败:{str(e)}")

    def save_condition(self):
        """保存当前条件"""
        condition = self.condition_entry.get().strip()
        if not condition:
            messagebox.showwarning("输入错误", "当前没有可保存的条件")
            return

        name = simpledialog.askstring("保存条件", "请输入条件名称:", parent=self.root)
        if not name:
            return

        # 检查重复
        self.cursor.execute("SELECT 1 FROM saved_conditions WHERE condition_name=?", (name,))
        if self.cursor.fetchone():
            messagebox.showerror("错误", "该名称已存在")
            return

        try:
            save_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.cursor.execute(
                "INSERT INTO saved_conditions VALUES (NULL, ?, ?, ?)",
                (name, condition, save_time)
            )
            self.conn.commit()

            # 更新界面
            self.load_saved_conditions()
            self.condition_combo["values"] = [c[0] for c in self.saved_conditions]
            messagebox.showinfo("成功", "条件已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败:{str(e)}")

    def select_condition(self, event):
        """选择已保存的条件"""
        selected_name = self.condition_combo.get()
        for name, expr in self.saved_conditions:
            if name == selected_name:
                self.condition_entry.delete(0, tk.END)
                self.condition_entry.insert(0, expr)
                break

    def clear_condition(self):
        """清空筛选条件和结果"""
        self.condition_entry.delete(0, tk.END)
        self.result_tree.delete(*self.result_tree.get_children())
        self.filtered_df = pd.DataFrame()
        messagebox.showinfo("已清空", "筛选条件和结果已重置")

    def __del__(self):
        """关闭数据库连接"""
        if hasattr(self, 'conn'):
            self.conn.close()

if __name__ == "__main__":
    root = tk.Tk()
    app = ExcelViewerApp(root)
    root.geometry("1366x768")
    root.mainloop()

完毕!!感谢您的收看

----------★★跳转到历史博文集合★★----------

我的零基础Python教程,Python入门篇 进阶篇 视频教程 Py安装py项目 Python模块 Python爬虫 Json Xpath 正则表达式 Selenium Etree CssGui程序开发 Tkinter Pyqt5 列表元组字典数据可视化 matplotlib 词云图 Pyecharts 海龟画图 Pandas Bug处理 电脑小知识office自动化办公 编程工具 NumPy Pygame

相关推荐
冬天给予的预感1 小时前
DAY 54 Inception网络及其思考
网络·python·深度学习
钢铁男儿1 小时前
PyQt5高级界而控件(容器:装载更多的控件QDockWidget)
数据库·python·qt
亿牛云爬虫专家5 小时前
Kubernetes下的分布式采集系统设计与实战:趋势监测失效引发的架构进化
分布式·python·架构·kubernetes·爬虫代理·监测·采集
蹦蹦跳跳真可爱5899 小时前
Python----OpenCV(图像増强——高通滤波(索贝尔算子、沙尔算子、拉普拉斯算子),图像浮雕与特效处理)
人工智能·python·opencv·计算机视觉
nananaij9 小时前
【Python进阶篇 面向对象程序设计(3) 继承】
开发语言·python·神经网络·pycharm
雷羿 LexChien10 小时前
从 Prompt 管理到人格稳定:探索 Cursor AI 编辑器如何赋能 Prompt 工程与人格风格设计(上)
人工智能·python·llm·编辑器·prompt
敲键盘的小夜猫10 小时前
LLM复杂记忆存储-多会话隔离案例实战
人工智能·python·langchain
高压锅_122011 小时前
Django Channels WebSocket实时通信实战:从聊天功能到消息推送
python·websocket·django
胖达不服输12 小时前
「日拱一码」020 机器学习——数据处理
人工智能·python·机器学习·数据处理
吴佳浩12 小时前
Python入门指南-番外-LLM-Fingerprint(大语言模型指纹):从技术视角看AI开源生态的边界与挑战
python·llm·mcp