效果展示
概述
本教程将引导你构建一个基于Python的图书管理系统,该系统使用Tkinter作为图形用户界面(GUI),并利用SQLite数据库存储和管理图书信息。通过本教程,你将学习如何实现添加、编辑、删除以及查询图书的功能。
准备工作
确保你的计算机上安装了Python 3.x版本。由于我们使用的是标准库
tkinter
和sqlite3
,所以无需额外安装其他依赖包。如果你使用的是非Windows操作系统,可能需要单独安装tkinter
。
步骤 1: 导入必要的模块
首先,我们需要导入所有必需的模块。这包括用于GUI开发的
tkinter
及其子模块ttk
(主题工具包),以及用于消息框和对话框的messagebox
和simpledialog
。同时,我们还会导入sqlite3
来处理数据库操作,以及pathlib
来处理文件路径。
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import sqlite3
from pathlib import Path
步骤 2: 数据库初始化与连接
接下来定义两个函数:
init_db()
用于初始化数据库表结构;get_db_connection()
用于获取数据库连接。init_db()
会在第一次运行程序时创建名为Library.db
的数据库文件,并在其中创建Books
表,如果它不存在的话。
def init_db():
"""初始化数据库表结构"""
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS Books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
author TEXT NOT NULL,
publisher TEXT,
isbn TEXT UNIQUE,
stock INTEGER DEFAULT 0
)
''')
conn.commit()
def get_db_connection():
"""获取数据库连接"""
conn = sqlite3.connect(Path(__file__).parent / 'Library.db')
conn.row_factory = sqlite3.Row
return conn
步骤 3: 创建主应用程序类 BookManagerApp
BookManagerApp
是整个应用的核心类,继承自tk.Tk
,负责构建界面并管理各个窗口之间的交互。
- 构造函数 (
__init__
) 设置了窗口标题、尺寸和背景颜色,并调用init_db()
确保数据库已准备好。- 它还设置了菜单栏、工具栏,并配置了样式以美化组件外观。
- 最后,它创建了一个Treeview小部件用来显示图书列表,并通过
load_books()
加载现有书籍数据。
class BookManagerApp(tk.Tk):
"""主应用程序类"""
def __init__(self):
super().__init__()
self.title("图书管理系统")
self.geometry("800x600")
self.configure(bg='#EAEAEA') # 设置背景颜色
# 初始化数据库
init_db()
# 创建顶部菜单栏和工具栏...
menubar = tk.Menu(self)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="退出", command=self.quit)
menubar.add_cascade(label="文件", menu=file_menu)
book_menu = tk.Menu(menubar, tearoff=0)
book_menu.add_command(label="添加图书", command=self.open_add_book_window)
book_menu.add_command(label="编辑图书", command=self.edit_selected_book)
book_menu.add_command(label="删除图书", command=self.delete_selected_books)
book_menu.add_command(label="查询图书", command=self.open_search_window) # 添加查询命令
menubar.add_cascade(label="图书", menu=book_menu)
self.config(menu=menubar)
# 创建工具栏
toolbar = ttk.Frame(self, style='TFrame')
add_button = ttk.Button(toolbar, text="添加", command=self.open_add_book_window, style='TButton')
edit_button = ttk.Button(toolbar, text="编辑", command=self.edit_selected_book, style='TButton')
delete_button = ttk.Button(toolbar, text="删除", command=self.delete_selected_books, style='TButton')
search_button = ttk.Button(toolbar, text="查询", command=self.open_search_window, style='TButton') # 添加查询按钮
add_button.pack(side=tk.LEFT, padx=5, pady=5)
edit_button.pack(side=tk.LEFT, padx=5, pady=5)
delete_button.pack(side=tk.LEFT, padx=5, pady=5)
search_button.pack(side=tk.LEFT, padx=5, pady=5) # 查询按钮布局
toolbar.pack(side=tk.TOP, fill=tk.X)
# 样式配置
style = ttk.Style()
style.theme_use('clam') # 使用预定义的主题
style.configure('TFrame', background='#EAEAEA')
style.configure('TButton', font=('Arial', 12), foreground='black', background='#4CAF50')
style.map('TButton', foreground=[('active', 'white')],
background=[('active', '#45a049')])
style.configure('Treeview', font=('Arial', 12), rowheight=25)
style.configure('Treeview.Heading', font=('Arial', 12, 'bold'))
# 创建图书列表视图
columns = ('id', 'title', 'author', 'publisher', 'isbn', 'stock')
self.book_tree = ttk.Treeview(self, columns=columns, show='headings', style='Treeview')
for col in columns:
self.book_tree.heading(col, text=col.capitalize())
self.book_tree.column(col, width=120, anchor=tk.CENTER)
self.book_tree.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
self.load_books()
步骤 4: 实现增删改查功能
增加图书
为了增加新的图书记录,我们创建了一个
AddBookWindow
类,提供给用户输入新书信息的界面。用户填写必要字段后点击“保存”按钮,即可将新书信息保存至数据库。
class AddBookWindow(tk.Toplevel):
"""添加新书窗口类"""
def __init__(self, parent):
super().__init__(parent)
self.title("添加新书")
self.geometry("400x300")
self.configure(bg='#EAEAEA')
# 输入框
self.entries = {}
fields = ['标题', '作者', '出版社', 'ISBN', '库存']
for i, field in enumerate(fields):
label = ttk.Label(self, text=field, background='#EAEAEA')
label.grid(row=i, column=0, padx=5, pady=5, sticky=tk.W)
entry = ttk.Entry(self)
entry.grid(row=i, column=1, padx=5, pady=5)
self.entries[field] = entry
save_button = ttk.Button(self, text="保存", command=self.save_book)
save_button.grid(row=len(fields), columnspan=2, pady=10)
def save_book(self):
"""保存新书信息至数据库"""
data = {key: entry.get() for key, entry in self.entries.items()}
try:
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO Books (title, author, publisher, isbn, stock)
VALUES (?, ?, ?, ?, ?)
''', (data['标题'], data['作者'], data['出版社'], data['ISBN'], int(data['库存'])))
conn.commit()
self.destroy()
self.master.load_books()
except Exception as e:
messagebox.showerror("错误", str(e))
编辑图书
EditBookWindow
类允许用户修改已有图书的信息。它会根据提供的book_id
从数据库中检索出对应的图书信息,并填充到表单中供用户编辑。
class EditBookWindow(tk.Toplevel):
"""编辑图书窗口类"""
def __init__(self, parent, book_id):
super().__init__(parent)
self.title("编辑图书")
self.geometry("400x300")
self.configure(bg='#EAEAEA')
self.book_id = book_id
self.entries = {}
fields_mapping = {
'标题': 'title',
'作者': 'author',
'出版社': 'publisher',
'ISBN': 'isbn',
'库存': 'stock'
}
# 从数据库获取要编辑的图书信息
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM Books WHERE id=?', (book_id,))
book = cursor.fetchone()
if not book:
messagebox.showerror("错误", "找不到指定ID的图书!")
self.destroy()
return
# 预填充表单字段
for i, field in enumerate(fields_mapping.keys()):
label = ttk.Label(self, text=field, background='#EAEAEA')
label.grid(row=i, column=0, padx=5, pady=5, sticky=tk.W)
entry = ttk.Entry(self)
# 使用映射后的列名访问数据
entry.insert(0, book[fields_mapping[field]]) # 使用映射后的列名
entry.grid(row=i, column=1, padx=5, pady=5)
self.entries[field] = entry
save_button = ttk.Button(self, text="保存", command=self.update_book)
save_button.grid(row=len(fields_mapping), columnspan=2, pady=10)
def update_book(self):
"""更新图书信息至数据库"""
data = {key: entry.get() for key, entry in self.entries.items()}
try:
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
UPDATE Books SET title=?, author=?, publisher=?, isbn=?, stock=?
WHERE id=?
''', (data['标题'], data['作者'], data['出版社'], data['ISBN'], int(data['库存']), self.book_id))
conn.commit()
self.destroy()
self.master.load_books() # 刷新主界面中的图书列表
except Exception as e:
messagebox.showerror("错误", str(e))
删除图书
在
BookManagerApp
类中定义了delete_selected_books()
方法,用于删除选中的图书记录。当用户选择了要删除的图书并确认后,这些记录将被从数据库中移除,并且界面上也会同步更新。
def delete_selected_books(self):
"""删除选中的图书记录"""
selected_items = self.book_tree.selection()
if not selected_items:
messagebox.showwarning("警告", "请选择要删除的图书!")
return
confirm = messagebox.askyesno("确认删除", "确定要删除这些图书吗?")
if not confirm:
return
book_ids = [self.book_tree.item(item)['values'][0] for item in selected_items]
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.executemany('DELETE FROM Books WHERE id=?', [(id,) for id in book_ids])
conn.commit()
for item in selected_items:
self.book_tree.delete(item)
查询图书
SearchWindow
类提供了用户根据标题、作者、出版社或ISBN部分匹配来查找图书的功能。用户输入关键字后点击“查询”按钮,系统将在数据库中查找符合条件的图书并将结果展示出来。
class SearchWindow(tk.Toplevel):
"""查询窗口类"""
def __init__(self, parent):
super().__init__(parent)
self.title("查询图书")
self.geometry("400x300")
self.configure(bg='#EAEAEA')
# 输入框
self.entries = {}
fields = ['标题', '作者', '出版社', 'ISBN']
for i, field in enumerate(fields):
label = ttk.Label(self, text=field, background='#EAEAEA')
label.grid(row=i, column=0, padx=5, pady=5, sticky=tk.W)
entry = ttk.Entry(self)
entry.grid(row=i, column=1, padx=5, pady=5)
self.entries[field] = entry
search_button = ttk.Button(self, text="查询", command=self.perform_search)
search_button.grid(row=len(fields), columnspan=2, pady=10)
def perform_search(self):
"""执行查询"""
conditions = []
parameters = []
for key, entry in self.entries.items():
value = entry.get().strip()
if value:
column_name = {'标题': 'title', '作者': 'author', '出版社': 'publisher', 'ISBN': 'isbn'}[key]
conditions.append(f"{column_name} LIKE ?")
parameters.append(f"%{value}%")
query = "SELECT * FROM Books"
if conditions:
query += " WHERE " + " AND ".join(conditions)
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute(query, parameters)
rows = cursor.fetchall()
self.master.load_books(rows) # 使用自定义加载方法来显示查询结果
self.destroy()
步骤 5: 启动应用程序
最后,在脚本的底部添加如下代码以启动应用程序:
if __name__ == '__main__':
app = BookManagerApp()
app.mainloop()
完整代码
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import sqlite3
from pathlib import Path
# 数据库初始化函数
def init_db():
"""初始化数据库表结构"""
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS Books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
author TEXT NOT NULL,
publisher TEXT,
isbn TEXT UNIQUE,
stock INTEGER DEFAULT 0
)
''')
conn.commit()
def get_db_connection():
"""获取数据库连接"""
conn = sqlite3.connect(Path(__file__).parent / 'Library.db')
conn.row_factory = sqlite3.Row
return conn
class BookManagerApp(tk.Tk):
"""主应用程序类"""
def __init__(self):
super().__init__()
self.title("图书管理系统")
self.geometry("800x600")
self.configure(bg='#EAEAEA') # 设置背景颜色
# 初始化数据库
init_db()
# 创建顶部菜单栏
menubar = tk.Menu(self)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="退出", command=self.quit)
menubar.add_cascade(label="文件", menu=file_menu)
book_menu = tk.Menu(menubar, tearoff=0)
book_menu.add_command(label="添加图书", command=self.open_add_book_window)
book_menu.add_command(label="编辑图书", command=self.edit_selected_book)
book_menu.add_command(label="删除图书", command=self.delete_selected_books)
menubar.add_cascade(label="图书", menu=book_menu)
self.config(menu=menubar)
# 创建工具栏
toolbar = ttk.Frame(self, style='TFrame')
add_button = ttk.Button(toolbar, text="添加", command=self.open_add_book_window, style='TButton')
edit_button = ttk.Button(toolbar, text="编辑", command=self.edit_selected_book, style='TButton')
delete_button = ttk.Button(toolbar, text="删除", command=self.delete_selected_books, style='TButton')
add_button.pack(side=tk.LEFT, padx=5, pady=5)
edit_button.pack(side=tk.LEFT, padx=5, pady=5)
delete_button.pack(side=tk.LEFT, padx=5, pady=5)
toolbar.pack(side=tk.TOP, fill=tk.X)
# 样式配置
style = ttk.Style()
style.theme_use('clam') # 使用预定义的主题
style.configure('TFrame', background='#EAEAEA')
style.configure('TButton', font=('Arial', 12), foreground='black', background='#4CAF50')
style.map('TButton', foreground=[('active', 'white')],
background=[('active', '#45a049')])
style.configure('Treeview', font=('Arial', 12), rowheight=25)
style.configure('Treeview.Heading', font=('Arial', 12, 'bold'))
# 创建图书列表视图
columns = ('id', 'title', 'author', 'publisher', 'isbn', 'stock')
self.book_tree = ttk.Treeview(self, columns=columns, show='headings', style='Treeview')
for col in columns:
self.book_tree.heading(col, text=col.capitalize())
self.book_tree.column(col, width=120, anchor=tk.CENTER)
self.book_tree.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
self.load_books()
def load_books(self):
"""从数据库加载所有图书到列表视图"""
for i in self.book_tree.get_children():
self.book_tree.delete(i)
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM Books')
rows = cursor.fetchall()
for row in rows:
book_data = [row['id'], row['title'], row['author'], row['publisher'], row['isbn'], row['stock']]
self.book_tree.insert('', tk.END, values=book_data)
def open_add_book_window(self):
"""打开添加新书窗口"""
AddBookWindow(self)
def edit_selected_book(self):
"""编辑选中的图书记录"""
selected_item = self.book_tree.selection()
if not selected_item:
messagebox.showwarning("警告", "请选择要编辑的图书!")
return
item_id = self.book_tree.item(selected_item)['values'][0]
EditBookWindow(self, item_id)
def delete_selected_books(self):
"""删除选中的图书记录"""
selected_items = self.book_tree.selection()
if not selected_items:
messagebox.showwarning("警告", "请选择要删除的图书!")
return
confirm = messagebox.askyesno("确认删除", "确定要删除这些图书吗?")
if not confirm:
return
book_ids = [self.book_tree.item(item)['values'][0] for item in selected_items]
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.executemany('DELETE FROM Books WHERE id=?', [(id,) for id in book_ids])
conn.commit()
for item in selected_items:
self.book_tree.delete(item)
class AddBookWindow(tk.Toplevel):
"""添加新书窗口类"""
def __init__(self, parent):
super().__init__(parent)
self.title("添加新书")
self.geometry("400x300")
self.configure(bg='#EAEAEA')
# 输入框
self.entries = {}
fields = ['标题', '作者', '出版社', 'ISBN', '库存']
for i, field in enumerate(fields):
label = ttk.Label(self, text=field, background='#EAEAEA')
label.grid(row=i, column=0, padx=5, pady=5, sticky=tk.W)
entry = ttk.Entry(self)
entry.grid(row=i, column=1, padx=5, pady=5)
self.entries[field] = entry
save_button = ttk.Button(self, text="保存", command=self.save_book)
save_button.grid(row=len(fields), columnspan=2, pady=10)
def save_book(self):
"""保存新书信息至数据库"""
data = {key: entry.get() for key, entry in self.entries.items()}
try:
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO Books (title, author, publisher, isbn, stock)
VALUES (?, ?, ?, ?, ?)
''', (data['标题'], data['作者'], data['出版社'], data['ISBN'], int(data['库存'])))
conn.commit()
self.destroy()
self.master.load_books()
except Exception as e:
messagebox.showerror("错误", str(e))
class EditBookWindow(tk.Toplevel):
"""编辑图书窗口类"""
def __init__(self, parent, book_id):
super().__init__(parent)
self.title("编辑图书")
self.geometry("400x300")
self.configure(bg='#EAEAEA')
self.book_id = book_id
self.entries = {}
fields_mapping = {
'标题': 'title',
'作者': 'author',
'出版社': 'publisher',
'ISBN': 'isbn',
'库存': 'stock'
}
# 从数据库获取要编辑的图书信息
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM Books WHERE id=?', (book_id,))
book = cursor.fetchone()
if not book:
messagebox.showerror("错误", "找不到指定ID的图书!")
self.destroy()
return
# 打印列名以供调试
print("Column names:", [description[0] for description in cursor.description])
# 预填充表单字段
for i, field in enumerate(fields_mapping.keys()):
label = ttk.Label(self, text=field, background='#EAEAEA')
label.grid(row=i, column=0, padx=5, pady=5, sticky=tk.W)
entry = ttk.Entry(self)
# 使用映射后的列名访问数据
entry.insert(0, book[fields_mapping[field]]) # 使用映射后的列名
entry.grid(row=i, column=1, padx=5, pady=5)
self.entries[field] = entry
save_button = ttk.Button(self, text="保存", command=self.update_book)
save_button.grid(row=len(fields_mapping), columnspan=2, pady=10)
def update_book(self):
"""更新图书信息至数据库"""
data = {key: entry.get() for key, entry in self.entries.items()}
try:
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
UPDATE Books SET title=?, author=?, publisher=?, isbn=?, stock=?
WHERE id=?
''', (data['标题'], data['作者'], data['出版社'], data['ISBN'], int(data['库存']), self.book_id))
conn.commit()
self.destroy()
self.master.load_books() # 刷新主界面中的图书列表
except Exception as e:
messagebox.showerror("错误", str(e))
if __name__ == '__main__':
app = BookManagerApp()
app.mainloop()
标签:__,管理系统,python,self,cursor,book,conn,图书,tk
From: https://blog.csdn.net/qq491735628/article/details/145243466