获取frame 标签里#documents里面的内容

本文介绍使用Selenium自动化测试工具获取包含Frame标签的网页中特定数据的方法。通过切换至目标Frame并抓取其内部HTML内容,成功解决了数据抓取难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近几天在爬一个网站,里面有frame标签。

今天有想获取frame标签下面#documents里面的内容,查了一晚上的资料,幸好找到,明天可以交差了。。。

一个有frame标签的网站,http://www.lmtw.com/search.php?show=title%2Ckeyboard%2Cwriter&searchget=1&keyboard=%E7%94%B5%E8%A7%86

获取# documents里面HTML之类的内容,好多时候关键数据都在# documents里面

直接上程序,亲测有效

from selenium import webdriver

browser = webdriver.Chrome('/home/worker/Desktop/driver/chromedriver')
browser.get('http://www.lmtw.com/search.php?show=title%2Ckeyboard%2Cwriter&searchget=1&keyboard=%E7%94%B5%E8%A7%86')

# 将frame切到要获取的标签,参数是,frame标签里面的id,或者name
browser.switch_to.frame('iframepage')  
print(browser.page_source)
browser.close()

这样就获取到id是iframepage,frame标签下面#documents里面的内容了

完美解决

 

跟据以下代码,进行修改并给出完整python:1、筛选功能移除,不需要了,2、添加档案时,页数为空,保存后显示不要为NONE,展示为空就可以了 3、统计页面优化一下美观程度 4、表格数据按灰白背景色交替展示import tkinter as tk from tkinter import ttk, messagebox, scrolledtext, simpledialog import sqlite3 import os import sys import traceback import re from datetime import datetime from tkinter import filedialog class FileManagementSystem: def init(self, root): # 主窗口设置 self.root = root self.root.title(“档案管理系统”) self.root.geometry(“1200x750”) self.root.minsize(1000, 650) self.root.configure(bg=“#f0f2f5”) # 设置应用程序图标 self.set_app_icon() # 样式配置 self.setup_styles() # 初始化数据库 self.init_database() # 当前类别 self.current_category = "文书类" self.current_infra_view = "总表" # 基建类当前视图:总表/明细 # 分页设置 self.current_page = 1 self.page_size = 20 # 每页显示20条记录 self.total_records = 0 self.total_pages = 1 # 筛选条件 self.filter_conditions = [] # 创建界面组件 self.create_widgets() # 加载数据 self.load_data() def set_app_icon(self): """设置应用程序图标(如果有图标文件)""" try: if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) icon_path = os.path.join(base_dir, "archive_icon.ico") if os.path.exists(icon_path): self.root.iconbitmap(icon_path) except: pass def setup_styles(self): """设置界面样式""" self.style = ttk.Style() # 基础字体设置 self.style.configure(".", font=("Microsoft YaHei", 10)) # 主题配置 self.style.theme_use("clam") # 统一按钮样式 (添加 focuswidth=0 去除焦点虚线框) self.style.configure("Primary.TButton", font=("Microsoft YaHei", 10), padding=8, background="#4285f4", foreground="white", borderwidth=1, relief="flat", width=4, focuswidth=0,focuscolor='') self.style.map("Primary.TButton", background=[("active", "#3367d6"), ("pressed", "#2850b3")]) # 删除按钮使用不同颜色 (添加 focuswidth=0) self.style.configure("Danger.TButton", font=("Microsoft YaHei", 10), padding=8, background="#ea4335", foreground="white", borderwidth=1, relief="flat", width=4, focuswidth=0,focuscolor='') self.style.map("Danger.TButton", background=[("active", "#d93025"), ("pressed", "#b31412")]) # 表格样式 self.style.configure("Treeview", background="white", foreground="#202124", rowheight=30, fieldbackground="white", borderwidth=1, relief="solid") self.style.configure("Treeview.Heading", background="#f8f9fa", foreground="#5f6368", font=("Microsoft YaHei", 10, "bold"), padding=8, relief="flat") self.style.map("Treeview.Heading", background=[("active", "#e8eaed")]) self.style.map("Treeview", background=[("selected", "#e8f0fe")], foreground=[("selected", "#202124")]) # 单选按钮样式 self.style.configure("TRadiobutton", background="#f0f2f5", font=("Microsoft YaHei", 10), foreground="#5f6368",focuswidth=0,focuscolor='') self.style.map("TRadiobutton", foreground=[("selected", "#4285f4")]) # 输入框样式 self.style.configure("TEntry", fieldbackground="white", borderwidth=1, relief="solid", padding=5) # 必填输入框样式 self.style.configure("Required.TEntry", fieldbackground="#fff8f8", borderwidth=1, relief="solid", padding=5) # 标签样式 self.style.configure("Header.TLabel", font=("Microsoft YaHei", 16, "bold"), background="#4285f4", foreground="white") self.style.configure("Section.TLabel", font=("Microsoft YaHei", 10, "bold"), background="#f0f2f5", foreground="#5f6368") # 分页按钮样式 (添加 focuswidth=0) self.style.configure("Page.TButton", font=("Microsoft YaHei", 9), padding=4, background="#e8eaed", foreground="#5f6368", borderwidth=1, relief="flat",focuswidth=0,focuscolor='') self.style.map("Page.TButton", background=[("active", "#d7d9dd"), ("pressed", "#c5c7cb")]) # 分页标签样式 self.style.configure("Page.TLabel", background="#f0f2f5", foreground="#5f6368", font=("Microsoft YaHei", 9)) def init_database(self): """初始化数据库,处理打包路径问题""" try: # 获取可执行文件所在目录(打包后) if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) # 确保目录存在 os.makedirs(base_dir, exist_ok=True) db_path = os.path.join(base_dir, "file_management.db") self.conn = sqlite3.connect(db_path) self.cursor = self.conn.cursor() # 创建文书类表(增加保存库房字段) self.cursor.execute(''' CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, security_level TEXT, page_count INTEGER, retention_period TEXT, carrier_form TEXT, storage_location TEXT, -- 新增保存库房字段 remarks TEXT ) ''') # 创建基建类总表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS infrastructure_summary ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, title TEXT NOT NULL, date TEXT, security_level TEXT, total_pages INTEGER, retention_period TEXT, carrier_form TEXT, storage_location TEXT, -- 保存库房 remarks TEXT ) ''') # 创建基建类明细表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS infrastructure_detail ( id INTEGER PRIMARY KEY AUTOINCREMENT, summary_id INTEGER, -- 关联总表ID file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, security_level TEXT, page_number TEXT, -- 页次(可以是范围) retention_period TEXT, carrier_form TEXT, storage_location TEXT, -- 保存库房 remarks TEXT, FOREIGN KEY (summary_id) REFERENCES infrastructure_summary(id) ) ''') self.conn.commit() except Exception as e: messagebox.showerror("数据库错误", f"初始化数据库失败: {str(e)}") traceback.print_exc() sys.exit(1) def create_widgets(self): """创建现代化界面组件""" # 主布局容器 main_frame = tk.Frame(self.root, bg="#f0f2f5") main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) # 顶部标题栏 header_frame = tk.Frame(main_frame, bg="#4285f4", height=60) header_frame.pack(fill=tk.X) header_frame.pack_propagate(False) title_label = tk.Label(header_frame, text="档案管理系统", font=("Microsoft YaHei", 18, "bold"), bg="#4285f4", fg="white") title_label.pack(pady=15) # 功能区 function_frame = tk.Frame(main_frame, bg="#f0f2f5", pady=15) function_frame.pack(fill=tk.X) # 左侧分类选择 category_frame = tk.Frame(function_frame, bg="#f0f2f5") category_frame.pack(side=tk.LEFT, padx=(0, 20)) ttk.Label(category_frame, text="档案类别:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.category_var = tk.StringVar(value="文书类") category_container = tk.Frame(category_frame, bg="#f0f2f5") category_container.pack(side=tk.LEFT) doc_radio = ttk.Radiobutton( category_container, text="文书类", variable=self.category_var, value="文书类", command=self.on_category_change, style="TRadiobutton" ) inf_radio = ttk.Radiobutton( category_container, text="基建类", variable=self.category_var, value="基建类", command=self.on_category_change, style="TRadiobutton" ) doc_radio.pack(side=tk.LEFT, padx=10) inf_radio.pack(side=tk.LEFT, padx=10) # 基建类视图切换 self.view_frame = tk.Frame(function_frame, bg="#f0f2f5") # 基建类视图切换标签 self.view_label = ttk.Label(self.view_frame, text="视图:", style="Section.TLabel") self.view_label.pack(side=tk.LEFT, padx=5) # 基建类视图切换按钮 self.view_var = tk.StringVar(value="总表") self.summary_btn = ttk.Radiobutton( self.view_frame, text="总表", variable=self.view_var, value="总表", command=self.on_view_change, style="TRadiobutton" ) self.detail_btn = ttk.Radiobutton( self.view_frame, text="明细", variable=self.view_var, value="明细", command=self.on_view_change, style="TRadiobutton" ) # 初始隐藏视图切换 self.view_frame.pack_forget() # 搜索区 search_frame = tk.Frame(function_frame, bg="#f0f2f5") search_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 20)) ttk.Label(search_frame, text="搜索:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.search_var = tk.StringVar() self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, style="TEntry", font=("Microsoft YaHei", 10)) self.search_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True, ipady=2) self.search_entry.bind("<Return>", lambda event: self.search_data()) # 搜索按钮 search_btn = ttk.Button(search_frame, text="搜索", command=self.search_data, style="Primary.TButton") search_btn.pack(side=tk.LEFT, padx=5) # 重置按钮 reset_btn = ttk.Button(search_frame, text="重置", command=self.reset_search, style="Primary.TButton") reset_btn.pack(side=tk.LEFT) # 高级筛选按钮 adv_filter_btn = ttk.Button(search_frame, text="筛选", command=self.open_advanced_filter, style="Primary.TButton") adv_filter_btn.pack(side=tk.LEFT, padx=5) # 统计按钮 stats_btn = ttk.Button(search_frame, text="统计", command=self.show_statistics, style="Primary.TButton") stats_btn.pack(side=tk.LEFT) # 右侧操作按钮 button_frame = tk.Frame(function_frame, bg="#f0f2f5") button_frame.pack(side=tk.RIGHT) self.add_btn = ttk.Button(button_frame, text="添加", command=self.add_record, style="Primary.TButton") self.add_btn.pack(side=tk.LEFT, padx=5) self.edit_btn = ttk.Button(button_frame, text="修改", command=self.edit_record, style="Primary.TButton") self.edit_btn.pack(side=tk.LEFT, padx=5) self.delete_btn = ttk.Button(button_frame, text="删除", command=self.delete_record, style="Danger.TButton") self.delete_btn.pack(side=tk.LEFT, padx=5) # 表格区域 table_frame = tk.Frame(main_frame, bg="#e0e0e0", padx=1, pady=1) table_frame.pack(fill=tk.BOTH, expand=True) # 表格标题栏 table_header = tk.Frame(table_frame, bg="#f8f9fa", height=40) table_header.pack(fill=tk.X) table_header.pack_propagate(False) self.table_title = tk.Label(table_header, text=f"{self.current_category}档案列表", font=("Microsoft YaHei", 11, "bold"), bg="#f8f9fa", fg="#5f6368") self.table_title.pack(side=tk.LEFT, padx=15) # 表格容器 table_container = tk.Frame(table_frame, bg="white", bd=1, relief=tk.SOLID) table_container.pack(fill=tk.BOTH, expand=True) # 滚动条 scrollbar_x = ttk.Scrollbar(table_container, orient=tk.HORIZONTAL) scrollbar_y = ttk.Scrollbar(table_container, orient=tk.VERTICAL) # 表格 self.tree = ttk.Treeview( table_container, columns=self.get_columns(), show="headings", yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set, style="Treeview" ) # 配置列 columns = self.get_columns() for col in columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 布局 scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y) scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar_y.config(command=self.tree.yview) scrollbar_x.config(command=self.tree.xview) # 绑定事件 self.tree.bind("<Double-1>", lambda e: self.edit_record()) # 分页控件 pagination_frame = tk.Frame(main_frame, bg="#f0f2f5", height=40, pady=10) pagination_frame.pack(fill=tk.X) # 上一页按钮 self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, style="Page.TButton", width=8) self.prev_btn.pack(side=tk.LEFT, padx=5) # 页码标签 self.page_label = ttk.Label(pagination_frame, text="1/1", style="Page.TLabel") self.page_label.pack(side=tk.LEFT, padx=10) # 下一页按钮 self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, style="Page.TButton", width=8) self.next_btn.pack(side=tk.LEFT, padx=5) # 跳转输入框 ttk.Label(pagination_frame, text="跳转至:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_var = tk.StringVar() self.page_entry = ttk.Entry(pagination_frame, textvariable=self.page_var, width=5, font=("Microsoft YaHei", 9)) self.page_entry.pack(side=tk.LEFT) # 跳转按钮 goto_btn = ttk.Button(pagination_frame, text="跳转", command=self.goto_page, style="Page.TButton", width=6) goto_btn.pack(side=tk.LEFT, padx=5) # 每页显示数量 ttk.Label(pagination_frame, text="每页显示:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_size_var = tk.StringVar(value=str(self.page_size)) page_size_combo = ttk.Combobox(pagination_frame, textvariable=self.page_size_var, width=5, font=("Microsoft YaHei", 9), state="readonly") page_size_combo['values'] = ('10', '20', '50', '100') page_size_combo.pack(side=tk.LEFT) page_size_combo.bind("<<ComboboxSelected>>", self.change_page_size) # 页脚 footer_frame = tk.Frame(main_frame, bg="#f0f2f5", height=30) footer_frame.pack(fill=tk.X, pady=(10, 0)) # 左侧状态信息 self.status_var = tk.StringVar(value="记录数量:0") status_label = ttk.Label( footer_frame, textvariable=self.status_var, style="Page.TLabel", anchor=tk.W ) status_label.pack(side=tk.LEFT, padx=20, pady=5) # 右侧版权信息 footer_label = ttk.Label( footer_frame, text="矿调所大数据中心 © 2025 开发人员:袁沙", style="Page.TLabel" ) footer_label.pack(side=tk.RIGHT, padx=20, pady=5) def center_window(self, window, width, height): """使窗口居中显示""" screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = (screen_width - width) // 2 y = (screen_height - height) // 2 - 20 # 稍微上移一点 window.geometry(f"{width}x{height}+{x}+{y}") window.focus_set() def get_columns(self): """获取当前类别的列""" if self.current_category == "文书类": return ["序号", "档号", "文号", "责任者", "题名", "日期", "密级", "页数", "保存期限", "载体形式", "保存库房", "备注"] else: if self.current_infra_view == "总表": return ["序号", "档号", "题名", "日期", "密级", "总页数", "保存期限", "载体形式", "保存库房", "备注"] else: return ["序号", "档号", "文号", "责任者", "题名", "日期", "密级", "页次", "保存期限", "载体形式", "保存库房", "备注"] def on_category_change(self): """类别改变时更新界面""" self.current_category = self.category_var.get() self.current_page = 1 # 重置到第一页 # 更新表格标题 self.table_title.config(text=f"{self.current_category}档案列表") # 清空表格数据 for item in self.tree.get_children(): self.tree.delete(item) # 根据类别显示/隐藏视图切换 if self.current_category == "基建类": self.view_frame.pack(side=tk.LEFT, padx=(0, 20)) self.summary_btn.pack(side=tk.LEFT, padx=10) self.detail_btn.pack(side=tk.LEFT, padx=10) else: self.view_frame.pack_forget() self.current_infra_view = "总表" # 重置视图 # 重新设置表格列 new_columns = self.get_columns() self.tree["columns"] = new_columns # 配置新列 for col in new_columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 重新加载对应类别的数据 self.load_data() def on_view_change(self): """基建类视图切换""" if self.current_category == "基建类": self.current_infra_view = self.view_var.get() self.current_page = 1 # 重置到第一页 # 更新表格标题 self.table_title.config(text=f"基建类档案{self.current_infra_view}列表") # 清空表格数据 for item in self.tree.get_children(): self.tree.delete(item) # 重新设置表格列 new_columns = self.get_columns() self.tree["columns"] = new_columns # 配置新列 for col in new_columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 重新加载数据 self.load_data() def load_data(self, search_term=None): """加载数据,使用参数化查询防止SQL注入""" try: # 清空表格 for item in self.tree.get_children(): self.tree.delete(item) # 根据当前类别和视图查询数据 if self.current_category == "文书类": query = """ SELECT id, file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, storage_location, remarks FROM documents """ count_query = "SELECT COUNT(*) FROM documents" else: if self.current_infra_view == "总表": query = """ SELECT id, file_number, title, date, security_level, total_pages, retention_period, carrier_form, storage_location, remarks FROM infrastructure_summary """ count_query = "SELECT COUNT(*) FROM infrastructure_summary" else: query = """ SELECT d.id, d.file_number, d.document_number, d.responsible, d.title, d.date, d.security_level, d.page_number, d.retention_period, d.carrier_form, d.storage_location, d.remarks FROM infrastructure_detail d """ count_query = "SELECT COUNT(*) FROM infrastructure_detail" # 添加筛选条件 where_clause = "" params = () # 应用高级筛选条件 if self.filter_conditions: conditions = [] for condition in self.filter_conditions: field, operator, value = condition # 处理日期范围 if operator == "between": start, end = value conditions.append(f"{field} BETWEEN ? AND ?") params += (start, end) else: conditions.append(f"{field} {operator} ?") params += (value,) where_clause = " AND ".join(conditions) # 添加搜索条件 if search_term: search_condition = " OR ".join([ "file_number LIKE ?", "title LIKE ?", "responsible LIKE ?", "document_number LIKE ?" ]) if where_clause: where_clause += " AND (" + search_condition + ")" else: where_clause = "WHERE " + search_condition search_pattern = f"%{search_term}%" params += (search_pattern, search_pattern, search_pattern, search_pattern) elif where_clause: where_clause = "WHERE " + where_clause # 计算总记录数 if where_clause: count_query += " " + where_clause self.cursor.execute(count_query, params) self.total_records = self.cursor.fetchone()[0] # 计算总页数 self.total_pages = max(1, (self.total_records + self.page_size - 1) // self.page_size) # 确保当前页在有效范围内 if self.current_page > self.total_pages: self.current_page = max(1, self.total_pages) # 添加分页限制 offset = (self.current_page - 1) * self.page_size query += " " + where_clause if where_clause else "" query += " ORDER BY id LIMIT ? OFFSET ?" params = params + (self.page_size, offset) self.cursor.execute(query, params) records = self.cursor.fetchall() # 更新状态栏 if search_term or self.filter_conditions: self.status_var.set(f"搜索到 {self.total_records} 条记录") else: self.status_var.set(f"总记录:{self.total_records} 条,当前显示第 {(self.current_page-1)*self.page_size+1} 到 {min(self.current_page*self.page_size, self.total_records)} 条") # 更新分页控件状态 self.prev_btn.state(["!disabled" if self.current_page > 1 else "disabled"]) self.next_btn.state(["!disabled" if self.current_page < self.total_pages else "disabled"]) self.page_label.config(text=f"{self.current_page}/{self.total_pages}") # 插入数据 start_index = (self.current_page - 1) * self.page_size + 1 for i, record in enumerate(records, start_index): values = (i,) + record[1:] tag = "even" if i % 2 == 0 else "odd" self.tree.insert("", tk.END, values=values, tags=(record[0], tag)) # 配置行样式 self.tree.tag_configure("odd", background="white") self.tree.tag_configure("even", background="#f8f9fa") except Exception as e: messagebox.showerror("数据库错误", f"加载数据失败: {str(e)}") traceback.print_exc() def search_data(self): """搜索当前类别的数据""" search_term = self.search_var.get().strip() self.current_page = 1 # 搜索后重置到第一页 self.load_data(search_term) def reset_search(self): """重置搜索""" self.search_var.set("") self.filter_conditions = [] # 清除筛选条件 self.current_page = 1 self.load_data() def change_page_size(self, event=None): """更改每页显示数量""" try: new_size = int(self.page_size_var.get()) if new_size > 0: self.page_size = new_size self.current_page = 1 # 重置到第一页 self.load_data() except ValueError: pass def prev_page(self): """上一页""" if self.current_page > 1: self.current_page -= 1 self.load_data(self.search_var.get().strip()) def next_page(self): """下一页""" if self.current_page < self.total_pages: self.current_page += 1 self.load_data(self.search_var.get().strip()) def goto_page(self): """跳转到指定页码""" try: page_num = int(self.page_var.get()) if 1 <= page_num <= self.total_pages: self.current_page = page_num self.load_data(self.search_var.get().strip()) else: messagebox.showwarning("提示", f"请输入1到{self.total_pages}之间的页码") except ValueError: messagebox.showwarning("提示", "请输入有效的页码数字") def open_advanced_filter(self): """打开高级筛选对话框""" dialog = tk.Toplevel(self.root) dialog.title("筛选") self.center_window(dialog, 600, 400) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text="筛选条件", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 条件列表 conditions_frame = tk.Frame(content_frame, bg="#f0f2f5") conditions_frame.pack(fill=tk.BOTH, expand=True) # 字段列表(根据当前类别) if self.current_category == "文书类": fields = ["档号", "文号", "责任者", "题名", "日期", "密级", "页数", "保存期限", "载体形式", "保存库房", "备注"] else: if self.current_infra_view == "总表": fields = ["档号", "题名", "日期", "密级", "总页数", "保存期限", "载体形式", "保存库房", "备注"] else: fields = ["档号", "文号", "责任者", "题名", "日期", "密级", "页次", "保存期限", "载体形式", "保存库房", "备注"] # 运算符 operators = ["=", "!=", ">", "<", ">=", "<=", "LIKE", "BETWEEN"] # 当前条件 self.conditions = [] # 添加初始条件行 self.add_condition_row(conditions_frame, fields, operators) # 添加条件按钮 add_btn_frame = tk.Frame(conditions_frame, bg="#f0f2f5") add_btn_frame.pack(fill=tk.X, pady=10) ttk.Button(add_btn_frame, text="添加条件", command=lambda: self.add_condition_row(conditions_frame, fields, operators), style="Primary.TButton").pack(side=tk.LEFT) # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) ttk.Button(btn_frame, text="筛选", command=lambda: self.apply_filter(dialog), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def add_condition_row(self, parent, fields, operators): """添加一个条件行""" row_frame = tk.Frame(parent, bg="#f0f2f5", pady=5) row_frame.pack(fill=tk.X) # 字段选择 field_var = tk.StringVar(value=fields[0]) field_combo = ttk.Combobox(row_frame, textvariable=field_var, values=fields, width=12, state="readonly") field_combo.pack(side=tk.LEFT, padx=5) # 运算符选择 operator_var = tk.StringVar(value=operators[0]) operator_combo = ttk.Combobox(row_frame, textvariable=operator_var, values=operators, width=8, state="readonly") operator_combo.pack(side=tk.LEFT, padx=5) # 值输入 value_var = tk.StringVar() value_entry = ttk.Entry(row_frame, textvariable=value_var, width=20) value_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) # 日期范围输入(当运算符为BETWEEN时显示) start_var = tk.StringVar() end_var = tk.StringVar() date_frame = tk.Frame(row_frame, bg="#f0f2f5") ttk.Entry(date_frame, textvariable=start_var, width=10).pack(side=tk.LEFT) ttk.Label(date_frame, text="至", bg="#f0f2f5").pack(side=tk.LEFT, padx=5) ttk.Entry(date_frame, textvariable=end_var, width=10).pack(side=tk.LEFT) date_frame.pack_forget() # 初始隐藏 # 删除按钮 delete_btn = ttk.Button(row_frame, text="×", width=2, command=lambda r=row_frame: r.destroy()) delete_btn.pack(side=tk.RIGHT, padx=5) # 绑定运算符变化事件 def on_operator_change(*args): if operator_var.get() == "BETWEEN": value_entry.pack_forget() date_frame.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) else: date_frame.pack_forget() value_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) operator_var.trace("w", on_operator_change) # 保存行引用 self.conditions.append({ "row": row_frame, "field": field_var, "operator": operator_var, "value": value_var, "start": start_var, "end": end_var }) def apply_filter(self, dialog): """应用筛选条件""" self.filter_conditions = [] for condition in self.conditions: if condition["row"].winfo_exists(): # 确保行未被删除 field = condition["field"].get() operator = condition["operator"].get() if operator == "BETWEEN": start = condition["start"].get().strip() end = condition["end"].get().strip() if not start or not end: continue # 验证日期格式 try: datetime.strptime(start, "%Y-%m-%d") datetime.strptime(end, "%Y-%m-%d") self.filter_conditions.append((field, operator, (start, end))) except ValueError: messagebox.showwarning("格式错误", "请输入有效的日期格式(YYYY-MM-DD)") return else: value = condition["value"].get().strip() if value: if operator == "LIKE": value = f"%{value}%" self.filter_conditions.append((field, operator, value)) self.current_page = 1 self.load_data() dialog.destroy() def show_statistics(self): """显示统计结果""" dialog = tk.Toplevel(self.root) dialog.title("档案统计") self.center_window(dialog, 600, 400) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text="档案统计结果", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 统计结果文本区域 stats_text = scrolledtext.ScrolledText(content_frame, wrap=tk.WORD, font=("Microsoft YaHei", 10)) stats_text.pack(fill=tk.BOTH, expand=True) # 获取统计结果 stats_result = self.get_statistics() stats_text.insert(tk.END, stats_result) stats_text.config(state=tk.DISABLED) # 导出按钮 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=10) btn_frame.pack(fill=tk.X) ttk.Button(btn_frame, text="导出统计结果", command=lambda: self.export_statistics(stats_result), style="Primary.TButton").pack(side=tk.RIGHT) def get_statistics(self): """获取统计结果""" result = "" if self.current_category == "文书类": # 文书类按年份统计 self.cursor.execute(""" SELECT SUBSTR(file_number, INSTR(file_number, '·') + 1, 4) AS year, COUNT(DISTINCT file_number) AS file_count FROM documents WHERE year GLOB '[0-9][0-9][0-9][0-9]' GROUP BY year ORDER BY year """) year_stats = self.cursor.fetchall() result += "文书类档案按年份统计:\n" result += "年份\t档号数量\n" result += "----------------\n" for row in year_stats: result += f"{row[0]}\t{row[1]}\n" result += "\n" else: # 基建类 if self.current_infra_view == "总表": # 总表按保存库房统计 self.cursor.execute(""" SELECT storage_location, COUNT(*) AS count FROM infrastructure_summary GROUP BY storage_location ORDER BY storage_location """) location_stats = self.cursor.fetchall() result += "基建类总表按保存库房统计:\n" result += "保存库房\t数量\n" result += "----------------\n" for row in location_stats: result += f"{row[0]}\t{row[1]}\n" else: # 明细按档号统计 self.cursor.execute(""" SELECT file_number, COUNT(*) AS count FROM infrastructure_detail GROUP BY file_number ORDER BY file_number """) file_stats = self.cursor.fetchall() result += "基建类明细按档号统计:\n" result += "档号\t数量\n" result += "----------------\n" for row in file_stats: result += f"{row[0]}\t{row[1]}\n" return result # def export_statistics(self, stats_result): # """导出统计结果到文件""" # file_path = filedialog.asksaveasfilename( # defaultextension=".txt", # filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")], # title="保存统计结果" # ) # # if file_path: # try: # with open(file_path, "w", encoding="utf-8") as f: # f.write(stats_result) # messagebox.showinfo("成功", "统计结果已成功导出!") # except Exception as e: # messagebox.showerror("错误", f"导出失败: {str(e)}") def add_record(self): """添加记录对话框 - 两列布局""" dialog = tk.Toplevel(self.root) dialog.title(f"添加{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"添加{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for col in left_columns: frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for col in right_columns: frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) entries[col] = entry # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) # 交换按钮顺序:先保存后取消 ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=True), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def edit_record(self): """编辑记录对话框 - 两列布局""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] current_values = self.tree.item(selected_item, "values") # 创建对话框 dialog = tk.Toplevel(self.root) dialog.title(f"修改{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"修改{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 values = current_values[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for i, col in enumerate(left_columns): frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 if i < len(values): entry.insert(0, values[i]) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for i, col in enumerate(right_columns): frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 idx = len(left_columns) + i if idx < len(values): entry.insert(0, values[idx]) entries[col] = entry # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) # 交换按钮顺序:先保存后取消 ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=False, record_id=record_id), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def save_record(self, entries, dialog, is_new=True, record_id=None): """保存记录(新增或修改)""" try: # 收集数据 data = {} for col, entry in entries.items(): data[col] = entry.get().strip() # 验证 if not data["档号"]: messagebox.showerror("错误", "档号不能为空!") return if not data["题名"]: messagebox.showerror("错误", "题名不能为空!") return if is_new: # 新增记录 if self.current_category == "文书类": self.cursor.execute(''' INSERT INTO documents ( file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, storage_location, remarks ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["保存库房"], data["备注"] )) else: if self.current_infra_view == "总表": self.cursor.execute(''' INSERT INTO infrastructure_summary ( file_number, title, date, security_level, total_pages, retention_period, carrier_form, storage_location, remarks ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["题名"], data["日期"], data["密级"], data["总页数"] if data["总页数"] else None, data["保存期限"], data["载体形式"], data["保存库房"], data["备注"] )) else: # 明细需要关联总表 # 查找总表ID self.cursor.execute("SELECT id FROM infrastructure_summary WHERE file_number = ?", (data["档号"],)) summary = self.cursor.fetchone() summary_id = summary[0] if summary else None self.cursor.execute(''' INSERT INTO infrastructure_detail ( summary_id, file_number, document_number, responsible, title, date, security_level, page_number, retention_period, carrier_form, storage_location, remarks ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( summary_id, data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页次"], data["保存期限"], data["载体形式"], data["保存库房"], data["备注"] )) else: # 修改记录 if self.current_category == "文书类": self.cursor.execute(''' UPDATE documents SET file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, security_level = ?, page_count = ?, retention_period = ?, carrier_form = ?, storage_location = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["保存库房"], data["备注"], record_id )) else: if self.current_infra_view == "总表": self.cursor.execute(''' UPDATE infrastructure_summary SET file_number = ?, title = ?, date = ?, security_level = ?, total_pages = ?, retention_period = ?, carrier_form = ?, storage_location = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["题名"], data["日期"], data["密级"], data["总页数"] if data["总页数"] else None, data["保存期限"], data["载体形式"], data["保存库房"], data["备注"], record_id )) else: # 更新明细时同时更新关联的总表ID self.cursor.execute("SELECT id FROM infrastructure_summary WHERE file_number = ?", (data["档号"],)) summary = self.cursor.fetchone() summary_id = summary[0] if summary else None self.cursor.execute(''' UPDATE infrastructure_detail SET summary_id = ?, file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, security_level = ?, page_number = ?, retention_period = ?, carrier_form = ?, storage_location = ?, remarks = ? WHERE id = ? ''', ( summary_id, data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页次"], data["保存期限"], data["载体形式"], data["保存库房"], data["备注"], record_id )) self.conn.commit() dialog.destroy() self.load_data() messagebox.showinfo("成功", "记录保存成功!") except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}") traceback.print_exc() def delete_record(self): """删除记录""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return if messagebox.askyesno("确认删除", "确定要删除选中的记录吗?\n此操作不可恢复。"): selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] try: if self.current_category == "文书类": self.cursor.execute("DELETE FROM documents WHERE id = ?", (record_id,)) else: if self.current_infra_view == "总表": # 删除总表时同时删除关联的明细 self.cursor.execute("DELETE FROM infrastructure_summary WHERE id = ?", (record_id,)) self.cursor.execute("DELETE FROM infrastructure_detail WHERE summary_id = ?", (record_id,)) else: self.cursor.execute("DELETE FROM infrastructure_detail WHERE id = ?", (record_id,)) self.conn.commit() self.load_data() messagebox.showinfo("成功", "记录已删除") except Exception as e: messagebox.showerror("错误", f"删除失败:{str(e)}") traceback.print_exc() if name == “main”: try: root = tk.Tk() app = FileManagementSystem(root) root.mainloop() except Exception as e: messagebox.showerror(“系统错误”, f"程序发生致命错误: {str(e)}") traceback.print_exc()
07-30
根据以下代码,如果想表格内展示的数据,底色按灰白交替进行展示,怎么修改?请给出完整代码import tkinter as tk from tkinter import ttk, messagebox, scrolledtext import sqlite3 import os import sys import traceback class FileManagementSystem: def __init__(self, root): # 主窗口设置 self.root = root self.root.title("档案管理系统") self.root.geometry("1200x750") self.root.minsize(1000, 650) self.root.configure(bg="#f0f2f5") # 设置应用程序图标 self.set_app_icon() # 样式配置 self.setup_styles() # 初始化数据库 self.init_database() # 当前类别 self.current_category = "文书类" # 分页设置 self.current_page = 1 self.page_size = 20 # 每页显示20条记录 self.total_records = 0 self.total_pages = 1 # 创建界面组件 self.create_widgets() # 加载数据 self.load_data() def set_app_icon(self): """设置应用程序图标(如果有图标文件)""" try: if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) icon_path = os.path.join(base_dir, "archive_icon.ico") if os.path.exists(icon_path): self.root.iconbitmap(icon_path) except: pass def setup_styles(self): """设置界面样式""" self.style = ttk.Style() # 基础字体设置 self.style.configure(".", font=("Microsoft YaHei", 10)) # 主题配置 self.style.theme_use("clam") # 统一按钮样式 (添加 focuswidth=0 去除焦点虚线框) self.style.configure("Primary.TButton", font=("Microsoft YaHei", 10), padding=8, background="#4285f4", foreground="white", borderwidth=1, relief="flat", width=4, focuswidth=0,focuscolor='') self.style.map("Primary.TButton", background=[("active", "#3367d6"), ("pressed", "#2850b3")]) # 删除按钮使用不同颜色 (添加 focuswidth=0) self.style.configure("Danger.TButton", font=("Microsoft YaHei", 10), padding=8, background="#ea4335", foreground="white", borderwidth=1, relief="flat", width=4, focuswidth=0,focuscolor='') self.style.map("Danger.TButton", background=[("active", "#d93025"), ("pressed", "#b31412")]) # 表格样式 self.style.configure("Treeview", background="white", foreground="#202124", rowheight=30, fieldbackground="white", borderwidth=1, relief="solid") self.style.configure("Treeview.Heading", background="#f8f9fa", foreground="#5f6368", font=("Microsoft YaHei", 10, "bold"), padding=8, relief="flat") self.style.map("Treeview.Heading", background=[("active", "#e8eaed")]) self.style.map("Treeview", background=[("selected", "#e8f0fe")], foreground=[("selected", "#202124")]) # 单选按钮样式 self.style.configure("TRadiobutton", background="#f0f2f5", font=("Microsoft YaHei", 10), foreground="#5f6368",focuswidth=0,focuscolor='') self.style.map("TRadiobutton", foreground=[("selected", "#4285f4")]) # 输入框样式 self.style.configure("TEntry", fieldbackground="white", borderwidth=1, relief="solid", padding=5) # 必填输入框样式 self.style.configure("Required.TEntry", fieldbackground="#fff8f8", borderwidth=1, relief="solid", padding=5) # 标签样式 self.style.configure("Header.TLabel", font=("Microsoft YaHei", 16, "bold"), background="#4285f4", foreground="white") self.style.configure("Section.TLabel", font=("Microsoft YaHei", 10, "bold"), background="#f0f2f5", foreground="#5f6368") # 分页按钮样式 (添加 focuswidth=0) self.style.configure("Page.TButton", font=("Microsoft YaHei", 9), padding=4, background="#e8eaed", foreground="#5f6368", borderwidth=1, relief="flat",focuswidth=0,focuscolor='') self.style.map("Page.TButton", background=[("active", "#d7d9dd"), ("pressed", "#c5c7cb")]) # 分页标签样式 self.style.configure("Page.TLabel", background="#f0f2f5", foreground="#5f6368", font=("Microsoft YaHei", 9)) def init_database(self): """初始化数据库,处理打包路径问题""" try: # 获取可执行文件所在目录(打包后) if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) # 确保目录存在 os.makedirs(base_dir, exist_ok=True) db_path = os.path.join(base_dir, "file_management.db") self.conn = sqlite3.connect(db_path) self.cursor = self.conn.cursor() # 创建文书类表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, security_level TEXT, page_count INTEGER, retention_period TEXT, carrier_form TEXT, remarks TEXT ) ''') # 创建基建类表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS infrastructure ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, project_date TEXT, security_level TEXT, page_count INTEGER, retention_period TEXT, carrier_form TEXT, remarks TEXT ) ''') self.conn.commit() except Exception as e: messagebox.showerror("数据库错误", f"初始化数据库失败: {str(e)}") traceback.print_exc() sys.exit(1) def create_widgets(self): """创建现代化界面组件""" # 主布局容器 main_frame = tk.Frame(self.root, bg="#f0f2f5") main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) # 顶部标题栏 header_frame = tk.Frame(main_frame, bg="#4285f4", height=60) header_frame.pack(fill=tk.X) header_frame.pack_propagate(False) title_label = tk.Label(header_frame, text="档案管理系统", font=("Microsoft YaHei", 18, "bold"), bg="#4285f4", fg="white") title_label.pack(pady=15) # 功能区 function_frame = tk.Frame(main_frame, bg="#f0f2f5", pady=15) function_frame.pack(fill=tk.X) # 左侧分类选择 category_frame = tk.Frame(function_frame, bg="#f0f2f5") category_frame.pack(side=tk.LEFT, padx=(0, 20)) ttk.Label(category_frame, text="档案类别:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.category_var = tk.StringVar(value="文书类") category_container = tk.Frame(category_frame, bg="#f0f2f5") category_container.pack(side=tk.LEFT) doc_radio = ttk.Radiobutton( category_container, text="文书类", variable=self.category_var, value="文书类", command=self.on_category_change, style="TRadiobutton" ) inf_radio = ttk.Radiobutton( category_container, text="基建类", variable=self.category_var, value="基建类", command=self.on_category_change, style="TRadiobutton" ) doc_radio.pack(side=tk.LEFT, padx=10) inf_radio.pack(side=tk.LEFT, padx=10) # 搜索区 search_frame = tk.Frame(function_frame, bg="#f0f2f5") search_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 20)) ttk.Label(search_frame, text="搜索:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.search_var = tk.StringVar() self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, style="TEntry", font=("Microsoft YaHei", 10)) self.search_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True, ipady=2) self.search_entry.bind("<Return>", lambda event: self.search_data()) # 搜索按钮 search_btn = ttk.Button(search_frame, text="搜索", command=self.search_data, style="Primary.TButton") search_btn.pack(side=tk.LEFT, padx=5) # 重置按钮 reset_btn = ttk.Button(search_frame, text="重置", command=self.reset_search, style="Primary.TButton") reset_btn.pack(side=tk.LEFT) # 右侧操作按钮 button_frame = tk.Frame(function_frame, bg="#f0f2f5") # 修复此处颜色代码 button_frame.pack(side=tk.RIGHT) self.add_btn = ttk.Button(button_frame, text="添加", command=self.add_record, style="Primary.TButton") self.add_btn.pack(side=tk.LEFT, padx=5) self.edit_btn = ttk.Button(button_frame, text="修改", command=self.edit_record, style="Primary.TButton") self.edit_btn.pack(side=tk.LEFT, padx=5) self.delete_btn = ttk.Button(button_frame, text="删除", command=self.delete_record, style="Danger.TButton") self.delete_btn.pack(side=tk.LEFT, padx=5) # 表格区域 table_frame = tk.Frame(main_frame, bg="#e0e0e0", padx=1, pady=1) table_frame.pack(fill=tk.BOTH, expand=True) # 表格标题栏 table_header = tk.Frame(table_frame, bg="#f8f9fa", height=40) table_header.pack(fill=tk.X) table_header.pack_propagate(False) self.table_title = tk.Label(table_header, text=f"{self.current_category}档案列表", font=("Microsoft YaHei", 11, "bold"), bg="#f8f9fa", fg="#5f6368") self.table_title.pack(side=tk.LEFT, padx=15) # 表格容器 table_container = tk.Frame(table_frame, bg="white", bd=1, relief=tk.SOLID) table_container.pack(fill=tk.BOTH, expand=True) # 滚动条 scrollbar_x = ttk.Scrollbar(table_container, orient=tk.HORIZONTAL) scrollbar_y = ttk.Scrollbar(table_container, orient=tk.VERTICAL) # 表格 self.tree = ttk.Treeview( table_container, columns=self.get_columns(), show="headings", yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set, style="Treeview" ) # 配置列 columns = self.get_columns() for col in columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 布局 scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y) scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar_y.config(command=self.tree.yview) scrollbar_x.config(command=self.tree.xview) # 绑定事件 self.tree.bind("<Double-1>", lambda e: self.edit_record()) # 分页控件 pagination_frame = tk.Frame(main_frame, bg="#f0f2f5", height=40, pady=10) pagination_frame.pack(fill=tk.X) # 上一页按钮 self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, style="Page.TButton", width=8) self.prev_btn.pack(side=tk.LEFT, padx=5) # 页码标签 self.page_label = ttk.Label(pagination_frame, text="1/1", style="Page.TLabel") self.page_label.pack(side=tk.LEFT, padx=10) # 下一页按钮 self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, style="Page.TButton", width=8) self.next_btn.pack(side=tk.LEFT, padx=5) # 跳转输入框 ttk.Label(pagination_frame, text="跳转至:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_var = tk.StringVar() self.page_entry = ttk.Entry(pagination_frame, textvariable=self.page_var, width=5, font=("Microsoft YaHei", 9)) self.page_entry.pack(side=tk.LEFT) # 跳转按钮 goto_btn = ttk.Button(pagination_frame, text="跳转", command=self.goto_page, style="Page.TButton", width=6) goto_btn.pack(side=tk.LEFT, padx=5) # 每页显示数量 ttk.Label(pagination_frame, text="每页显示:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_size_var = tk.StringVar(value=str(self.page_size)) page_size_combo = ttk.Combobox(pagination_frame, textvariable=self.page_size_var, width=5, font=("Microsoft YaHei", 9), state="readonly") page_size_combo['values'] = ('10', '20', '50', '100') page_size_combo.pack(side=tk.LEFT) page_size_combo.bind("<<ComboboxSelected>>", self.change_page_size) # 页脚 footer_frame = tk.Frame(main_frame, bg="#f0f2f5", height=30) footer_frame.pack(fill=tk.X, pady=(10, 0)) # 左侧状态信息 self.status_var = tk.StringVar(value="记录数量:0") status_label = ttk.Label( footer_frame, textvariable=self.status_var, style="Page.TLabel", anchor=tk.W ) status_label.pack(side=tk.LEFT, padx=20, pady=5) # 右侧版权信息 footer_label = ttk.Label( footer_frame, text="矿调所大数据中心 © 2025 开发人员:袁沙", style="Page.TLabel" ) footer_label.pack(side=tk.RIGHT, padx=20, pady=5) def center_window(self, window, width, height): """使窗口居中显示""" screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = (screen_width - width) // 2 y = (screen_height - height) // 2 - 20 # 稍微上移一点 window.geometry(f"{width}x{height}+{x}+{y}") window.focus_set() def get_columns(self): """获取当前类别的列""" if self.current_category == "文书类": return ["序号", "档号", "文号", "责任者", "题名", "日期", "密级", "页数", "保存期限", "载体形式", "备注"] else: return ["序号", "档号", "文号", "责任者", "题名", "日期", "工程日期", "密级", "页数", "保存期限", "载体形式", "备注"] def on_category_change(self): """类别改变时更新界面""" self.current_category = self.category_var.get() self.current_page = 1 # 重置到第一页 # 更新表格标题 self.table_title.config(text=f"{self.current_category}档案列表") # 清空表格数据 for item in self.tree.get_children(): self.tree.delete(item) # 重新设置表格列 new_columns = self.get_columns() self.tree["columns"] = new_columns # 配置新列 for col in new_columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 重新加载对应类别的数据 self.load_data() def load_data(self, search_term=None): """加载数据,使用参数化查询防止SQL注入""" try: # 清空表格 for item in self.tree.get_children(): self.tree.delete(item) # 根据当前类别查询对应表的数据 if self.current_category == "文书类": query = """ SELECT id, file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, remarks FROM documents """ count_query = "SELECT COUNT(*) FROM documents" else: query = """ SELECT id, file_number, document_number, responsible, title, date, project_date, security_level, page_count, retention_period, carrier_form, remarks FROM infrastructure """ count_query = "SELECT COUNT(*) FROM infrastructure" # 添加搜索条件 params = () if search_term: query += " WHERE file_number LIKE ? OR title LIKE ? OR responsible LIKE ? OR document_number LIKE ?" count_query += " WHERE file_number LIKE ? OR title LIKE ? OR responsible LIKE ? OR document_number LIKE ?" search_pattern = f"%{search_term}%" params = (search_pattern, search_pattern, search_pattern, search_pattern) # 计算总记录数 self.cursor.execute(count_query, params) self.total_records = self.cursor.fetchone()[0] # 计算总页数 self.total_pages = max(1, (self.total_records + self.page_size - 1) // self.page_size) # 确保当前页在有效范围内 if self.current_page > self.total_pages: self.current_page = max(1, self.total_pages) # 添加分页限制 offset = (self.current_page - 1) * self.page_size query += " ORDER BY id LIMIT ? OFFSET ?" params = params + (self.page_size, offset) self.cursor.execute(query, params) records = self.cursor.fetchall() # 更新状态栏 if search_term: self.status_var.set(f"搜索到 {self.total_records} 条记录") else: self.status_var.set(f"总记录:{self.total_records} 条,当前显示第 {(self.current_page-1)*self.page_size+1} 到 {min(self.current_page*self.page_size, self.total_records)} 条") # 更新分页控件状态 self.prev_btn.state(["!disabled" if self.current_page > 1 else "disabled"]) self.next_btn.state(["!disabled" if self.current_page < self.total_pages else "disabled"]) self.page_label.config(text=f"{self.current_page}/{self.total_pages}") # 插入数据 start_index = (self.current_page - 1) * self.page_size + 1 for i, record in enumerate(records, start_index): values = (i,) + record[1:] tag = "even" if i % 2 == 0 else "odd" self.tree.insert("", tk.END, values=values, tags=(record[0], tag)) # 配置行样式 self.tree.tag_configure("odd", background="white") self.tree.tag_configure("even", background="#f8f9fa") except Exception as e: messagebox.showerror("数据库错误", f"加载数据失败: {str(e)}") def search_data(self): """搜索当前类别的数据""" search_term = self.search_var.get().strip() self.current_page = 1 # 搜索后重置到第一页 self.load_data(search_term) def reset_search(self): """重置搜索""" self.search_var.set("") self.current_page = 1 self.load_data() def change_page_size(self, event=None): """更改每页显示数量""" try: new_size = int(self.page_size_var.get()) if new_size > 0: self.page_size = new_size self.current_page = 1 # 重置到第一页 self.load_data() except ValueError: pass def prev_page(self): """上一页""" if self.current_page > 1: self.current_page -= 1 self.load_data(self.search_var.get().strip()) def next_page(self): """下一页""" if self.current_page < self.total_pages: self.current_page += 1 self.load_data(self.search_var.get().strip()) def goto_page(self): """跳转到指定页码""" try: page_num = int(self.page_var.get()) if 1 <= page_num <= self.total_pages: self.current_page = page_num self.load_data(self.search_var.get().strip()) else: messagebox.showwarning("提示", f"请输入1到{self.total_pages}之间的页码") except ValueError: messagebox.showwarning("提示", "请输入有效的页码数字") def add_record(self): """添加记录对话框 - 两列布局""" dialog = tk.Toplevel(self.root) dialog.title(f"添加{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"添加{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 修复此处颜色代码 # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for col in left_columns: frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for col in right_columns: frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) entries[col] = entry # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) # 交换按钮顺序:先保存后取消 ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=True), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def edit_record(self): """编辑记录对话框 - 两列布局""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] current_values = self.tree.item(selected_item, "values") # 创建对话框 dialog = tk.Toplevel(self.root) dialog.title(f"修改{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"修改{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 values = current_values[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for i, col in enumerate(left_columns): frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 if i < len(values): entry.insert(0, values[i]) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for i, col in enumerate(right_columns): frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 idx = len(left_columns) + i if idx < len(values): entry.insert(0, values[idx]) entries[col] = entry # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) # 交换按钮顺序:先保存后取消 ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=False, record_id=record_id), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def save_record(self, entries, dialog, is_new=True, record_id=None): """保存记录(新增或修改)""" try: # 收集数据 data = {} for col, entry in entries.items(): if isinstance(entry, tk.Text): # 处理备注字段(Text控件) data[col] = entry.get("1.0", tk.END).strip() else: data[col] = entry.get().strip() # 验证 if not data["档号"]: messagebox.showerror("错误", "档号不能为空!") return if not data["题名"]: messagebox.showerror("错误", "题名不能为空!") return if is_new: # 新增记录到当前类别的对应表 if self.current_category == "文书类": self.cursor.execute(''' INSERT INTO documents (file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, remarks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"] )) else: self.cursor.execute(''' INSERT INTO infrastructure (file_number, document_number, responsible, title, date, project_date, security_level, page_count, retention_period, carrier_form, remarks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["工程日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"] )) else: # 修改当前类别对应表中的记录 if self.current_category == "文书类": self.cursor.execute(''' UPDATE documents SET file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, security_level = ?, page_count = ?, retention_period = ?, carrier_form = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"], record_id )) else: self.cursor.execute(''' UPDATE infrastructure SET file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, project_date = ?, security_level = ?, page_count = ?, retention_period = ?, carrier_form = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["工程日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"], record_id )) self.conn.commit() dialog.destroy() self.load_data() messagebox.showinfo("成功", "记录保存成功!") except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}") def delete_record(self): """删除记录""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return if messagebox.askyesno("确认删除", "确定要删除选中的记录吗?\n此操作不可恢复。"): selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] try: if self.current_category == "文书类": self.cursor.execute("DELETE FROM documents WHERE id = ?", (record_id,)) else: self.cursor.execute("DELETE FROM infrastructure WHERE id = ?", (record_id,)) self.conn.commit() self.load_data() messagebox.showinfo("成功", "记录已删除") except Exception as e: messagebox.showerror("错误", f"删除失败:{str(e)}") if __name__ == "__main__": try: root = tk.Tk() app = FileManagementSystem(root) root.mainloop() except Exception as e: messagebox.showerror("系统错误", f"程序发生致命错误: {str(e)}") traceback.print_exc()
07-29
#将按钮上的字调成深色,以与浅色背景区分开,发现其它有错也一并改了 import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext import ollama import os import time import threading import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure import pandas as pd import seaborn as sns import PyPDF2 import docx import markdown from bs4 import BeautifulSoup import openpyxl from PIL import Image import pytesseract import io import psutil from ttkthemes import ThemedTk # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 class RAGApplication: def __init__(self, root): self.root = root self.root.title("✨智能RAG应用系统✨") self.root.geometry("1400x900") self.root.configure(bg="#f0f0f0") # 淡灰色背景 # 设置现代主题 self.style = ttk.Style() self.style.theme_use('arc') # 使用现代主题 # 自定义样式 - 淡色调 self.style.configure('TFrame', background='#f0f0f0') self.style.configure('TLabel', background='#f0f0f0', foreground='#333333') self.style.configure('TLabelframe', background='#f0f0f0', foreground='#333333', borderwidth=1) self.style.configure('TLabelframe.Label', background='#f0f0f0', foreground='#4dabf5') # 淡蓝色标题 self.style.configure('TButton', background='#4dabf5', foreground='white', borderwidth=1) # 淡青色按钮 self.style.map('TButton', background=[('active', '#3b99e0')]) self.style.configure('TNotebook', background='#f0f0f0', borderwidth=0) self.style.configure('TNotebook.Tab', background='#e6f0ff', foreground='#333333', padding=[10, 5]) # 淡蓝色标签 self.style.map('TNotebook.Tab', background=[('selected', '#4dabf5')]) # 初始化数据 self.documents = [] self.chunks = [] self.embeddings = [] self.qa_history = [] # 模型配置 self.models = { "llm": "gemma3:27b", "embedding": "bge-m3:latest" } # 默认参数 self.params = { "temperature": 0.7, "top_p": 0.9, "max_length": 2048, "num_context_docs": 3, "chunk_size": 500, "chunk_overlap": 100, "chunk_strategy": "固定大小", "separators": "\n\n\n。\n!\n?\n", "embed_batch_size": 1, "enable_stream": True, "show_progress": True, "show_visualization": True, "ocr_enabled": True } # 创建界面 self.create_ui() def create_ui(self): # 主框架 self.main_frame = ttk.Frame(self.root) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20) # 标题 title_frame = ttk.Frame(self.main_frame) title_frame.pack(fill=tk.X, pady=(0, 20)) ttk.Label(title_frame, text="✨ 智能RAG应用系统 ✨", font=('Arial', 24, 'bold'), foreground="#4dabf5").pack(side=tk.LEFT) # 淡青色标题 # 状态指示器 status_frame = ttk.Frame(title_frame) status_frame.pack(side=tk.RIGHT) self.status_label = ttk.Label(status_frame, text="● 就绪", foreground="#28a745") # 绿色状态 self.status_label.pack(side=tk.RIGHT, padx=10) # 参数控制面板 self.create_sidebar() # 主内容区域 self.create_main_content() def create_sidebar(self): # 侧边栏框架 self.sidebar = ttk.LabelFrame(self.main_frame, text="⚙️ 参数控制面板", width=300) self.sidebar.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10) # 大模型参数 ttk.Label(self.sidebar, text="🔧 大模型参数", font=('Arial', 10, 'bold'), foreground="#333333").pack(pady=(15, 5)) self.temperature = tk.DoubleVar(value=self.params["temperature"]) ttk.Label(self.sidebar, text="温度(temperature)").pack(anchor=tk.W, padx=10) temp_frame = ttk.Frame(self.sidebar) temp_frame.pack(fill=tk.X, padx=10, pady=(0, 5)) ttk.Scale(temp_frame, from_=0.0, to=2.0, variable=self.temperature, length=180, command=lambda v: self.update_param("temperature", float(v))).pack(side=tk.LEFT) self.temp_label = ttk.Label(temp_frame, text=f"{self.temperature.get():.1f}", width=5) self.temp_label.pack(side=tk.RIGHT, padx=5) self.top_p = tk.DoubleVar(value=self.params["top_p"]) ttk.Label(self.sidebar, text="Top P").pack(anchor=tk.W, padx=10) top_p_frame = ttk.Frame(self.sidebar) top_p_frame.pack(fill=tk.X, padx=10, pady=(0, 5)) ttk.Scale(top_p_frame, from_=0.0, to=1.0, variable=self.top_p, length=180, command=lambda v: self.update_param("top_p", float(v))).pack(side=tk.LEFT) self.top_p_label = ttk.Label(top_p_frame, text=f"{self.top_p.get():.2f}", width=5) self.top_p_label.pack(side=tk.RIGHT, padx=5) # RAG参数 ttk.Label(self.sidebar, text="\n🔧 RAG参数", font=('Arial', 10, 'bold'), foreground="#333333").pack(pady=(15, 5)) self.chunk_size = tk.IntVar(value=self.params["chunk_size"]) ttk.Label(self.sidebar, text="分块大小(字符)").pack(anchor=tk.W, padx=10) chunk_frame = ttk.Frame(self.sidebar) chunk_frame.pack(fill=tk.X, padx=10, pady=(0, 5)) ttk.Scale(chunk_frame, from_=100, to=2000, variable=self.chunk_size, length=180, command=lambda v: self.update_param("chunk_size", int(v))).pack(side=tk.LEFT) self.chunk_label = ttk.Label(chunk_frame, text=f"{self.chunk_size.get()}", width=5) self.chunk_label.pack(side=tk.RIGHT, padx=5) # OCR开关 self.ocr_var = tk.BooleanVar(value=self.params["ocr_enabled"]) ttk.Checkbutton(self.sidebar, text="启用OCR扫描", variable=self.ocr_var, command=lambda: self.update_param("ocr_enabled", self.ocr_var.get())).pack(pady=(15, 5), padx=10, anchor=tk.W) # 使用说明 ttk.Label(self.sidebar, text="\n📖 使用说明", font=('Arial', 10, 'bold'), foreground="#333333").pack(pady=(15, 5)) instructions = """1. 在"文档上传"页上传您的文档 2. 在"文档处理"页对文档进行分块和嵌入 3. 在"问答交互"页提问并获取答案 4. 在"系统监控"页查看系统状态""" ttk.Label(self.sidebar, text=instructions, justify=tk.LEFT, background="#e6f0ff", # 淡蓝色背景 foreground="#333333", padding=10).pack(fill=tk.X, padx=10, pady=5) def create_main_content(self): # 主内容框架 self.content_frame = ttk.Frame(self.main_frame) self.content_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 创建选项卡 self.notebook = ttk.Notebook(self.content_frame) self.notebook.pack(fill=tk.BOTH, expand=True) # 文档上传页 self.create_upload_tab() # 文档处理页 self.create_process_tab() # 问答交互页 self.create_qa_tab() # 系统监控页 self.create_monitor_tab() def create_upload_tab(self): self.upload_tab = ttk.Frame(self.notebook) self.notebook.add(self.upload_tab, text="📤 文档上传") # 标题 title_frame = ttk.Frame(self.upload_tab) title_frame.pack(fill=tk.X, pady=(10, 20)) ttk.Label(title_frame, text="📤 文档上传与管理", font=('Arial', 14, 'bold'), foreground="#4dabf5").pack(side=tk.LEFT) # 淡青色标题 # 上传区域 upload_frame = ttk.Frame(self.upload_tab) upload_frame.pack(fill=tk.X, pady=10) # 上传按钮 upload_btn = ttk.Button(upload_frame, text="📁 上传文档", command=self.upload_files, style='Accent.TButton') upload_btn.pack(side=tk.LEFT, padx=10) # 清除按钮 clear_btn = ttk.Button(upload_frame, text="🗑️ 清除所有", command=self.clear_documents) clear_btn.pack(side=tk.RIGHT, padx=10) # 文档列表 self.doc_list_frame = ttk.LabelFrame(self.upload_tab, text="📋 已上传文档") self.doc_list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 创建带滚动条的树状视图 tree_frame = ttk.Frame(self.doc_list_frame) tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建滚动条 tree_scroll = ttk.Scrollbar(tree_frame) tree_scroll.pack(side=tk.RIGHT, fill=tk.Y) # 创建树状视图 columns = ("name", "size", "time", "type") self.doc_tree = ttk.Treeview(tree_frame, columns=columns, show="headings", yscrollcommand=tree_scroll.set, height=8) # 设置列标题 self.doc_tree.heading("name", text="文件名") self.doc_tree.heading("size", text="大小") self.doc_tree.heading("time", text="上传时间") self.doc_tree.heading("type", text="类型") # 设置列宽 self.doc_tree.column("name", width=250) self.doc_tree.column("size", width=80) self.doc_tree.column("time", width=150) self.doc_tree.column("type", width=80) self.doc_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tree_scroll.config(command=self.doc_tree.yview) # 文档统计 self.doc_stats_frame = ttk.Frame(self.upload_tab) self.doc_stats_frame.pack(fill=tk.X, pady=10, padx=10) stats_style = ttk.Style() stats_style.configure('Stats.TLabel', background='#e6f0ff', foreground='#333333', padding=5) # 淡蓝色背景 ttk.Label(self.doc_stats_frame, text="📊 文档统计:", style='Stats.TLabel').pack(side=tk.LEFT, padx=5) self.doc_count_label = ttk.Label(self.doc_stats_frame, text="0", style='Stats.TLabel') self.doc_count_label.pack(side=tk.LEFT, padx=5) ttk.Label(self.doc_stats_frame, text="总字符数:", style='Stats.TLabel').pack(side=tk.LEFT, padx=5) self.char_count_label = ttk.Label(self.doc_stats_frame, text="0", style='Stats.TLabel') self.char_count_label.pack(side=tk.LEFT, padx=5) ttk.Label(self.doc_stats_frame, text="总页数:", style='Stats.TLabel').pack(side=tk.LEFT, padx=5) self.page_count_label = ttk.Label(self.doc_stats_frame, text="0", style='Stats.TLabel') self.page_count_label.pack(side=tk.LEFT, padx=5) def clear_documents(self): if not self.documents: return if messagebox.askyesno("确认", "确定要清除所有文档吗?"): self.documents = [] self.update_doc_list() # ================== 文件读取函数 ================== def read_pdf(self, filepath): """读取PDF文件内容,支持扫描版OCR""" content = "" pages = 0 try: with open(filepath, 'rb') as f: reader = PyPDF2.PdfReader(f) num_pages = len(reader.pages) pages = num_pages for page_num in range(num_pages): page = reader.pages[page_num] text = page.extract_text() # 如果是扫描版PDF,使用OCR识别 if not text.strip() and self.params["ocr_enabled"]: try: # 获取页面图像 images = page.images if images: for img in images: image_data = img.data image = Image.open(io.BytesIO(image_data)) text += pytesseract.image_to_string(image, lang='chi_sim+eng') except Exception as e: print(f"OCR处理失败: {str(e)}") content += text + "\n" except Exception as e: print(f"读取PDF失败: {str(e)}") return content, pages def read_docx(self, filepath): """读取Word文档内容""" content = "" pages = 0 try: doc = docx.Document(filepath) for para in doc.paragraphs: content += para.text + "\n" pages = len(doc.paragraphs) // 50 + 1 # 估算页数 except Exception as e: print(f"读取Word文档失败: {str(e)}") return content, pages def read_excel(self, filepath): """读取Excel文件内容,优化内存使用""" content = "" pages = 0 try: # 使用openpyxl优化大文件读取 wb = openpyxl.load_workbook(filepath, read_only=True) for sheet_name in wb.sheetnames: content += f"\n工作表: {sheet_name}\n" sheet = wb[sheet_name] for row in sheet.iter_rows(values_only=True): row_content = " | ".join([str(cell) if cell is not None else "" for cell in row]) content += row_content + "\n" pages = len(wb.sheetnames) except Exception as e: print(f"读取Excel文件失败: {str(e)}") return content, pages def read_md(self, filepath): """读取Markdown文件内容""" content = "" pages = 0 try: with open(filepath, 'r', encoding='utf-8') as f: html = markdown.markdown(f.read()) soup = BeautifulSoup(html, 'html.parser') content = soup.get_text() pages = len(content) // 2000 + 1 # 估算页数 except Exception as e: print(f"读取Markdown文件失败: {str(e)}") return content, pages def read_ppt(self, filepath): """读取PPT文件内容(简化版)""" content = "" pages = 0 try: # 实际应用中应使用python-pptx库 # 这仅作演示 content = f"PPT文件内容提取: {os.path.basename(filepath)}" pages = 10 # 假设有10页 except Exception as e: print(f"读取PPT文件失败: {str(e)}") return content, pages def upload_files(self): filetypes = [ ("文本文件", "*.txt"), ("PDF文件", "*.pdf"), ("Word文件", "*.docx *.doc"), ("Excel文件", "*.xlsx *.xls"), ("Markdown文件", "*.md"), ("PPT文件", "*.pptx *.ppt"), ("所有文件", "*.*") ] filenames = filedialog.askopenfilenames(title="选择文档", filetypes=filetypes) if filenames: self.status_label.config(text="● 正在上传文档...", foreground="#ffc107") # 黄色状态 total_pages = 0 for filename in filenames: try: ext = os.path.splitext(filename)[1].lower() if ext == '.txt': with open(filename, 'r', encoding='utf-8') as f: content = f.read() pages = len(content) // 2000 + 1 elif ext == '.pdf': content, pages = self.read_pdf(filename) elif ext in ('.docx', '.doc'): content, pages = self.read_docx(filename) elif ext in ('.xlsx', '.xls'): content, pages = self.read_excel(filename) elif ext == '.md': content, pages = self.read_md(filename) elif ext in ('.pptx', '.ppt'): content, pages = self.read_ppt(filename) else: messagebox.showwarning("警告", f"不支持的文件类型: {ext}") continue # 处理字符编码问题 if not isinstance(content, str): try: content = content.decode('utf-8') except: content = content.decode('latin-1', errors='ignore') self.documents.append({ "name": os.path.basename(filename), "content": content, "size": len(content), "upload_time": time.strftime("%Y-%m-%d %H:%M:%S"), "type": ext.upper().replace(".", ""), "pages": pages }) total_pages += pages # 更新文档列表 self.update_doc_list() except Exception as e: messagebox.showerror("错误", f"无法读取文件 {filename}: {str(e)}") self.status_label.config(text=f"● 上传完成! 共{len(filenames)}个文档", foreground="#28a745") # 绿色状态 self.page_count_label.config(text=str(total_pages)) def update_doc_list(self): # 清空现有列表 for item in self.doc_tree.get_children(): self.doc_tree.delete(item) # 添加新文档 for doc in self.documents: size_kb = doc["size"] / 1024 size_str = f"{size_kb:.1f} KB" if size_kb < 1024 else f"{size_kb / 1024:.1f} MB" self.doc_tree.insert("", tk.END, values=( doc["name"], size_str, doc["upload_time"], doc["type"] )) # 更新统计信息 self.doc_count_label.config(text=str(len(self.documents))) self.char_count_label.config(text=str(sum(d['size'] for d in self.documents))) def create_process_tab(self): self.process_tab = ttk.Frame(self.notebook) self.notebook.add(self.process_tab, text="🔧 文档处理") # 标题 title_frame = ttk.Frame(self.process_tab) title_frame.pack(fill=tk.X, pady=(10, 20)) ttk.Label(title_frame, text="🔧 文档处理与分块", font=('Arial', 14, 'bold'), foreground="#4dabf5").pack(side=tk.LEFT) # 淡青色标题 # 处理按钮 btn_frame = ttk.Frame(self.process_tab) btn_frame.pack(fill=tk.X, pady=10) process_btn = ttk.Button(btn_frame, text="🔄 处理文档", command=self.process_documents, style='Accent.TButton') process_btn.pack(side=tk.LEFT, padx=10) visualize_btn = ttk.Button(btn_frame, text="📊 更新可视化", command=self.show_visualizations) visualize_btn.pack(side=tk.LEFT, padx=10) # 主内容区域 content_frame = ttk.Frame(self.process_tab) content_frame.pack(fill=tk.BOTH, expand=True) # 左侧:可视化区域 self.visual_frame = ttk.LabelFrame(content_frame, text="📈 文档分析") self.visual_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10) # 右侧:分块列表 self.chunk_frame = ttk.LabelFrame(content_frame, text="📋 分块结果") self.chunk_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10) # 创建带滚动条的树状视图 tree_frame = ttk.Frame(self.chunk_frame) tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建滚动条 tree_scroll = ttk.Scrollbar(tree_frame) tree_scroll.pack(side=tk.RIGHT, fill=tk.Y) # 创建树状视图 columns = ("doc_name", "start", "end", "content") self.chunk_tree = ttk.Treeview(tree_frame, columns=columns, show="headings", yscrollcommand=tree_scroll.set, height=15) # 设置列标题 self.chunk_tree.heading("doc_name", text="来源文档") self.chunk_tree.heading("start", text="起始位置") self.chunk_tree.heading("end", text="结束位置") self.chunk_tree.heading("content", text="内容预览") # 设置列宽 self.chunk_tree.column("doc_name", width=150) self.chunk_tree.column("start", width=80) self.chunk_tree.column("end", width=80) self.chunk_tree.column("content", width=300) self.chunk_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tree_scroll.config(command=self.chunk_tree.yview) # 初始显示占位图 self.show_placeholder() def show_placeholder(self): """显示可视化占位图""" for widget in self.visual_frame.winfo_children(): widget.destroy() placeholder = ttk.Label(self.visual_frame, text="文档处理后将显示分析图表", font=('Arial', 12), foreground="#7f8c8d") placeholder.pack(expand=True, pady=50) def process_documents(self): if not self.documents: messagebox.showwarning("警告", "请先上传文档") return # 在新线程中处理文档 threading.Thread(target=self._process_documents_thread, daemon=True).start() def _process_documents_thread(self): # 显示进度条 self.progress_window = tk.Toplevel(self.root) self.progress_window.title("处理进度") self.progress_window.geometry("400x150") self.progress_window.resizable(False, False) self.progress_window.transient(self.root) self.progress_window.grab_set() self.progress_window.configure(bg="#f0f0f0") # 淡灰色背景 # 设置窗口居中 x = self.root.winfo_x() + (self.root.winfo_width() - 400) // 2 y = self.root.winfo_y() + (self.root.winfo_height() - 150) // 2 self.progress_window.geometry(f"+{x}+{y}") # 进度窗口内容 ttk.Label(self.progress_window, text="正在处理文档...", font=('Arial', 11)).pack(pady=(20, 10)) progress_frame = ttk.Frame(self.progress_window) progress_frame.pack(fill=tk.X, padx=20, pady=10) self.progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var, maximum=100, length=360) progress_bar.pack() self.progress_label = ttk.Label(progress_frame, text="0%") self.progress_label.pack(pady=5) self.status_label.config(text="● 正在处理文档...", foreground="#ffc107") # 黄色状态 self.progress_window.update() try: # 分块处理 self.chunks = self.chunk_documents( self.documents, self.params["chunk_strategy"], self.params["chunk_size"], self.params["chunk_overlap"] ) # 生成嵌入 self.embeddings = self.generate_embeddings(self.chunks) # 更新UI self.root.after(0, self.update_chunk_list) self.root.after(0, self.show_visualizations) self.root.after(0, lambda: messagebox.showinfo("完成", "文档处理完成!")) self.status_label.config(text="● 文档处理完成", foreground="#28a745") # 绿色状态 except Exception as e: self.root.after(0, lambda: messagebox.showerror("错误", f"处理文档时出错: {str(e)}")) self.status_label.config(text="● 处理出错", foreground="#dc3545") # 红色状态 finally: self.root.after(0, self.progress_window.destroy) def chunk_documents(self, documents, strategy, size, overlap): chunks = [] total_docs = len(documents) for doc_idx, doc in enumerate(documents): content = doc['content'] if strategy == "固定大小": for i in range(0, len(content), size - overlap): chunk = content[i:i + size] chunks.append({ "doc_name": doc['name'], "content": chunk, "start": i, "end": min(i + size, len(content)) }) # 更新进度 progress = (doc_idx + 1) / total_docs * 100 self.progress_var.set(progress) self.progress_label.config(text=f"{int(progress)}%") self.progress_window.update() return chunks def generate_embeddings(self, chunks): """修复:单批次处理每个分块,避免API参数类型错误""" embeddings = [] total_chunks = len(chunks) for idx, chunk in enumerate(chunks): try: # 修复:传递单个字符串而不是列表 response = ollama.embeddings( model=self.models['embedding'], prompt=chunk['content'] # 单个字符串 ) embeddings.append({ "chunk_id": idx, "embedding": response['embedding'], "doc_name": chunk['doc_name'] }) except Exception as e: print(f"生成嵌入时出错: {str(e)}") # 添加空嵌入占位符 embeddings.append({ "chunk_id": idx, "embedding": None, "doc_name": chunk['doc_name'] }) # 更新进度 progress = (idx + 1) / total_chunks * 100 self.progress_var.set(progress) self.progress_label.config(text=f"{int(progress)}%") self.progress_window.update() # 添加延迟避免请求过快 time.sleep(0.1) return embeddings def update_chunk_list(self): # 清空现有列表 for item in self.chunk_tree.get_children(): self.chunk_tree.delete(item) # 添加新分块 for chunk in self.chunks: preview = chunk['content'][:50] + "..." if len(chunk['content']) > 50 else chunk['content'] self.chunk_tree.insert("", tk.END, values=( chunk['doc_name'], chunk['start'], chunk['end'], preview )) def show_visualizations(self): # 清空可视化区域 for widget in self.visual_frame.winfo_children(): widget.destroy() if not self.params["show_visualization"] or not self.chunks: self.show_placeholder() return # 创建图表框架 fig = plt.Figure(figsize=(10, 8), dpi=100) fig.set_facecolor('#f0f0f0') # 淡灰色背景 # 分块大小分布 ax1 = fig.add_subplot(221) ax1.set_facecolor('#e6f0ff') # 淡蓝色背景 chunk_sizes = [len(c['content']) for c in self.chunks] sns.histplot(chunk_sizes, bins=20, ax=ax1, color='#4dabf5') # 淡青色 ax1.set_title("分块大小分布", color='#333333') ax1.set_xlabel("字符数", color='#333333') ax1.set_ylabel("数量", color='#333333') ax1.tick_params(axis='x', colors='#333333') ax1.tick_params(axis='y', colors='#333333') ax1.spines['bottom'].set_color('#333333') ax1.spines['left'].set_color('#333333') # 文档分块数量 ax2 = fig.add_subplot(222) ax2.set_facecolor('#e6f0ff') # 淡蓝色背景 doc_chunk_counts = {} for chunk in self.chunks: doc_chunk_counts[chunk['doc_name']] = doc_chunk_counts.get(chunk['doc_name'], 0) + 1 # 只显示前10个文档 doc_names = list(doc_chunk_counts.keys()) counts = list(doc_chunk_counts.values()) if len(doc_names) > 10: # 按分块数量排序,取前10 sorted_indices = np.argsort(counts)[::-1][:10] doc_names = [doc_names[i] for i in sorted_indices] counts = [counts[i] for i in sorted_indices] sns.barplot(x=counts, y=doc_names, ax=ax2, palette='Blues', orient='h') # 蓝色调 ax2.set_title("各文档分块数量", color='#333333') ax2.set_xlabel("分块数", color='#333333') ax2.set_ylabel("") ax2.tick_params(axis='x', colors='#333333') ax2.tick_params(axis='y', colors='#333333') ax2.spines['bottom'].set_color('#333333') ax2.spines['left'].set_color('#333333') # 内容词云(模拟) ax3 = fig.add_subplot(223) ax3.set_facecolor('#e6f0ff') # 淡蓝色背景 ax3.set_title("内容关键词分布", color='#333333') ax3.text(0.5, 0.5, "关键词可视化区域", horizontalalignment='center', verticalalignment='center', color='#333333', fontsize=12) ax3.axis('off') # 处理进度 ax4 = fig.add_subplot(224) ax4.set_facecolor('#e6f0ff') # 淡蓝色背景 ax4.set_title("处理进度", color='#333333') # 模拟数据 stages = ['上传', '分块', '嵌入', '完成'] progress = [100, 100, 100, 100] # 假设都已完成 ax4.barh(stages, progress, color=['#4dabf5', '#20c997', '#9b59b6', '#ffc107']) # 淡色系 ax4.set_xlim(0, 100) ax4.set_xlabel("完成百分比", color='#333333') ax4.tick_params(axis='x', colors='#333333') ax4.tick_params(axis='y', colors='#333333') ax4.spines['bottom'].set_color('#333333') ax4.spines['left'].set_color('#333333') # 调整布局 fig.tight_layout(rect=[0, 0, 1, 0.95], pad=3.0) # 添加总标题 fig.suptitle("文档分析概览", fontsize=16, color='#333333') # 在Tkinter中显示图表 canvas = FigureCanvasTkAgg(fig, master=self.visual_frame) canvas.draw() canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def create_qa_tab(self): self.qa_tab = ttk.Frame(self.notebook) self.notebook.add(self.qa_tab, text="💬 问答交互") # 标题 title_frame = ttk.Frame(self.qa_tab) title_frame.pack(fill=tk.X, pady=(10, 20)) ttk.Label(title_frame, text="💬 问答交互", font=('Arial', 14, 'bold'), foreground="#4dabf5").pack(side=tk.LEFT) # 淡青色标题 # 主内容区域 main_frame = ttk.Frame(self.qa_tab) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) # 左侧:问答区域 left_frame = ttk.Frame(main_frame) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 问题输入 self.question_frame = ttk.LabelFrame(left_frame, text="❓ 输入问题") self.question_frame.pack(fill=tk.X, padx=5, pady=5) self.question_text = scrolledtext.ScrolledText(self.question_frame, height=8, wrap=tk.WORD, font=('Arial', 11)) self.question_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.question_text.focus_set() # 提交按钮 btn_frame = ttk.Frame(left_frame) btn_frame.pack(fill=tk.X, pady=10) submit_btn = ttk.Button(btn_frame, text="🚀 提交问题", command=self.submit_question, style='Accent.TButton') submit_btn.pack(side=tk.LEFT, padx=5) clear_btn = ttk.Button(btn_frame, text="🗑️ 清除问题", command=self.clear_question) clear_btn.pack(side=tk.LEFT, padx=5) # 回答显示 self.answer_frame = ttk.LabelFrame(left_frame, text="💡 回答") self.answer_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.answer_text = scrolledtext.ScrolledText(self.answer_frame, state=tk.DISABLED, wrap=tk.WORD, font=('Arial', 11)) self.answer_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧:问答历史 right_frame = ttk.Frame(main_frame, width=400) # 在创建时设置宽度 right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=False, padx=5, pady=5) self.history_frame = ttk.LabelFrame(right_frame, text="🕒 问答历史") self.history_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建带滚动条的树状视图 tree_frame = ttk.Frame(self.history_frame) tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建滚动条 tree_scroll = ttk.Scrollbar(tree_frame) tree_scroll.pack(side=tk.RIGHT, fill=tk.Y) # 创建树状视图 columns = ("question", "time") self.history_tree = ttk.Treeview(tree_frame, columns=columns, show="headings", yscrollcommand=tree_scroll.set, height=20) # 设置列标题 self.history_tree.heading("question", text="问题") self.history_tree.heading("time", text="时间") # 设置列宽 self.history_tree.column("question", width=250) self.history_tree.column("time", width=120) self.history_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tree_scroll.config(command=self.history_tree.yview) # 历史操作按钮 history_btn_frame = ttk.Frame(right_frame) history_btn_frame.pack(fill=tk.X, pady=10) view_btn = ttk.Button(history_btn_frame, text="👁️ 查看详情", command=lambda: self.show_history_detail(None)) view_btn.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) clear_history_btn = ttk.Button(history_btn_frame, text="🗑️ 清除历史", command=self.clear_history) clear_history_btn.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) # 绑定双击事件查看历史详情 self.history_tree.bind("<Double-1>", self.show_history_detail) def clear_question(self): self.question_text.delete("1.0", tk.END) def clear_history(self): if not self.qa_history: return if messagebox.askyesno("确认", "确定要清除所有问答历史吗?"): self.qa_history = [] self.update_history_list() def submit_question(self): question = self.question_text.get("1.0", tk.END).strip() if not question: messagebox.showwarning("警告", "问题不能为空") return # 在新线程中处理问题 threading.Thread(target=self._submit_question_thread, args=(question,), daemon=True).start() def _submit_question_thread(self, question): try: # 显示进度窗口 self.progress_window = tk.Toplevel(self.root) self.progress_window.title("处理中...") self.progress_window.geometry("400x150") self.progress_window.resizable(False, False) self.progress_window.transient(self.root) self.progress_window.grab_set() self.progress_window.configure(bg="#f0f0f0") # 淡灰色背景 # 设置窗口居中 x = self.root.winfo_x() + (self.root.winfo_width() - 400) // 2 y = self.root.winfo_y() + (self.root.winfo_height() - 150) // 2 self.progress_window.geometry(f"+{x}+{y}") # 进度窗口内容 ttk.Label(self.progress_window, text="正在思考中...", font=('Arial', 11)).pack(pady=(20, 10)) progress_frame = ttk.FFrame(self.progress_window) progress_frame.pack(fill=tk.X, padx=20, pady=10) self.progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var, maximum=100, length=360) progress_bar.pack() self.progress_label = ttk.Label(progress_frame, text="0%") self.progress_label.pack(pady=5) self.status_label.config(text="● 正在处理问题...", foreground="#ffc107") # 黄色状态 self.progress_window.update() # 检索相关文档块 relevant_chunks = self.retrieve_relevant_chunks(question, self.params["num_context_docs"]) # 构建上下文 context = "\n\n".join([ f"文档: {c['doc_name']}\n内容: {c['content']}\n相关性: {c['similarity']:.4f}" for c in relevant_chunks ]) # 调用大模型生成回答 prompt = f"""基于以下上下文,回答问题。如果答案不在上下文中,请回答"我不知道"。 上下文: {context} 问题: {question} 回答:""" # 更新进度 self.progress_var.set(50) self.progress_label.config(text="50%") self.progress_window.update() # 流式输出或一次性输出 self.root.after(0, self.answer_text.config, {'state': tk.NORMAL}) self.root.after(0, self.answer_text.delete, "1.0", tk.END) if self.params["enable_stream"]: full_response = "" for chunk in ollama.generate( model=self.models['llm'], prompt=prompt, stream=True, options={ 'temperature': self.params["temperature"], 'top_p': self.params["top_p"], 'num_ctx': self.params["max_length"] } ): full_response += chunk['response'] self.root.after(0, self.answer_text.insert, tk.END, chunk['response']) self.root.after(0, self.answer_text.see, tk.END) self.root.after(0, self.answer_text.update) # 更新进度 if len(full_response) > 0: progress = min(50 + len(full_response) / 200, 99) self.progress_var.set(progress) self.progress_label.config(text=f"{int(progress)}%") self.progress_window.update() else: response = ollama.generate( model=self.models['llm'], prompt=prompt, options={ 'temperature': self.params["temperature"], 'top_p': self.params["top_p"], 'num_ctx': self.params["max_length"] } ) full_response = response['response'] self.root.after(0, self.answer_text.insert, tk.END, full_response) # 记录问答历史 self.qa_history.append({ "question": question, "answer": full_response, "context": context, "time": time.strftime("%Y-%m-%d %H:%M:%S") }) # 更新历史列表 self.root.after(0, self.update_history_list) # 完成 self.progress_var.set(100) self.progress_label.config(text="100%") self.status_label.config(text="● 问题处理完成", foreground="#28a745") # 绿色状态 self.root.after(1000, self.progress_window.destroy) except Exception as e: self.root.after(0, lambda: messagebox.showerror("错误", f"处理问题时出错: {str(e)}")) self.root.after(0, self.progress_window.destroy) self.status_label.config(text="● 处理出错", foreground="#dc3545") # 红色状态 def retrieve_relevant_chunks(self, query, k): """修复:处理嵌入为None的情况""" # 生成查询的嵌入 query_embedding = ollama.embeddings( model=self.models['embedding'], prompt=query )['embedding'] # 注意:返回的是字典中的'embedding'字段 # 计算相似度 similarities = [] for emb in self.embeddings: # 跳过无效的嵌入 if emb['embedding'] is None: continue # 计算余弦相似度 similarity = np.dot(query_embedding, emb['embedding']) similarities.append({ 'chunk_id': emb['chunk_id'], 'similarity': similarity, 'doc_name': emb['doc_name'] }) # 按相似度排序并返回前k个 top_chunks = sorted(similarities, key=lambda x: x['similarity'], reverse=True)[:k] return [{ **self.chunks[c['chunk_id']], 'similarity': c['similarity'] } for c in top_chunks] def update_history_list(self): # 清空现有列表 for item in self.history_tree.get_children(): self.history_tree.delete(item) # 添加新历史记录 for i, qa in enumerate(reversed(self.qa_history)): # 截断长问题 question = qa["question"] if len(question) > 50: question = question[:47] + "..." self.history_tree.insert("", tk.END, values=(question, qa["time"])) def show_history_detail(self, event): selected_item = self.history_tree.selection() if not selected_item: return item = self.history_tree.item(selected_item) question = item['values'][0] # 查找对应的问答记录 for qa in reversed(self.qa_history): if qa["question"].startswith(question) or question.startswith(qa["question"][:50]): # 显示详情窗口 detail_window = tk.Toplevel(self.root) detail_window.title("问答详情") detail_window.geometry("900x700") detail_window.configure(bg='#f0f0f0') # 淡灰色背景 # 设置窗口居中 x = self.root.winfo_x() + (self.root.winfo_width() - 900) // 2 y = self.root.winfo_y() + (self.root.winfo_height() - 700) // 2 detail_window.geometry(f"+{x}+{y}") # 问题 ttk.Label(detail_window, text="问题:", font=('Arial', 12, 'bold'), foreground="#4dabf5").pack(pady=(15, 5), padx=20, anchor=tk.W) question_frame = ttk.Frame(detail_window) question_frame.pack(fill=tk.X, padx=20, pady=(0, 10)) question_text = scrolledtext.ScrolledText(question_frame, wrap=tk.WORD, height=3, font=('Arial', 11)) question_text.insert(tk.INSERT, qa["question"]) question_text.config(state=tk.DISABLED) question_text.pack(fill=tk.X) # 回答 ttk.Label(detail_window, text="回答:", font=('Arial', 12, 'bold'), foreground="#4dabf5").pack(pady=(15, 5), padx=20, anchor=tk.W) answer_frame = ttk.Frame(detail_window) answer_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 10)) answer_text = scrolledtext.ScrolledText(answer_frame, wrap=tk.WORD, font=('Arial', 11)) answer_text.insert(tk.INSERT, qa["answer"]) answer_text.config(state=tk.DISABLED) answer_text.pack(fill=tk.BOTH, expand=True) # 上下文 ttk.Label(detail_window, text="上下文:", font=('Arial', 12, 'bold'), foreground="#4dabf5").pack(pady=(15, 5), padx=20, anchor=tk.W) context_frame = ttk.Frame(detail_window) context_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20)) context_text = scrolledtext.ScrolledText(context_frame, wrap=tk.WORD, font=('Arial', 10)) context_text.insert(tk.INSERT, qa["context"]) context_text.config(state=tk.DISABLED) context_text.pack(fill=tk.BOTH, expand=True) break def create_monitor_tab(self): self.monitor_tab = ttk.Frame(self.notebook) self.notebook.add(self.monitor_tab, text="📊 系统监控") # 标题 title_frame = ttk.Frame(self.monitor_tab) title_frame.pack(fill=tk.X, pady=(10, 20)) ttk.Label(title_frame, text="📊 系统监控", font=('Arial', 14, 'bold'), foreground="#4dabf5").pack(side=tk.LEFT) # 主内容区域 main_frame = ttk.Frame(self.monitor_tab) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) # 修复:main极值rame → main_frame # 左侧:资源监控 left_frame = ttk.Frame(main_frame) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 资源使用 self.resource_frame = ttk.LabelFrame(left_frame, text="📈 资源使用") self.resource_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # CPU使用 cpu_frame = ttk.Frame(self.resource_frame) cpu_frame.pack(fill=tk.X, padx=10, pady=10) ttk.Label(cpu_frame, text="CPU使用率:").pack(side=tk.LEFT) self.cpu_value = ttk.Label(cpu_frame, text="0%", width=5) self.cpu_value.pack(side=tk.RIGHT, padx=10) self.cpu_usage = ttk.Progressbar(self.resource_frame, length=400, mode='determinate') self.cpu_usage.pack(fill=tk.X, padx=10, pady=(0, 10)) # 内存使用 mem_frame = ttk.Frame(self.resource_frame) mem_frame.pack(fill=tk.X, padx=10, pady=10) ttk.Label(mem_frame, text="内存使用率:").pack(side=tk.LEFT) self.mem_value = ttk.Label(mem_frame, text="0%", width=5) self.mem_value.pack(side=tk.RIGHT, padx=10) self.mem_usage = ttk.Progressbar(self.resource_frame, length=400, mode='determinate') self.mem_usage.pack(fill=tk.X, padx=10, pady=(0, 10)) # 磁盘使用 disk_frame = ttk.Frame(self.resource_frame) disk_frame.pack(fill=tk.X, padx=10, pady=10) ttk.Label(disk_frame, text="磁盘使用率:").pack(side=tk.LEFT) self.disk_value = ttk.Label(disk_frame, text="0%", width=5) self.disk_value.pack(side=tk.RIGHT, padx=10) self.disk_usage = ttk.Progressbar(self.resource_frame, length=400, mode='determinate') self.disk_usage.pack(fill=tk.X, padx=10, pady=(0, 10)) # 右侧:模型状态 right_frame = ttk.Frame(main_frame) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 模型状态 self.model_frame = ttk.LabelFrame(right_frame, text="🤖 模型状态") self.model_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) btn_frame = ttk.Frame(self.model_frame) btn_frame.pack(fill=tk.X, padx=10, pady=10) ttk.Button(btn_frame, text="🔄 检查模型状态", command=self.check_model_status).pack() self.model_status_text = scrolledtext.ScrolledText(self.model_frame, height=15, state=tk.DISABLED, font=('Consolas', 10)) self.model_status_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10)) # 性能统计 self.perf_frame = ttk.LabelFrame(left_frame, text="⚡ 性能统计") self.perf_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建图表 fig = Figure(figsize=(8, 4), dpi=100) fig.set_facecolor('#f0f0f0') # 淡灰色背景 self.ax = fig.add_subplot(111) self.ax.set_facecolor('#e6f0ff') # 淡蓝色背景 self.ax.set_title("CPU使用率历史", color='#333333') self.ax.set_xlabel("时间", color='#333333') self.ax.set_ylabel("使用率(%)", color='#333333') self.ax.tick_params(axis='x', colors='#333333') self.ax.tick_params(axis='y', colors='#333333') self.ax.spines['bottom'].set_color('#333333') self.ax.spines['left'].set_color('#333333') self.cpu_history = [] self.line, = self.ax.plot([], [], color='#4dabf5', marker='o', markersize=4) # 淡青色线条 self.ax.set_ylim(0, 100) canvas = FigureCanvasTkAgg(fig, master=self.perf_frame) canvas.draw() canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 开始更新资源使用情况 self.update_resource_usage() def update_resource_usage(self): # 获取真实资源数据 cpu_percent = psutil.cpu_percent() mem_percent = psutil.virtual_memory().percent disk_percent = psutil.disk_usage('/').percent # 更新进度条 self.cpu_usage['value'] = cpu_percent self.mem_usage['value'] = mem_percent self.disk_usage['value'] = disk_percent # 更新数值标签 self.cpu_value.config(text=f"{cpu_percent}%") self.mem_value.config(text=f"{mem_percent}%") self.disk_value.config(text=f"{disk_percent}%") # 更新CPU历史图表 self.cpu_history.append(cpu_percent) if len(self.cpu_history) > 20: self.cpu_history.pop(0) self.line.set_data(range(len(self.cpu_history)), self.cpu_history) self.ax.set_xlim(0, max(10, len(self.cpu_history))) self.ax.figure.canvas.draw() # 5秒后再次更新 self.root.after(5000, self.update_resource_usage) def check_model_status(self): try: self.model_status_text.config(state=tk.NORMAL) self.model_status_text.delete("1.0", tk.END) # 添加加载动画 self.model_status_text.insert(tk.INSERT, "正在检查模型状态...") self.model_status_text.update() # 模拟检查过程 time.sleep(1) # 清空并插入真实信息 self.model_status_text.delete("1.0", tk.END) llm_info = ollama.show(self.models['llm']) embed_info = ollama.show(self.models['embedding']) status_text = f"""✅ 大模型信息: 名称: {self.models['llm']} 参数大小: {llm_info.get('size', '未知')} 最后使用时间: {llm_info.get('modified_at', '未知')} 支持功能: {llm_info.get('capabilities', '未知')} ✅ 嵌入模型信息: 名称: {self.models['embedding']} 参数大小: {embed_info.get('size', '未知')} 最后使用时间: {embed_info.get('modified_at', '未知')} 支持功能: {embed_info.get('capabilities', '未知')} ⏱️ 最后检查时间: {time.strftime("%Y-%m-%d %H:%M:%S")} """ self.model_status_text.insert(tk.INSERT, status_text) self.model_status_text.config(state=tk.DISABLED) self.status_label.config(text="● 模型状态检查完成", foreground="#28a745") # 绿色状态 except Exception as e: self.model_status_text.config(state=tk.NORMAL) self.model_status_text.delete("1.0", tk.END) self.model_status_text.insert(tk.INSERT, f"❌ 检查模型状态时出错: {str(e)}") self.model_status_text.config(state=tk.DISABLED) self.status_label.config(text="● 模型检查出错", foreground="#dc3545") # 红色状态 def update_param(self, param, value): self.params[param] = value # 更新标签显示 if param == "temperature": self.temp_label.config(text=f"{value:.1f}") elif param == "top_p": self.top_p_label.config(text=f"{value:.2f}") elif param == "chunk_size": self.chunk_label.config(text=f"{value}") # 运行应用程序 if __name__ == "__main__": root = ThemedTk(theme="arc") # 使用现代主题 app = RAGApplication(root) root.mainloop()
最新发布
08-07
优化以下代码,1、点击按钮时,不要出现虚线框,点击之后展示另一个背景色 2、添加档案和修改档案的界面,备注字段只用一个,不要展示在中间,文书类展示在左边即可,基建类展示在右边 3、 保存和取消按钮顺序换一下,保存在前,取消在后import tkinter as tk from tkinter import ttk, messagebox, scrolledtext import sqlite3 import os import sys import traceback class FileManagementSystem: def init(self, root): # 主窗口设置 self.root = root self.root.title(“档案管理系统”) self.root.geometry(“1200x750”) self.root.minsize(1000, 650) self.root.configure(bg=“#f0f2f5”) # 设置应用程序图标 self.set_app_icon() # 样式配置 self.setup_styles() # 初始化数据库 self.init_database() # 当前类别 self.current_category = "文书类" # 分页设置 self.current_page = 1 self.page_size = 20 # 每页显示20条记录 self.total_records = 0 self.total_pages = 1 # 创建界面组件 self.create_widgets() # 加载数据 self.load_data() def set_app_icon(self): """设置应用程序图标(如果有图标文件)""" try: if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) icon_path = os.path.join(base_dir, "archive_icon.ico") if os.path.exists(icon_path): self.root.iconbitmap(icon_path) except: pass def setup_styles(self): """设置现代化界面样式""" self.style = ttk.Style() # 基础字体设置 self.style.configure(".", font=("Microsoft YaHei", 10)) # 主题配置 self.style.theme_use("clam") # 统一按钮样式 self.style.configure("Primary.TButton", font=("Microsoft YaHei", 10), padding=8, background="#4285f4", foreground="white", borderwidth=1, relief="flat", width=10) self.style.map("Primary.TButton", background=[("active", "#3367d6"), ("pressed", "#2850b3")]) # 删除按钮使用不同颜色 self.style.configure("Danger.TButton", font=("Microsoft YaHei", 10), padding=8, background="#ea4335", foreground="white", borderwidth=1, relief="flat", width=10) self.style.map("Danger.TButton", background=[("active", "#d93025"), ("pressed", "#b31412")]) # 表格样式 self.style.configure("Treeview", background="white", foreground="#202124", rowheight=30, fieldbackground="white", borderwidth=1, relief="solid") self.style.configure("Treeview.Heading", background="#f8f9fa", foreground="#5f6368", font=("Microsoft YaHei", 10, "bold"), padding=8, relief="flat") self.style.map("Treeview.Heading", background=[("active", "#e8eaed")]) self.style.map("Treeview", background=[("selected", "#e8f0fe")], foreground=[("selected", "#202124")]) # 单选按钮样式 self.style.configure("TRadiobutton", background="#f0f2f5", font=("Microsoft YaHei", 10), foreground="#5f6368") self.style.map("TRadiobutton", foreground=[("selected", "#4285f4")]) # 输入框样式 self.style.configure("TEntry", fieldbackground="white", borderwidth=1, relief="solid", padding=5) # 必填输入框样式 self.style.configure("Required.TEntry", fieldbackground="#fff8f8", borderwidth=1, relief="solid", padding=5) # 标签样式 self.style.configure("Header.TLabel", font=("Microsoft YaHei", 16, "bold"), background="#4285f4", foreground="white") self.style.configure("Section.TLabel", font=("Microsoft YaHei", 10, "bold"), background="#f0f2f5", foreground="#5f6368") # 分页按钮样式 self.style.configure("Page.TButton", font=("Microsoft YaHei", 9), padding=4, background="#e8eaed", foreground="#5f6368", borderwidth=1, relief="flat") self.style.map("Page.TButton", background=[("active", "#d7d9dd"), ("pressed", "#c5c7cb")]) # 分页标签样式 self.style.configure("Page.TLabel", background="#f0f2f5", foreground="#5f6368", font=("Microsoft YaHei", 9)) def init_database(self): """初始化数据库,处理打包路径问题""" try: # 获取可执行文件所在目录(打包后) if getattr(sys, 'frozen', False): base_dir = os.path.dirname(sys.executable) else: base_dir = os.path.dirname(os.path.abspath(__file__)) # 确保目录存在 os.makedirs(base_dir, exist_ok=True) db_path = os.path.join(base_dir, "file_management.db") self.conn = sqlite3.connect(db_path) self.cursor = self.conn.cursor() # 创建文书类表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, security_level TEXT, page_count INTEGER, retention_period TEXT, carrier_form TEXT, remarks TEXT ) ''') # 创建基建类表 self.cursor.execute(''' CREATE TABLE IF NOT EXISTS infrastructure ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_number TEXT NOT NULL, document_number TEXT, responsible TEXT, title TEXT NOT NULL, date TEXT, project_date TEXT, security_level TEXT, page_count INTEGER, retention_period TEXT, carrier_form TEXT, remarks TEXT ) ''') self.conn.commit() except Exception as e: messagebox.showerror("数据库错误", f"初始化数据库失败: {str(e)}") traceback.print_exc() sys.exit(1) def create_widgets(self): """创建现代化界面组件""" # 主布局容器 main_frame = tk.Frame(self.root, bg="#f0f2f5") main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) # 顶部标题栏 header_frame = tk.Frame(main_frame, bg="#4285f4", height=60) header_frame.pack(fill=tk.X) header_frame.pack_propagate(False) title_label = tk.Label(header_frame, text="档案管理系统", font=("Microsoft YaHei", 18, "bold"), bg="#4285f4", fg="white") title_label.pack(pady=15) # 功能区 function_frame = tk.Frame(main_frame, bg="#f0f2f5", pady=15) function_frame.pack(fill=tk.X) # 左侧分类选择 category_frame = tk.Frame(function_frame, bg="#f0f2f5") category_frame.pack(side=tk.LEFT, padx=(0, 20)) ttk.Label(category_frame, text="档案类别:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.category_var = tk.StringVar(value="文书类") category_container = tk.Frame(category_frame, bg="#f0f2f5") category_container.pack(side=tk.LEFT) doc_radio = ttk.Radiobutton( category_container, text="文书类", variable=self.category_var, value="文书类", command=self.on_category_change, style="TRadiobutton" ) inf_radio = ttk.Radiobutton( category_container, text="基建类", variable=self.category_var, value="基建类", command=self.on_category_change, style="TRadiobutton" ) doc_radio.pack(side=tk.LEFT, padx=10) inf_radio.pack(side=tk.LEFT, padx=10) # 搜索区 search_frame = tk.Frame(function_frame, bg="#f0f2f5") search_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 20)) ttk.Label(search_frame, text="搜索:", style="Section.TLabel").pack(side=tk.LEFT, padx=5) self.search_var = tk.StringVar() self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, style="TEntry", font=("Microsoft YaHei", 10)) self.search_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True, ipady=2) self.search_entry.bind("<Return>", lambda event: self.search_data()) # 搜索按钮 search_btn = ttk.Button(search_frame, text="搜索", command=self.search_data, style="Primary.TButton") search_btn.pack(side=tk.LEFT, padx=5) # 重置按钮 reset_btn = ttk.Button(search_frame, text="重置", command=self.reset_search, style="Primary.TButton") reset_btn.pack(side=tk.LEFT) # 右侧操作按钮 button_frame = tk.Frame(function_frame, bg="#f0f2f5") # 修复此处颜色代码 button_frame.pack(side=tk.RIGHT) self.add_btn = ttk.Button(button_frame, text="添加", command=self.add_record, style="Primary.TButton") self.add_btn.pack(side=tk.LEFT, padx=5) self.edit_btn = ttk.Button(button_frame, text="修改", command=self.edit_record, style="Primary.TButton") self.edit_btn.pack(side=tk.LEFT, padx=5) self.delete_btn = ttk.Button(button_frame, text="删除", command=self.delete_record, style="Danger.TButton") self.delete_btn.pack(side=tk.LEFT, padx=5) # 表格区域 table_frame = tk.Frame(main_frame, bg="#e0e0e0", padx=1, pady=1) table_frame.pack(fill=tk.BOTH, expand=True) # 表格标题栏 table_header = tk.Frame(table_frame, bg="#f8f9fa", height=40) table_header.pack(fill=tk.X) table_header.pack_propagate(False) self.table_title = tk.Label(table_header, text=f"{self.current_category}档案列表", font=("Microsoft YaHei", 11, "bold"), bg="#f8f9fa", fg="#5f6368") self.table_title.pack(side=tk.LEFT, padx=15) # 表格容器 table_container = tk.Frame(table_frame, bg="white", bd=1, relief=tk.SOLID) table_container.pack(fill=tk.BOTH, expand=True) # 滚动条 scrollbar_x = ttk.Scrollbar(table_container, orient=tk.HORIZONTAL) scrollbar_y = ttk.Scrollbar(table_container, orient=tk.VERTICAL) # 表格 self.tree = ttk.Treeview( table_container, columns=self.get_columns(), show="headings", yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set, style="Treeview" ) # 配置列 columns = self.get_columns() for col in columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 布局 scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y) scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar_y.config(command=self.tree.yview) scrollbar_x.config(command=self.tree.xview) # 绑定事件 self.tree.bind("<Double-1>", lambda e: self.edit_record()) # 分页控件 pagination_frame = tk.Frame(main_frame, bg="#f0f2f5", height=40, pady=10) pagination_frame.pack(fill=tk.X) # 上一页按钮 self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, style="Page.TButton", width=8) self.prev_btn.pack(side=tk.LEFT, padx=5) # 页码标签 self.page_label = ttk.Label(pagination_frame, text="1/1", style="Page.TLabel") self.page_label.pack(side=tk.LEFT, padx=10) # 下一页按钮 self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, style="Page.TButton", width=8) self.next_btn.pack(side=tk.LEFT, padx=5) # 跳转输入框 ttk.Label(pagination_frame, text="跳转至:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_var = tk.StringVar() self.page_entry = ttk.Entry(pagination_frame, textvariable=self.page_var, width=5, font=("Microsoft YaHei", 9)) self.page_entry.pack(side=tk.LEFT) # 跳转按钮 goto_btn = ttk.Button(pagination_frame, text="跳转", command=self.goto_page, style="Page.TButton", width=6) goto_btn.pack(side=tk.LEFT, padx=5) # 每页显示数量 ttk.Label(pagination_frame, text="每页显示:", style="Page.TLabel").pack(side=tk.LEFT, padx=(20, 5)) self.page_size_var = tk.StringVar(value=str(self.page_size)) page_size_combo = ttk.Combobox(pagination_frame, textvariable=self.page_size_var, width=5, font=("Microsoft YaHei", 9), state="readonly") page_size_combo['values'] = ('10', '20', '50', '100') page_size_combo.pack(side=tk.LEFT) page_size_combo.bind("<<ComboboxSelected>>", self.change_page_size) # 页脚 footer_frame = tk.Frame(main_frame, bg="#f0f2f5", height=30) footer_frame.pack(fill=tk.X, pady=(10, 0)) # 左侧状态信息 self.status_var = tk.StringVar(value="记录数量:0") status_label = ttk.Label( footer_frame, textvariable=self.status_var, style="Page.TLabel", anchor=tk.W ) status_label.pack(side=tk.LEFT, padx=20, pady=5) # 右侧版权信息 footer_label = ttk.Label( footer_frame, text="矿调所大数据中心 © 2025 开发人员:袁沙", style="Page.TLabel" ) footer_label.pack(side=tk.RIGHT, padx=20, pady=5) def center_window(self, window, width, height): """使窗口居中显示""" screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = (screen_width - width) // 2 y = (screen_height - height) // 2 - 20 # 稍微上移一点 window.geometry(f"{width}x{height}+{x}+{y}") window.focus_set() def get_columns(self): """获取当前类别的列""" if self.current_category == "文书类": return ["序号", "档号", "文号", "责任者", "题名", "日期", "密级", "页数", "保存期限", "载体形式", "备注"] else: return ["序号", "档号", "文号", "责任者", "题名", "日期", "工程日期", "密级", "页数", "保存期限", "载体形式", "备注"] def on_category_change(self): """类别改变时更新界面""" self.current_category = self.category_var.get() self.current_page = 1 # 重置到第一页 # 更新表格标题 self.table_title.config(text=f"{self.current_category}档案列表") # 清空表格数据 for item in self.tree.get_children(): self.tree.delete(item) # 重新设置表格列 new_columns = self.get_columns() self.tree["columns"] = new_columns # 配置新列 for col in new_columns: self.tree.heading(col, text=col) width = 110 if col not in ["题名", "备注"] else 200 if col == "题名" else 150 self.tree.column(col, width=width, anchor=tk.CENTER, stretch=False) # 重新加载对应类别的数据 self.load_data() def load_data(self, search_term=None): """加载数据,使用参数化查询防止SQL注入""" try: # 清空表格 for item in self.tree.get_children(): self.tree.delete(item) # 根据当前类别查询对应表的数据 if self.current_category == "文书类": query = """ SELECT id, file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, remarks FROM documents """ count_query = "SELECT COUNT(*) FROM documents" else: query = """ SELECT id, file_number, document_number, responsible, title, date, project_date, security_level, page_count, retention_period, carrier_form, remarks FROM infrastructure """ count_query = "SELECT COUNT(*) FROM infrastructure" # 添加搜索条件 params = () if search_term: query += " WHERE file_number LIKE ? OR title LIKE ? OR responsible LIKE ? OR document_number LIKE ?" count_query += " WHERE file_number LIKE ? OR title LIKE ? OR responsible LIKE ? OR document_number LIKE ?" search_pattern = f"%{search_term}%" params = (search_pattern, search_pattern, search_pattern, search_pattern) # 计算总记录数 self.cursor.execute(count_query, params) self.total_records = self.cursor.fetchone()[0] # 计算总页数 self.total_pages = max(1, (self.total_records + self.page_size - 1) // self.page_size) # 确保当前页在有效范围内 if self.current_page > self.total_pages: self.current_page = max(1, self.total_pages) # 添加分页限制 offset = (self.current_page - 1) * self.page_size query += " ORDER BY id LIMIT ? OFFSET ?" params = params + (self.page_size, offset) self.cursor.execute(query, params) records = self.cursor.fetchall() # 更新状态栏 if search_term: self.status_var.set(f"搜索到 {self.total_records} 条记录,当前显示第 {(self.current_page-1)*self.page_size+1} 到 {min(self.current_page*self.page_size, self.total_records)} 条") else: self.status_var.set(f"总记录:{self.total_records} 条,当前显示第 {(self.current_page-1)*self.page_size+1} 到 {min(self.current_page*self.page_size, self.total_records)} 条") # 更新分页控件状态 self.prev_btn.state(["!disabled" if self.current_page > 1 else "disabled"]) self.next_btn.state(["!disabled" if self.current_page < self.total_pages else "disabled"]) self.page_label.config(text=f"{self.current_page}/{self.total_pages}") # 插入数据 start_index = (self.current_page - 1) * self.page_size + 1 for i, record in enumerate(records, start_index): values = (i,) + record[1:] tag = "even" if i % 2 == 0 else "odd" self.tree.insert("", tk.END, values=values, tags=(record[0], tag)) # 配置行样式 self.tree.tag_configure("odd", background="white") self.tree.tag_configure("even", background="#f8f9fa") except Exception as e: messagebox.showerror("数据库错误", f"加载数据失败: {str(e)}") def search_data(self): """搜索当前类别的数据""" search_term = self.search_var.get().strip() self.current_page = 1 # 搜索后重置到第一页 self.load_data(search_term) def reset_search(self): """重置搜索""" self.search_var.set("") self.current_page = 1 self.load_data() def change_page_size(self, event=None): """更改每页显示数量""" try: new_size = int(self.page_size_var.get()) if new_size > 0: self.page_size = new_size self.current_page = 1 # 重置到第一页 self.load_data() except ValueError: pass def prev_page(self): """上一页""" if self.current_page > 1: self.current_page -= 1 self.load_data(self.search_var.get().strip()) def next_page(self): """下一页""" if self.current_page < self.total_pages: self.current_page += 1 self.load_data(self.search_var.get().strip()) def goto_page(self): """跳转到指定页码""" try: page_num = int(self.page_var.get()) if 1 <= page_num <= self.total_pages: self.current_page = page_num self.load_data(self.search_var.get().strip()) else: messagebox.showwarning("提示", f"请输入1到{self.total_pages}之间的页码") except ValueError: messagebox.showwarning("提示", "请输入有效的页码数字") def add_record(self): """添加记录对话框 - 两列布局""" dialog = tk.Toplevel(self.root) dialog.title(f"添加{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"添加{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 修复此处颜色代码 # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for col in left_columns: frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for col in right_columns: frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 修复此处参数名 entries[col] = entry # 备注字段特殊处理(跨两列) if "备注" in entries: frame = tk.Frame(columns_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X, padx=10) tk.Label(frame, text="备注", width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg="#5f6368", anchor="w").pack(side=tk.LEFT) # 使用Text控件代替Entry,支持多行输入 remarks_text = scrolledtext.ScrolledText(frame, height=4, font=("Microsoft YaHei", 10), wrap=tk.WORD, padx=5, pady=5) remarks_text.pack(side=tk.RIGHT, fill=tk.X, expand=True, padx=(10, 0)) entries["备注"] = remarks_text # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=True), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def edit_record(self): """编辑记录对话框 - 两列布局""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] current_values = self.tree.item(selected_item, "values") # 创建对话框 dialog = tk.Toplevel(self.root) dialog.title(f"修改{self.current_category}档案") self.center_window(dialog, 800, 550) dialog.resizable(False, False) dialog.transient(self.root) dialog.grab_set() dialog.configure(bg="#f0f2f5") # 标题栏 header = tk.Frame(dialog, bg="#4285f4", height=50) header.pack(fill=tk.X) header.pack_propagate(False) tk.Label(header, text=f"修改{self.current_category}档案", font=("Microsoft YaHei", 14, "bold"), bg="#4285f4", fg="white").pack(pady=12) # 内容区域 content_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) content_frame.pack(fill=tk.BOTH, expand=True) # 创建双列容器 columns_frame = tk.Frame(content_frame, bg="#f0f2f5") columns_frame.pack(fill=tk.BOTH, expand=True) # 左列框架 left_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 右列框架 right_frame = tk.Frame(columns_frame, bg="#f0f2f5", padx=10) right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 输入字段 entries = {} columns = self.get_columns()[1:] # 排除序号 values = current_values[1:] # 排除序号 # 左列字段 left_columns = columns[:len(columns)//2] for i, col in enumerate(left_columns): frame = tk.Frame(left_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 if i < len(values): entry.insert(0, values[i]) entries[col] = entry # 右列字段 right_columns = columns[len(columns)//2:] for i, col in enumerate(right_columns): frame = tk.Frame(right_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X) label_text = f"{col}*" if col in ["档号", "题名"] else col label_color = "#ea4335" if col in ["档号", "题名"] else "#5f6368" tk.Label(frame, text=label_text, width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg=label_color, anchor="w").pack(side=tk.LEFT) entry_style = "Required.TEntry" if col in ["档号", "题名"] else "TEntry" entry = ttk.Entry(frame, font=("Microsoft YaHei", 10), style=entry_style) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True, ipady=3, padx=(10, 0)) # 填充现有值 idx = len(left_columns) + i if idx < len(values): entry.insert(0, values[idx]) entries[col] = entry # 备注字段特殊处理(跨两列) if "备注" in entries: frame = tk.Frame(columns_frame, bg="#f0f2f5", pady=8) frame.pack(fill=tk.X, padx=10) tk.Label(frame, text="备注", width=8, font=("Microsoft YaHei", 10), bg="#f0f2f5", fg="#5f6368", anchor="w").pack(side=tk.LEFT) # 使用Text控件代替Entry,支持多行输入 remarks_text = scrolledtext.ScrolledText(frame, height=4, font=("Microsoft YaHei", 10), wrap=tk.WORD, padx=5, pady=5) # 填充备注值 remarks_idx = len(columns) - 1 # 备注是最后一个字段 if remarks_idx < len(values): remarks_text.insert("1.0", values[remarks_idx]) remarks_text.pack(side=tk.RIGHT, fill=tk.X, expand=True, padx=(10, 0)) entries["备注"] = remarks_text # 按钮区域 btn_frame = tk.Frame(dialog, bg="#f0f2f5", padx=20, pady=15) btn_frame.pack(fill=tk.X) ttk.Button(btn_frame, text="保存", command=lambda: self.save_record(entries, dialog, is_new=False, record_id=record_id), style="Primary.TButton").pack(side=tk.RIGHT, padx=10) ttk.Button(btn_frame, text="取消", command=dialog.destroy, style="Primary.TButton").pack(side=tk.RIGHT) def save_record(self, entries, dialog, is_new=True, record_id=None): """保存记录(新增或修改)""" try: # 收集数据 data = {} for col, entry in entries.items(): if isinstance(entry, tk.Text): # 处理备注字段(Text控件) data[col] = entry.get("1.0", tk.END).strip() else: data[col] = entry.get().strip() # 验证 if not data["档号"]: messagebox.showerror("错误", "档号不能为空!") return if not data["题名"]: messagebox.showerror("错误", "题名不能为空!") return if is_new: # 新增记录到当前类别的对应表 if self.current_category == "文书类": self.cursor.execute(''' INSERT INTO documents (file_number, document_number, responsible, title, date, security_level, page_count, retention_period, carrier_form, remarks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"] )) else: self.cursor.execute(''' INSERT INTO infrastructure (file_number, document_number, responsible, title, date, project_date, security_level, page_count, retention_period, carrier_form, remarks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["工程日期"], data["密级"], data["页数"] if data["页数"] else None, data["保管期限"], data["载体形式"], data["备注"] )) else: # 修改当前类别对应表中的记录 if self.current_category == "文书类": self.cursor.execute(''' UPDATE documents SET file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, security_level = ?, page_count = ?, retention_period = ?, carrier_form = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["密级"], data["页数"] if data["页数"] else None, data["保存期限"], data["载体形式"], data["备注"], record_id )) else: self.cursor.execute(''' UPDATE infrastructure SET file_number = ?, document_number = ?, responsible = ?, title = ?, date = ?, project_date = ?, security_level = ?, page_count = ?, retention_period = ?, carrier_form = ?, remarks = ? WHERE id = ? ''', ( data["档号"], data["文号"], data["责任者"], data["题名"], data["日期"], data["工程日期"], data["密级"], data["页数"] if data["页数"] else None, data["保管期限"], data["载体形式"], data["备注"], record_id )) self.conn.commit() dialog.destroy() self.load_data() messagebox.showinfo("成功", "记录保存成功!") except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}") def delete_record(self): """删除记录""" selected_items = self.tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择一条记录!") return if messagebox.askyesno("确认删除", "确定要删除选中的记录吗?\n此操作不可恢复。"): selected_item = selected_items[0] record_id = self.tree.item(selected_item, "tags")[0] try: if self.current_category == "文书类": self.cursor.execute("DELETE FROM documents WHERE id = ?", (record_id,)) else: self.cursor.execute("DELETE FROM infrastructure WHERE id = ?", (record_id,)) self.conn.commit() self.load_data() messagebox.showinfo("成功", "记录已删除") except Exception as e: messagebox.showerror("错误", f"删除失败:{str(e)}") if name == “main”: try: root = tk.Tk() app = FileManagementSystem(root) root.mainloop() except Exception as e: messagebox.showerror(“系统错误”, f"程序发生致命错误: {str(e)}") traceback.print_exc()
07-26
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值