Python + MySQL + Tkinter 桌面版图书借阅管理系统
Python MySQL Tkinter 桌面版图书借阅管理系统这是一套经典的C/S架构桌面应用适合作为课程设计或毕业设计。下面我将从系统设计、数据库建模、核心功能代码、界面布局等方面为你完整呈现。️ 系统总体设计功能模块划分图书借阅管理系统 ├── 登录模块 ├── 图书管理 │ ├── 图书录入/编辑/删除 │ ├── 图书检索书名、作者、ISBN │ └── 库存查看 ├── 读者管理 │ ├── 读者注册/编辑/注销 │ └── 读者信息查询 ├── 借书管理 │ ├── 借书操作扫描ISBN读者证号 │ └── 续借操作 ├── 还书管理 │ ├── 还书操作 │ └── 逾期罚款计算 └── 统计报表 ├── 热门图书排行 ├── 读者借阅排行榜 └── 每日/月流通量技术栈层次技术客户端Python 3.10, Tkinter, ttkbootstrap美化数据库MySQL 8.0连接池PyMySQL 或 mysql-connector-python辅助库Pillow图片处理, pandas报表导出️ 数据库设计 (MySQL)-- 创建数据库 CREATE DATABASE library_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE library_db; -- 管理员表 CREATE TABLE admins ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(32) UNIQUE NOT NULL, password_hash VARCHAR(256) NOT NULL, real_name VARCHAR(50), role ENUM(admin,operator) DEFAULT operator ); -- 图书类别表 CREATE TABLE categories ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, parent_id INT DEFAULT NULL, FOREIGN KEY (parent_id) REFERENCES categories(id) ); -- 图书表 CREATE TABLE books ( id INT AUTO_INCREMENT PRIMARY KEY, isbn VARCHAR(20) UNIQUE NOT NULL, title VARCHAR(200) NOT NULL, author VARCHAR(100), publisher VARCHAR(100), publish_year YEAR, category_id INT, total_stock INT DEFAULT 1, -- 总库存 current_stock INT DEFAULT 1, -- 当前可借数量 location VARCHAR(50), -- 书架位置 cover_image BLOB, -- 封面图片二进制 description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_title (title), INDEX idx_isbn (isbn), FOREIGN KEY (category_id) REFERENCES categories(id) ); -- 读者表 CREATE TABLE readers ( id INT AUTO_INCREMENT PRIMARY KEY, reader_no VARCHAR(18) UNIQUE NOT NULL, -- 读者证号 name VARCHAR(50) NOT NULL, gender ENUM(男,女), phone VARCHAR(15), email VARCHAR(100), id_card VARCHAR(18), -- 身份证号 register_date DATE DEFAULT (CURRENT_DATE), max_borrow INT DEFAULT 5, -- 最大借阅数量 current_borrow INT DEFAULT 0, -- 当前已借数量 status ENUM(正常,挂失,注销) DEFAULT 正常 ); -- 借阅记录表 CREATE TABLE borrow_records ( id BIGINT AUTO_INCREMENT PRIMARY KEY, book_id INT NOT NULL, reader_id INT NOT NULL, borrow_date DATETIME DEFAULT CURRENT_TIMESTAMP, due_date DATE NOT NULL, -- 应还日期 return_date DATETIME DEFAULT NULL, -- 实际归还日期 renew_count INT DEFAULT 0, -- 续借次数 fine_amount DECIMAL(8,2) DEFAULT 0.00, -- 罚款金额 operator_id INT, -- 办理管理员ID status ENUM(借出,已还,逾期,丢失) DEFAULT 借出, FOREIGN KEY (book_id) REFERENCES books(id), FOREIGN KEY (reader_id) REFERENCES readers(id), FOREIGN KEY (operator_id) REFERENCES admins(id), INDEX idx_reader (reader_id), INDEX idx_book (book_id), INDEX idx_status (status) ); -- 罚款记录表 CREATE TABLE fines ( id INT AUTO_INCREMENT PRIMARY KEY, record_id BIGINT NOT NULL, amount DECIMAL(8,2) NOT NULL, paid TINYINT DEFAULT 0, pay_date DATETIME DEFAULT NULL, FOREIGN KEY (record_id) REFERENCES borrow_records(id) );️ Tkinter 界面设计与核心代码项目结构library_manager/ ├── main.py # 程序入口 ├── config.py # 数据库配置 ├── database.py # 数据库操作类 ├── models/ │ ├── book.py # 图书模型 │ ├── reader.py # 读者模型 │ └── borrow.py # 借阅模型 ├── views/ │ ├── login_window.py # 登录窗口 │ ├── main_window.py # 主界面包含菜单栏、工具栏 │ ├── book_management.py # 图书管理页面 │ ├── reader_management.py # 读者管理页面 │ ├── borrow_return.py # 借还书页面 │ └── statistics.py # 统计报表页面 ├── utils/ │ ├── validators.py # 输入验证 │ └── helpers.py # 辅助函数日期计算、罚款等 └── resources/ ├── icons/ # 图标文件 └── images/ # 默认封面等1. 数据库连接与操作基类 (database.py)import pymysql from config import DB_CONFIG class DatabaseManager: _instance None def __new__(cls): if cls._instance is None: cls._instance super().__new__(cls) cls._instance.connection None return cls._instance def connect(self): if not self.connection or not self.connection.open: self.connection pymysql.connect(**DB_CONFIG) return self.connection def execute_query(self, sql, paramsNone): conn self.connect() with conn.cursor(pymysql.cursors.DictCursor) as cursor: cursor.execute(sql, params) result cursor.fetchall() return result def execute_update(self, sql, paramsNone): conn self.connect() with conn.cursor() as cursor: affected cursor.execute(sql, params) conn.commit() return affected def close(self): if self.connection and self.connection.open: self.connection.close()2. 登录窗口 (views/login_window.py)import tkinter as tk from tkinter import messagebox from database import DatabaseManager import hashlib class LoginWindow: def __init__(self): self.window tk.Tk() self.window.title(图书借阅管理系统 - 登录) self.window.geometry(400x300) self.window.resizable(False, False) # 居中显示 screen_w self.window.winfo_screenwidth() screen_h self.window.winfo_screenheight() x (screen_w - 400) // 2 y (screen_h - 300) // 2 self.window.geometry(f{x}{y}) self.setup_ui() def setup_ui(self): # 标题 tk.Label(self.window, text图书借阅管理系统, font(微软雅黑, 18)).pack(pady30) # 用户名 frame_user tk.Frame(self.window) frame_user.pack(pady10) tk.Label(frame_user, text用户名:, font(微软雅黑, 12)).pack(sidetk.LEFT) self.entry_user tk.Entry(frame_user, font(微软雅黑, 12), width25) self.entry_user.pack(sidetk.LEFT, padx10) # 密码 frame_pass tk.Frame(self.window) frame_pass.pack(pady10) tk.Label(frame_pass, text密 码:, font(微软雅黑, 12)).pack(sidetk.LEFT) self.entry_pass tk.Entry(frame_pass, show*, font(微软雅黑, 12), width25) self.entry_pass.pack(sidetk.LEFT, padx10) # 登录按钮 btn_login tk.Button(self.window, text登 录, font(微软雅黑, 14), commandself.login, bg#4CAF50, fgwhite, width15) btn_login.pack(pady20) # 回车绑定 self.window.bind(Return, lambda e: self.login()) def login(self): username self.entry_user.get().strip() password self.entry_pass.get().strip() if not username or not password: messagebox.showwarning(提示, 请输入用户名和密码) return # 密码加密SHA256 pwd_hash hashlib.sha256(password.encode()).hexdigest() db DatabaseManager() sql SELECT id, username, real_name, role FROM admins WHERE username%s AND password_hash%s result db.execute_query(sql, (username, pwd_hash)) if result: admin_info result[0] self.window.destroy() from views.main_window import MainWindow MainWindow(admin_info).run() else: messagebox.showerror(错误, 用户名或密码错误)3. 主窗口 (views/main_window.py)import tkinter as tk from tkinter import ttk, Menu from views.book_management import BookManagement from views.reader_management import ReaderManagement from views.borrow_return import BorrowReturn from views.statistics import Statistics class MainWindow: def __init__(self, admin_info): self.admin admin_info self.root tk.Tk() self.root.title(f图书借阅管理系统 - 欢迎 {admin_info[real_name]}) self.root.geometry(1200x700) self.setup_menu() self.setup_notebook() self.setup_status_bar() def setup_menu(self): menubar Menu(self.root) self.root.config(menumenubar) # 系统菜单 sys_menu Menu(menubar, tearoff0) sys_menu.add_command(label修改密码, commandself.change_password) sys_menu.add_separator() sys_menu.add_command(label退出系统, commandself.exit_app) menubar.add_cascade(label系统, menusys_menu) # 帮助菜单 help_menu Menu(menubar, tearoff0) help_menu.add_command(label关于, commandself.show_about) menubar.add_cascade(label帮助, menuhelp_menu) def setup_notebook(self): self.notebook ttk.Notebook(self.root) self.notebook.pack(filltk.BOTH, expandTrue, padx5, pady5) # 添加各个功能标签页 self.book_mgr BookManagement(self.notebook, self.admin) self.reader_mgr ReaderManagement(self.notebook, self.admin) self.borrow_mgr BorrowReturn(self.notebook, self.admin) self.statistics Statistics(self.notebook, self.admin) self.notebook.add(self.book_mgr, text图书管理) self.notebook.add(self.reader_mgr, text读者管理) self.notebook.add(self.borrow_mgr, text借还书) self.notebook.add(self.statistics, text统计报表) def setup_status_bar(self): status_frame tk.Frame(self.root, relieftk.SUNKEN, bd1) status_frame.pack(sidetk.BOTTOM, filltk.X) self.lbl_admin tk.Label(status_frame, textf当前管理员: {self.admin[real_name]}, anchortk.W) self.lbl_admin.pack(sidetk.LEFT, padx5) self.lbl_time tk.Label(status_frame, anchortk.E) self.lbl_time.pack(sidetk.RIGHT, padx5) self.update_time() def update_time(self): from datetime import datetime now datetime.now().strftime(%Y-%m-%d %H:%M:%S) self.lbl_time.config(textnow) self.root.after(1000, self.update_time) def change_password(self): # 弹出修改密码对话框 pass def exit_app(self): if tk.messagebox.askokcancel(确认, 确定要退出系统吗): self.root.destroy() def show_about(self): tk.messagebox.showinfo(关于, 图书借阅管理系统 v1.0\nPython MySQL Tkinter) def run(self): self.root.mainloop()4. 图书管理页面 (views/book_management.py) 核心部分import tkinter as tk from tkinter import ttk, messagebox from database import DatabaseManager class BookManagement(tk.Frame): def __init__(self, parent, admin): super().__init__(parent) self.admin admin self.db DatabaseManager() self.setup_ui() self.load_books() def setup_ui(self): # 顶部搜索区域 search_frame tk.Frame(self) search_frame.pack(filltk.X, padx10, pady5) tk.Label(search_frame, text关键词:).pack(sidetk.LEFT) self.search_entry tk.Entry(search_frame, width30) self.search_entry.pack(sidetk.LEFT, padx5) self.search_entry.bind(KeyRelease, self.search_books) tk.Label(search_frame, text类别:).pack(sidetk.LEFT, padx(20,0)) self.category_combo ttk.Combobox(search_frame, statereadonly, width15) self.category_combo.pack(sidetk.LEFT, padx5) self.load_categories() self.category_combo.bind(ComboboxSelected, self.search_books) # 操作按钮 btn_frame tk.Frame(self) btn_frame.pack(filltk.X, padx10, pady5) tk.Button(btn_frame, text新增图书, commandself.add_book).pack(sidetk.LEFT, padx5) tk.Button(btn_frame, text编辑图书, commandself.edit_book).pack(sidetk.LEFT, padx5) tk.Button(btn_frame, text删除图书, commandself.delete_book).pack(sidetk.LEFT, padx5) tk.Button(btn_frame, text刷新, commandself.load_books).pack(sidetk.LEFT, padx5) # 表格显示 columns (id, isbn, title, author, publisher, stock, current, location) self.tree ttk.Treeview(self, columnscolumns, showheadings, selectmodebrowse) self.tree.heading(id, textID) self.tree.heading(isbn, textISBN) self.tree.heading(title, text书名) self.tree.heading(author, text作者) self.tree.heading(publisher, text出版社) self.tree.heading(stock, text总库存) self.tree.heading(current, text可借) self.tree.heading(location, text位置) self.tree.column(id, width50, anchortk.CENTER) self.tree.column(isbn, width150) self.tree.column(title, width250) self.tree.column(author, width100) self.tree.column(publisher, width150) self.tree.column(stock, width70, anchortk.CENTER) self.tree.column(current, width70, anchortk.CENTER) self.tree.column(location, width100) scrollbar ttk.Scrollbar(self, orienttk.VERTICAL, commandself.tree.yview) self.tree.configure(yscrollcommandscrollbar.set) self.tree.pack(sidetk.LEFT, filltk.BOTH, expandTrue, padx10, pady5) scrollbar.pack(sidetk.RIGHT, filltk.Y, pady5) self.tree.bind(Double-1, self.edit_book) def load_categories(self): sql SELECT id, name FROM categories cats self.db.execute_query(sql) names [全部] [c[name] for c in cats] self.category_combo[values] names self.category_combo.current(0) self.cat_map {c[name]: c[id] for c in cats} def load_books(self, keyword, cat_idNone): # 清空现有数据 for item in self.tree.get_children(): self.tree.delete(item) sql SELECT id, isbn, title, author, publisher, total_stock, current_stock, location FROM books WHERE 11 params [] if keyword: sql AND (title LIKE %s OR author LIKE %s OR isbn LIKE %s) kw f%{keyword}% params.extend([kw, kw, kw]) if cat_id: sql AND category_id %s params.append(cat_id) sql ORDER BY id DESC books self.db.execute_query(sql, tuple(params)) for b in books: self.tree.insert(, tk.END, values( b[id], b[isbn], b[title], b[author], b[publisher], b[total_stock], b[current_stock], b[location] )) def search_books(self, eventNone): keyword self.search_entry.get().strip() cat_name self.category_combo.get() cat_id self.cat_map.get(cat_name) if cat_name ! 全部 else None self.load_books(keyword, cat_id) def add_book(self): # 弹出新增对话框这里简化 from dialogs.book_dialog import BookDialog BookDialog(self, 新增图书, None, self.on_book_saved) def edit_book(self, eventNone): selected self.tree.selection() if not selected: messagebox.showwarning(提示, 请选择一本图书) return item self.tree.item(selected[0]) book_id item[values][0] from dialogs.book_dialog import BookDialog BookDialog(self, 编辑图书, book_id, self.on_book_saved) def delete_book(self): selected self.tree.selection() if not selected: messagebox.showwarning(提示, 请选择要删除的图书) return item self.tree.item(selected[0]) book_id item[values][0] title item[values][2] if messagebox.askyesno(确认删除, f确定要删除《{title}》吗\n此操作不可恢复): try: sql DELETE FROM books WHERE id %s self.db.execute_update(sql, (book_id,)) self.load_books() messagebox.showinfo(成功, 图书已删除) except Exception as e: messagebox.showerror(错误, f删除失败{str(e)}) def on_book_saved(self): self.load_books()5. 借还书页面核心逻辑 (views/borrow_return.py)import tkinter as tk from tkinter import ttk, messagebox from datetime import datetime, timedelta from database import DatabaseManager class BorrowReturn(tk.Frame): def __init__(self, parent, admin): super().__init__(parent) self.admin admin self.db DatabaseManager() self.setup_ui() def setup_ui(self): # 借书区域 borrow_frame tk.LabelFrame(self, text借书操作, padx10, pady10) borrow_frame.pack(filltk.X, padx10, pady5) tk.Label(borrow_frame, text读者证号:).grid(row0, column0, stickytk.W) self.entry_reader tk.Entry(borrow_frame, width25) self.entry_reader.grid(row0, column1, padx5) tk.Button(borrow_frame, text查询读者, commandself.query_reader).grid(row0, column2, padx5) tk.Label(borrow_frame, text图书ISBN:).grid(row1, column0, stickytk.W, pady10) self.entry_isbn tk.Entry(borrow_frame, width25) self.entry_isbn.grid(row1, column1, padx5) tk.Button(borrow_frame, text查询图书, commandself.query_book).grid(row1, column2, padx5) self.btn_borrow tk.Button(borrow_frame, text确认借书, commandself.do_borrow, bg#2196F3, fgwhite, width15) self.btn_borrow.grid(row2, column0, columnspan3, pady10) # 显示读者和图书信息 self.info_text tk.Text(borrow_frame, height4, width70, statetk.DISABLED) self.info_text.grid(row3, column0, columnspan3, pady5) # 还书区域 return_frame tk.LabelFrame(self, text还书操作, padx10, pady10) return_frame.pack(filltk.X, padx10, pady5) tk.Label(return_frame, text借阅记录ID:).pack(sidetk.LEFT) self.entry_record tk.Entry(return_frame, width20) self.entry_record.pack(sidetk.LEFT, padx5) tk.Button(return_frame, text查询记录, commandself.query_record).pack(sidetk.LEFT, padx5) tk.Button(return_frame, text确认还书, commandself.do_return, bg#FF9800, fgwhite).pack(sidetk.LEFT, padx20) # 当前借阅列表 list_frame tk.LabelFrame(self, text当前借阅记录, padx10, pady10) list_frame.pack(filltk.BOTH, expandTrue, padx10, pady5) columns (id, reader, book, borrow_date, due_date, status) self.tree ttk.Treeview(list_frame, columnscolumns, showheadings, height10) self.tree.heading(id, text记录ID) self.tree.heading(reader, text读者) self.tree.heading(book, text图书) self.tree.heading(borrow_date, text借书日期) self.tree.heading(due_date, text应还日期) self.tree.heading(status, text状态) self.tree.column(id, width60, anchortk.CENTER) self.tree.column(reader, width100) self.tree.column(book, width200) self.tree.column(borrow_date, width130) self.tree.column(due_date, width110) self.tree.column(status, width80, anchortk.CENTER) self.tree.pack(filltk.BOTH, expandTrue) self.load_current_borrows() def query_reader(self): reader_no self.entry_reader.get().strip() if not reader_no: messagebox.showwarning(提示, 请输入读者证号) return sql SELECT id, name, current_borrow, max_borrow, status FROM readers WHERE reader_no%s result self.db.execute_query(sql, (reader_no,)) if result: self.current_reader result[0] self.show_info(f读者{result[0][name]} 已借{result[0][current_borrow]}/{result[0][max_borrow]} 状态{result[0][status]}) else: messagebox.showerror(错误, 未找到该读者) def query_book(self): isbn self.entry_isbn.get().strip() if not isbn: messagebox.showwarning(提示, 请输入ISBN) return sql SELECT id, title, author, current_stock FROM books WHERE isbn%s result self.db.execute_query(sql, (isbn,)) if result: self.current_book result[0] self.show_info(f图书《{result[0][title]}》 可借{result[0][current_stock]}本) else: messagebox.showerror(错误, 未找到该书) def do_borrow(self): if not hasattr(self, current_reader) or not hasattr(self, current_book): messagebox.showwarning(提示, 请先查询读者和图书) return reader self.current_reader book self.current_book # 检查读者状态 if reader[status] ! 正常: messagebox.showerror(错误, 该读者状态异常无法借书) return if reader[current_borrow] reader[max_borrow]: messagebox.showerror(错误, 该读者已达到最大借阅数量) return if book[current_stock] 0: messagebox.showerror(错误, 该书库存不足) return # 执行借书事务 try: conn self.db.connect() with conn.cursor() as cursor: # 插入借阅记录 due_date (datetime.now() timedelta(days30)).date() # 默认借期30天 sql_insert INSERT INTO borrow_records (book_id, reader_id, due_date, operator_id, status) VALUES (%s, %s, %s, %s, 借出) cursor.execute(sql_insert, (book[id], reader[id], due_date, self.admin[id])) # 更新图书库存 sql_update_book UPDATE books SET current_stock current_stock - 1 WHERE id %s cursor.execute(sql_update_book, (book[id],)) # 更新读者当前借阅数 sql_update_reader UPDATE readers SET current_borrow current_borrow 1 WHERE id %s cursor.execute(sql_update_reader, (reader[id],)) conn.commit() messagebox.showinfo(成功, f借书成功应还日期{due_date}) self.clear_borrow_form() self.load_current_borrows() except Exception as e: conn.rollback() messagebox.showerror(错误, f借书失败{str(e)}) def do_return(self): record_id self.entry_record.get().strip() if not record_id: messagebox.showwarning(提示, 请输入借阅记录ID) return # 查询借阅记录 sql SELECT br.*, b.id as book_id, b.title, r.id as reader_id, r.name as reader_name FROM borrow_records br JOIN books b ON br.book_id b.id JOIN readers r ON br.reader_id r.id WHERE br.id %s AND br.status 借出 result self.db.execute_query(sql, (record_id,)) if not result: messagebox.showerror(错误, 未找到该借阅记录或已归还) return record result[0] # 计算逾期罚款假设每本每天0.5元 due record[due_date] now datetime.now().date() overdue_days (now - due).days fine max(overdue_days * 0.5, 0) if overdue_days 0 else 0 msg f图书《{record[title]}》\n读者{record[reader_name]}\n应还日期{due}\n逾期天数{overdue_days if overdue_days0 else 0}\n罚款金额{fine:.2f}元 if fine 0: if not messagebox.askyesno(逾期罚款, f{msg}\n\n是否确认还书并缴纳罚款): return else: messagebox.showinfo(还书确认, msg) # 执行还书事务 try: conn self.db.connect() with conn.cursor() as cursor: # 更新借阅记录 sql_update_record UPDATE borrow_records SET return_dateNOW(), status已还, fine_amount%s WHERE id%s cursor.execute(sql_update_record, (fine, record_id)) # 增加图书库存 sql_update_book UPDATE books SET current_stock current_stock 1 WHERE id %s cursor.execute(sql_update_book, (record[book_id],)) # 减少读者当前借阅数 sql_update_reader UPDATE readers SET current_borrow current_borrow - 1 WHERE id %s cursor.execute(sql_update_reader, (record[reader_id],)) # 如果有罚款插入罚款记录 if fine 0: sql_fine INSERT INTO fines (record_id, amount, paid) VALUES (%s, %s, 1) cursor.execute(sql_fine, (record_id, fine)) conn.commit() messagebox.showinfo(成功, f还书成功罚款{fine:.2f}元已缴纳。) self.entry_record.delete(0, tk.END) self.load_current_borrows() except Exception as e: conn.rollback() messagebox.showerror(错误, f还书失败{str(e)}) def load_current_borrows(self): for item in self.tree.get_children(): self.tree.delete(item) sql SELECT br.id, r.name as reader_name, b.title, br.borrow_date, br.due_date, br.status FROM borrow_records br JOIN readers r ON br.reader_id r.id JOIN books b ON br.book_id b.id WHERE br.status IN (借出,逾期) ORDER BY br.borrow_date DESC LIMIT 100 records self.db.execute_query(sql) for rec in records: self.tree.insert(, tk.END, values( rec[id], rec[reader_name], rec[title], rec[borrow_date].strftime(%Y-%m-%d %H:%M), rec[due_date], rec[status] )) def clear_borrow_form(self): self.entry_reader.delete(0, tk.END) self.entry_isbn.delete(0, tk.END) if hasattr(self, current_reader): del self.current_reader if hasattr(self, current_book): del self.current_book self.info_text.config(statetk.NORMAL) self.info_text.delete(1.0, tk.END) self.info_text.config(statetk.DISABLED) def show_info(self, text): self.info_text.config(statetk.NORMAL) self.info_text.delete(1.0, tk.END) self.info_text.insert(tk.END, text) self.info_text.config(statetk.DISABLED) 统计报表示例 (views/statistics.py)import tkinter as tk from tkinter import ttk from database import DatabaseManager class Statistics(tk.Frame): def __init__(self, parent, admin): super().__init__(parent) self.admin admin self.db DatabaseManager() self.setup_ui() self.load_statistics() def setup_ui(self): notebook ttk.Notebook(self) notebook.pack(filltk.BOTH, expandTrue) # 热门图书 tab1 tk.Frame(notebook) notebook.add(tab1, text热门图书TOP10) self.popular_tree ttk.Treeview(tab1, columns(rank,title,author,count), showheadings) self.popular_tree.heading(rank, text排名) self.popular_tree.heading(title, text书名) self.popular_tree.heading(author, text作者) self.popular_tree.heading(count, text借阅次数) self.popular_tree.pack(filltk.BOTH, expandTrue, padx10, pady10) # 读者排行 tab2 tk.Frame(notebook) notebook.add(tab2, text读者借阅排行) self.reader_tree ttk.Treeview(tab2, columns(rank,name,count), showheadings) self.reader_tree.heading(rank, text排名) self.reader_tree.heading(name, text读者姓名) self.reader_tree.heading(count, text借阅次数) self.reader_tree.pack(filltk.BOTH, expandTrue, padx10, pady10) # 日/月流通量 tab3 tk.Frame(notebook) notebook.add(tab3, text流通量统计) self.flow_tree ttk.Treeview(tab3, columns(date,borrow,return), showheadings) self.flow_tree.heading(date, text日期) self.flow_tree.heading(borrow, text借书数) self.flow_tree.heading(return, text还书数) self.flow_tree.pack(filltk.BOTH, expandTrue, padx10, pady10) def load_statistics(self): # 热门图书 sql_popular SELECT b.title, b.author, COUNT(br.id) as cnt FROM borrow_records br JOIN books b ON br.book_id b.id GROUP BY b.id ORDER BY cnt DESC LIMIT 10 popular self.db.execute_query(sql_popular) for i, book in enumerate(popular, 1): self.popular_tree.insert(, tk.END, values(i, book[title], book[author], book[cnt])) # 读者排行 sql_reader SELECT r.name, COUNT(br.id) as cnt FROM borrow_records br JOIN readers r ON br.reader_id r.id GROUP BY r.id ORDER BY cnt DESC LIMIT 10 readers self.db.execute_query(sql_reader) for i, reader in enumerate(readers, 1): self.reader_tree.insert(, tk.END, values(i, reader[name], reader[cnt])) # 近7天流通量 sql_flow SELECT DATE(borrow_date) as date, SUM(CASE WHEN status IN (借出,逾期) THEN 1 ELSE 0 END) as borrow_cnt, SUM(CASE WHEN status 已还 THEN 1 ELSE 0 END) as return_cnt FROM borrow_records WHERE borrow_date CURDATE() - INTERVAL 7 DAY GROUP BY DATE(borrow_date) ORDER BY date DESC flows self.db.execute_query(sql_flow) for flow in flows: self.flow_tree.insert(, tk.END, values(flow[date], flow[borrow_cnt], flow[return_cnt])) UI美化建议使用ttkbootstrap库可以让界面更加现代化pip install ttkbootstrap然后在主窗口中替换ttk为ttkbootstrapimport ttkbootstrap as tb from ttkbootstrap.constants import * class MainWindow: def __init__(self, admin_info): self.root tb.Window(themenameflatly) # 主题可选superhero, darkly, flatly等 ... 打包发布使用 PyInstaller 打包成 exepip install pyinstaller pyinstaller --onefile --windowed --iconresources/icon.ico main.py注意需要将 MySQL 连接配置放在外部 config.ini 文件中避免硬编码。

相关新闻

Codex实战:基于LLM的PR自动化安全审查如何提升开发效率与代码安全

Codex实战:基于LLM的PR自动化安全审查如何提升开发效率与代码安全

如果你是一名开发者,每天要 Review 十几个 Pull Request (PR),最头疼的是什么? 不是代码逻辑复杂,而是那些 隐蔽的安全漏洞 :一个未经验证的用户输入、一个硬编码的密钥、一个过时的依赖版本……这些“定时炸弹”可…

2026/8/10 8:37:43 阅读更多 →
ArcGIS 3D Analyst栅格计算器:从基础到高级应用

ArcGIS 3D Analyst栅格计算器:从基础到高级应用

1. 项目概述 作为一名GIS行业从业者,我经常需要处理各种空间分析任务。在众多工具中,ArcToolbox的3D Analyst扩展模块一直是我的得力助手,特别是其中的栅格计算功能。这个看似简单的工具实际上蕴含着强大的空间分析能力,能够解决从…

2026/8/10 8:37:42 阅读更多 →
告别手动整理!OncePower批量重命名工具实战指南

告别手动整理!OncePower批量重命名工具实战指南

你是不是也遇到过这样的场景:电脑里堆积了几百个从不同渠道下载的图片、文档或视频,文件名五花八门,有带乱码的、有带日期戳的、有全是数字序列的。想要整理,手动一个个改?光是想想就头皮发麻。用系统自带的批量重命名…

2026/8/10 8:37:42 阅读更多 →

最新新闻

Seed2.0大语言模型:架构解析、本地部署与微调实战指南

Seed2.0大语言模型:架构解析、本地部署与微调实战指南

1. 项目概述:Seed2.0系列大语言模型的登场 最近在开源社区和开发者圈子里,Seed团队发布的Seed2.0系列大语言模型引起了不小的讨论。如果你关注过之前的Seed模型,或者正在寻找一个性能不错、易于部署且对中文支持友好的开源大模型,…

2026/8/10 9:34:12 阅读更多 →
企业级运维智能体:从自动化到智能化的核心能力与落地实践

企业级运维智能体:从自动化到智能化的核心能力与落地实践

1. 从“救火队员”到“智能管家”:企业运维的范式转移 干了十几年运维,从半夜被电话叫醒处理服务器宕机,到如今看着智能体自动预测、自动修复,这个转变过程我算是亲身经历了。今天想聊的“企业级运维智能体”,听起来高…

2026/8/10 9:34:12 阅读更多 →
AlmaLinux上容器化部署Prometheus+Grafana监控方案

AlmaLinux上容器化部署Prometheus+Grafana监控方案

1. 项目概述 在AlmaLinux系统上容器化部署PrometheusGrafana监控组合,是当前企业级监控系统搭建的主流方案之一。作为CentOS的替代品,AlmaLinux凭借其稳定性和长期支持特性,正成为越来越多运维团队的首选操作系统。而将Prometheus和Grafana这…

2026/8/10 9:34:12 阅读更多 →
独立开发者软著申请全攻略:DIY与代办选择指南

独立开发者软著申请全攻略:DIY与代办选择指南

1. 软著申请:独立开发者绕不开的“必修课” 最近和几个独立开发的朋友聊天,发现大家普遍对“软件著作权”这件事又爱又恨。爱的是,它确实是证明你代码所有权、保护劳动成果最直接、最基础的法律凭证,无论是上架应用商店、参与项目…

2026/8/10 9:34:12 阅读更多 →
AI搜索摘要如何重塑信息生态:技术原理、风险与应对策略

AI搜索摘要如何重塑信息生态:技术原理、风险与应对策略

1. 项目概述:当搜索不再“浏览”最近在折腾一些个人项目,需要频繁查阅技术文档和开源方案,一个越来越明显的感受是:我好像很久没有真正“点开”过一个网页了。无论是查找一个API的具体参数,还是想了解某个框架的优劣对…

2026/8/10 9:34:12 阅读更多 →
Java枚举类深度解析与高级应用实战

Java枚举类深度解析与高级应用实战

1. Java枚举类深度解析与应用实战 枚举(Enum)是Java 5引入的一种特殊数据类型,它通过有限的命名值来定义变量集合。在实际开发中,枚举类远比简单的常量定义强大得多。我见过不少开发者仅仅把枚举当作常量列表使用,这实…

2026/8/10 9:33:12 阅读更多 →

日新闻

GraphQL-CSS API全解析:useGqlCSS、GqlCSS组件与getStyles实用指南

GraphQL-CSS API全解析:useGqlCSS、GqlCSS组件与getStyles实用指南

GraphQL-CSS API全解析:useGqlCSS、GqlCSS组件与getStyles实用指南 【免费下载链接】graphql-css A blazing fast CSS-in-GQL™ library. 项目地址: https://gitcode.com/gh_mirrors/gr/graphql-css GraphQL-CSS是一个基于GraphQL的CSS-in-GQL™库&#xff0…

2026/8/10 0:00:02 阅读更多 →
告别语言障碍:KISS Translator 双语翻译插件终极指南

告别语言障碍:KISS Translator 双语翻译插件终极指南

告别语言障碍:KISS Translator 双语翻译插件终极指南 【免费下载链接】kiss-translator A simple, open source bilingual translation extension & Greasemonkey script (一个简约、开源的 双语对照翻译扩展 & 油猴脚本) 项目地址: https://gitcode.com/…

2026/8/10 0:00:02 阅读更多 →
BepInEx配置管理器:游戏插件配置的终极可视化解决方案

BepInEx配置管理器:游戏插件配置的终极可视化解决方案

BepInEx配置管理器:游戏插件配置的终极可视化解决方案 【免费下载链接】BepInEx.ConfigurationManager Plugin configuration manager for BepInEx 项目地址: https://gitcode.com/gh_mirrors/be/BepInEx.ConfigurationManager 你是否曾经因为游戏插件的复杂…

2026/8/10 0:00:02 阅读更多 →

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/10 1:05:29 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/10 1:05:29 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/10 1:05:29 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/9 17:05:02 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/10 1:05:29 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/9 17:05:02 阅读更多 →