报错问题可能是:
1.缺少依赖
2.数据库账户密码没配置对,我自己用的是root root
更新ui布局




python
import tkinter as tk
from tkinter import ttk, messagebox, font
import mysql.connector
from mysql.connector import Error
from datetime import datetime
class StudentManagementSystem:
def __init__(self, root):
self.root = root
self.root.title("学生信息管理系统")
self.root.geometry("1100x600")
# 设置窗口图标(如果有图标文件)
try:
self.root.iconbitmap('icon.ico')
except:
pass
# 设置字体
self.title_font = font.Font(family="微软雅黑", size=16, weight="bold")
self.button_font = font.Font(family="微软雅黑", size=10)
self.label_font = font.Font(family="微软雅黑", size=10)
self.table_font = font.Font(family="微软雅黑", size=9)
# 设置颜色
self.bg_color = "#f0f0f0"
self.button_bg = "#4CAF50"
self.button_fg = "white"
self.button_hover = "#45a049"
self.search_bg = "#2196F3"
self.search_hover = "#1976D2"
self.delete_bg = "#f44336"
self.delete_hover = "#d32f2f"
# 配置主窗口背景色
self.root.configure(bg=self.bg_color)
# 数据库连接配置
self.db_config = {
'host': 'localhost',
'user': 'root',
'password': 'root',
'database': 'student_management',
'charset': 'utf8mb4',
'use_unicode': True,
'collation': 'utf8mb4_unicode_ci'
}
# 初始化数据库连接
self.connection = None
self.connect_db()
self.create_table_if_not_exists()
# 创建GUI
self.create_widgets()
self.load_data()
# 中心化窗口
self.center_window()
def center_window(self):
"""将窗口居中显示"""
self.root.update_idletasks()
width = self.root.winfo_width()
height = self.root.winfo_height()
x = (self.root.winfo_screenwidth() // 2) - (width // 2)
y = (self.root.winfo_screenheight() // 2) - (height // 2)
self.root.geometry(f'{width}x{height}+{x}+{y}')
def connect_db(self):
"""连接到MySQL数据库"""
try:
self.connection = mysql.connector.connect(**self.db_config)
if self.connection.is_connected():
cursor = self.connection.cursor()
cursor.execute("SET NAMES utf8mb4")
cursor.execute("SET CHARACTER SET utf8mb4")
cursor.execute("SET character_set_connection=utf8mb4")
cursor.close()
print("成功连接到MySQL数据库,字符集已设置为utf8mb4")
except Error as e:
messagebox.showerror("数据库连接错误", f"无法连接到数据库: {str(e)}")
self.root.quit()
def create_table_if_not_exists(self):
"""如果表不存在则创建表"""
try:
cursor = self.connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS students
(
student_id
VARCHAR
(
10
) PRIMARY KEY,
name VARCHAR
(
50
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
gender VARCHAR
(
2
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
birth_date DATE,
major VARCHAR
(
50
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
total_credits INT,
remarks TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
""")
self.connection.commit()
print("表已创建/验证,字符集为utf8mb4")
except Error as e:
messagebox.showerror("数据库错误", f"创建表失败: {str(e)}")
def create_widgets(self):
"""创建GUI组件"""
# 标题框架
title_frame = tk.Frame(self.root, bg=self.bg_color)
title_frame.pack(fill=tk.X, pady=(20, 10))
# 标题标签
title_label = tk.Label(
title_frame,
text="学生信息管理系统",
font=self.title_font,
bg=self.bg_color,
fg="#333333"
)
title_label.pack()
# 按钮框架
button_frame = tk.Frame(self.root, bg=self.bg_color)
button_frame.pack(fill=tk.X, pady=10, padx=20)
# 创建按钮样式
style = ttk.Style()
style.theme_use('clam')
# 按钮定义
button_configs = [
("查询", self.query_student, self.search_bg, self.search_hover),
("插入", self.insert_student, self.button_bg, self.button_hover),
("修改", self.update_student, "#FF9800", "#F57C00"),
("删除", self.delete_student, self.delete_bg, self.delete_hover),
("刷新", self.load_data, "#9C27B0", "#7B1FA2"),
]
for text, command, bg_color, hover_color in button_configs:
btn = self.create_button(button_frame, text, command, bg_color, hover_color)
btn.pack(side=tk.LEFT, padx=5, ipady=5, ipadx=15)
# 搜索框架
search_frame = tk.Frame(self.root, bg=self.bg_color)
search_frame.pack(fill=tk.X, pady=10, padx=20)
search_label = tk.Label(
search_frame,
text="学号/姓名搜索:",
font=self.label_font,
bg=self.bg_color,
fg="#333333"
)
search_label.pack(side=tk.LEFT, padx=(0, 10))
self.search_var = tk.StringVar()
self.search_entry = tk.Entry(
search_frame,
textvariable=self.search_var,
width=40,
font=self.label_font,
relief=tk.GROOVE,
bd=2
)
self.search_entry.pack(side=tk.LEFT, padx=(0, 10))
# 绑定回车键搜索
self.search_entry.bind('<Return>', lambda e: self.search_data())
search_btn = self.create_button(search_frame, "搜索", self.search_data, self.search_bg, self.search_hover)
search_btn.pack(side=tk.LEFT, ipady=5, ipadx=15)
# 状态标签
self.status_label = tk.Label(
self.root,
text="就绪",
font=font.Font(family="微软雅黑", size=9),
bg=self.bg_color,
fg="#666666",
anchor=tk.W
)
self.status_label.pack(fill=tk.X, padx=20, pady=(5, 10))
# 表格框架
table_frame = tk.Frame(self.root, bg=self.bg_color)
table_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
# 创建表格
columns = ("学号", "姓名", "性别", "出生时间", "专业", "总学分", "备注")
self.tree = ttk.Treeview(
table_frame,
columns=columns,
show="headings",
height=18,
selectmode="browse"
)
# 配置表格样式
style.configure("Treeview",
font=self.table_font,
rowheight=25,
background="#ffffff",
fieldbackground="#ffffff",
foreground="#000000")
style.configure("Treeview.Heading",
font=font.Font(family="微软雅黑", size=10, weight="bold"),
background="#e1e1e1",
foreground="#333333",
relief=tk.FLAT)
style.map("Treeview.Heading", background=[('active', '#d1d1d1')])
# 设置列标题和宽度
column_widths = {
"学号": 100,
"姓名": 100,
"性别": 60,
"出生时间": 120,
"专业": 120,
"总学分": 80,
"备注": 200
}
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=column_widths[col], anchor="center")
# 设置隔行变色
self.tree.tag_configure('oddrow', background='#f9f9f9')
self.tree.tag_configure('evenrow', background='#ffffff')
# 垂直滚动条
scrollbar = ttk.Scrollbar(
table_frame,
orient="vertical",
command=self.tree.yview
)
self.tree.configure(yscrollcommand=scrollbar.set)
# 布局
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定事件
self.tree.bind('<Double-Button-1>', self.on_double_click)
# 右键菜单
self.create_context_menu()
def create_button(self, parent, text, command, bg_color, hover_color):
"""创建自定义样式的按钮"""
def on_enter(e):
btn['background'] = hover_color
def on_leave(e):
btn['background'] = bg_color
btn = tk.Button(
parent,
text=text,
command=command,
font=self.button_font,
bg=bg_color,
fg="white",
activebackground=hover_color,
activeforeground="white",
relief=tk.RAISED,
bd=1,
cursor="hand2"
)
btn.bind("<Enter>", on_enter)
btn.bind("<Leave>", on_leave)
return btn
def create_context_menu(self):
"""创建右键菜单"""
self.context_menu = tk.Menu(self.root, tearoff=0)
self.context_menu.add_command(label="查看详情", command=self.show_details)
self.context_menu.add_command(label="修改信息", command=self.update_student)
self.context_menu.add_separator()
self.context_menu.add_command(label="删除学生", command=self.delete_student)
# 绑定右键事件
self.tree.bind("<Button-3>", self.show_context_menu)
def show_context_menu(self, event):
"""显示右键菜单"""
item = self.tree.identify_row(event.y)
if item:
self.tree.selection_set(item)
self.context_menu.post(event.x_root, event.y_root)
def load_data(self):
"""从数据库加载数据到表格"""
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
try:
cursor = self.connection.cursor()
cursor.execute("SELECT * FROM students ORDER BY student_id")
rows = cursor.fetchall()
# 插入数据到表格
for i, row in enumerate(rows):
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 处理None值
values = list(row)
for j in range(len(values)):
if values[j] is None:
values[j] = ""
values[3] = birth_date_str
# 设置隔行颜色
tag = 'evenrow' if i % 2 == 0 else 'oddrow'
self.tree.insert("", tk.END, values=values, tags=(tag,))
cursor.close()
# 更新状态
self.status_label.config(text=f"共 {len(rows)} 条记录")
print(f"加载了 {len(rows)} 条记录")
except Error as e:
messagebox.showerror("数据库错误", f"加载数据失败: {str(e)}")
def search_data(self):
"""搜索学生信息"""
search_text = self.search_var.get().strip()
if not search_text:
messagebox.showwarning("搜索提示", "请输入搜索内容")
return
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
try:
cursor = self.connection.cursor()
query = """
SELECT * \
FROM students
WHERE student_id LIKE %s \
OR name LIKE %s
ORDER BY student_id \
"""
search_pattern = f"%{search_text}%"
cursor.execute(query, (search_pattern, search_pattern))
rows = cursor.fetchall()
if rows:
for i, row in enumerate(rows):
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 处理None值
values = list(row)
for j in range(len(values)):
if values[j] is None:
values[j] = ""
values[3] = birth_date_str
# 设置隔行颜色
tag = 'evenrow' if i % 2 == 0 else 'oddrow'
self.tree.insert("", tk.END, values=values, tags=(tag,))
self.status_label.config(text=f"找到 {len(rows)} 条记录")
messagebox.showinfo("搜索结果", f"找到 {len(rows)} 条记录")
else:
self.status_label.config(text="未找到相关记录")
messagebox.showinfo("搜索结果", "未找到相关记录")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"搜索失败: {str(e)}")
def show_details(self):
"""显示选中学生的详细信息"""
selected_item = self.tree.selection()
if selected_item:
values = self.tree.item(selected_item[0])['values']
details_window = tk.Toplevel(self.root)
details_window.title("学生详细信息")
details_window.geometry("400x300")
details_window.configure(bg=self.bg_color)
# 中心化窗口
details_window.update_idletasks()
width = details_window.winfo_width()
height = details_window.winfo_height()
x = (details_window.winfo_screenwidth() // 2) - (width // 2)
y = (details_window.winfo_screenheight() // 2) - (height // 2)
details_window.geometry(f'{width}x{height}+{x}+{y}')
# 创建详情内容
details = [
("学号:", values[0]),
("姓名:", values[1]),
("性别:", values[2]),
("出生时间:", values[3]),
("专业:", values[4]),
("总学分:", values[5]),
("备注:", values[6] if values[6] else "无")
]
for i, (label, value) in enumerate(details):
tk.Label(details_window, text=label, font=self.label_font,
bg=self.bg_color, fg="#333333", anchor="e").grid(
row=i, column=0, padx=20, pady=10, sticky="e"
)
tk.Label(details_window, text=value, font=self.label_font,
bg=self.bg_color, fg="#666666", anchor="w").grid(
row=i, column=1, padx=(0, 20), pady=10, sticky="w"
)
def insert_student(self):
"""打开插入学生窗口"""
InsertWindow(self)
def query_student(self):
"""查询特定学生信息"""
QueryWindow(self)
def delete_student(self):
"""删除选中的学生"""
selected_item = self.tree.selection()
if not selected_item:
messagebox.showwarning("删除提示", "请先选择要删除的学生")
return
# 获取选中的学生学号
student_id = self.tree.item(selected_item[0])['values'][0]
student_name = self.tree.item(selected_item[0])['values'][1]
# 确认删除
confirm = messagebox.askyesno("确认删除",
f"确定要删除学生 {student_name} (学号: {student_id}) 吗?",
icon='warning')
if confirm:
try:
cursor = self.connection.cursor()
cursor.execute("DELETE FROM students WHERE student_id = %s", (student_id,))
self.connection.commit()
# 从表格中移除
self.tree.delete(selected_item[0])
# 更新状态
self.status_label.config(text=f"已删除学生 {student_name}")
messagebox.showinfo("删除成功", f"已删除学生 {student_name}")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"删除失败: {str(e)}")
def update_student(self):
"""修改选中的学生信息"""
selected_item = self.tree.selection()
if not selected_item:
messagebox.showwarning("修改提示", "请先选择要修改的学生")
return
# 获取选中的学生信息
values = self.tree.item(selected_item[0])['values']
UpdateWindow(self, values)
def on_double_click(self, event):
"""双击表格行时显示详细信息"""
selected_item = self.tree.selection()
if selected_item:
values = self.tree.item(selected_item[0])['values']
self.show_details()
def __del__(self):
"""关闭数据库连接"""
if self.connection and self.connection.is_connected():
self.connection.close()
print("数据库连接已关闭")
class InsertWindow:
"""插入学生信息窗口"""
def __init__(self, parent):
self.parent = parent
self.window = tk.Toplevel(parent.root)
self.window.title("插入学生信息")
self.window.geometry("500x450")
self.window.configure(bg=parent.bg_color)
# 中心化窗口
self.window.update_idletasks()
width = self.window.winfo_width()
height = self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry(f'{width}x{height}+{x}+{y}')
# 创建标题
title_label = tk.Label(
self.window,
text="新增学生信息",
font=self.parent.title_font,
bg=self.parent.bg_color,
fg="#333333"
)
title_label.pack(pady=(20, 30))
# 创建表单框架
form_frame = tk.Frame(self.window, bg=self.parent.bg_color)
form_frame.pack(padx=40)
self.create_widgets(form_frame)
# 创建按钮框架
button_frame = tk.Frame(self.window, bg=self.parent.bg_color)
button_frame.pack(pady=30)
# 插入按钮
insert_btn = self.parent.create_button(
button_frame,
"插入",
self.insert,
self.parent.button_bg,
self.parent.button_hover
)
insert_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=20)
# 取消按钮
cancel_btn = self.parent.create_button(
button_frame,
"取消",
self.window.destroy,
self.parent.delete_bg,
self.parent.delete_hover
)
cancel_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=20)
def create_widgets(self, parent):
"""创建表单组件"""
# 字段定义
fields = [
("学号:", "id_entry"),
("姓名:", "name_entry"),
("性别:", "gender_var"), # 特别注意:这是单选按钮,不是输入框
("出生时间 (YYYY/MM/DD):", "birth_entry", "1997/02/10"),
("专业:", "major_entry", "计算机"),
("总学分:", "credits_entry", "50"),
("备注:", "remarks_entry")
]
row = 0
for field in fields:
label_text = field[0]
field_name = field[1]
default_value = field[2] if len(field) > 2 else ""
# 标签
label = tk.Label(
parent,
text=label_text,
font=self.parent.label_font,
bg=self.parent.bg_color,
fg="#333333",
anchor="e"
)
label.grid(row=row, column=0, padx=10, pady=10, sticky="e")
# 如果是性别字段,创建单选按钮
if field_name == "gender_var":
gender_frame = tk.Frame(parent, bg=self.parent.bg_color)
gender_frame.grid(row=row, column=1, padx=10, pady=10, sticky="w")
self.gender_var = tk.StringVar(value="男")
tk.Radiobutton(gender_frame, text="男", variable=self.gender_var,
value="男", font=self.parent.label_font,
bg=self.parent.bg_color, selectcolor=self.parent.bg_color).pack(side=tk.LEFT, padx=10)
tk.Radiobutton(gender_frame, text="女", variable=self.gender_var,
value="女", font=self.parent.label_font,
bg=self.parent.bg_color, selectcolor=self.parent.bg_color).pack(side=tk.LEFT, padx=10)
row += 1
continue
# 对于其他字段,创建输入框
entry = tk.Entry(
parent,
width=30,
font=self.parent.label_font,
relief=tk.GROOVE,
bd=2
)
entry.grid(row=row, column=1, padx=10, pady=10, sticky="w")
if default_value:
entry.insert(0, default_value)
# 保存引用
setattr(self, field_name, entry)
row += 1
def insert(self):
"""插入新学生到数据库"""
# 获取输入值
student_id = self.id_entry.get().strip()
name = self.name_entry.get().strip()
gender = self.gender_var.get()
birth_date = self.birth_entry.get().strip()
major = self.major_entry.get().strip()
credits = self.credits_entry.get().strip()
remarks = self.remarks_entry.get().strip()
# 验证输入
if not student_id or not name:
messagebox.showwarning("输入错误", "学号和姓名不能为空")
return
# 验证学号是否已存在
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT student_id FROM students WHERE student_id = %s", (student_id,))
if cursor.fetchone():
messagebox.showwarning("输入错误", f"学号 {student_id} 已存在")
cursor.close()
return
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"验证学号失败: {str(e)}")
return
# 插入数据
try:
cursor = self.parent.connection.cursor()
# 转换日期格式
if birth_date:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y/%m/%d')
except ValueError:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y-%m-%d')
except ValueError:
messagebox.showwarning("日期格式错误", "请使用YYYY/MM/DD或YYYY-MM-DD格式")
return
else:
birth_date_obj = None
# 转换学分
try:
credits_int = int(credits) if credits else 0
except ValueError:
credits_int = 0
# 执行插入
insert_query = """
INSERT INTO students (student_id, name, gender, birth_date, major, total_credits, remarks)
VALUES (%s, %s, %s, %s, %s, %s, %s) \
"""
insert_values = (student_id, name, gender, birth_date_obj, major, credits_int, remarks if remarks else None)
cursor.execute(insert_query, insert_values)
self.parent.connection.commit()
cursor.close()
messagebox.showinfo("插入成功", f"学生 {name} 已成功添加")
# 刷新主窗口数据
self.parent.load_data()
# 关闭窗口
self.window.destroy()
except Error as e:
messagebox.showerror("数据库错误", f"插入失败: {str(e)}")
class QueryWindow:
"""查询学生信息窗口"""
def __init__(self, parent):
self.parent = parent
self.window = tk.Toplevel(parent.root)
self.window.title("查询学生信息")
self.window.geometry("400x200")
self.window.configure(bg=parent.bg_color)
# 中心化窗口
self.window.update_idletasks()
width = self.window.winfo_width()
height = self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry(f'{width}x{height}+{x}+{y}')
# 创建标题
title_label = tk.Label(
self.window,
text="查询学生信息",
font=self.parent.title_font,
bg=self.parent.bg_color,
fg="#333333"
)
title_label.pack(pady=(30, 20))
# 查询输入框
input_frame = tk.Frame(self.window, bg=self.parent.bg_color)
input_frame.pack(pady=10)
tk.Label(input_frame, text="请输入学号:", font=self.parent.label_font,
bg=self.parent.bg_color, fg="#333333").pack(side=tk.LEFT, padx=5)
self.query_id_entry = tk.Entry(
input_frame,
width=25,
font=self.parent.label_font,
relief=tk.GROOVE,
bd=2
)
self.query_id_entry.pack(side=tk.LEFT, padx=5)
# 绑定回车键
self.query_id_entry.bind('<Return>', lambda e: self.query())
# 按钮框架
btn_frame = tk.Frame(self.window, bg=self.parent.bg_color)
btn_frame.pack(pady=20)
query_btn = self.parent.create_button(
btn_frame,
"查询",
self.query,
self.parent.search_bg,
self.parent.search_hover
)
query_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=15)
cancel_btn = self.parent.create_button(
btn_frame,
"取消",
self.window.destroy,
self.parent.delete_bg,
self.parent.delete_hover
)
cancel_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=15)
def query(self):
"""查询学生信息"""
student_id = self.query_id_entry.get().strip()
if not student_id:
messagebox.showwarning("查询提示", "请输入学号")
return
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT * FROM students WHERE student_id = %s", (student_id,))
row = cursor.fetchone()
if row:
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 显示查询结果
details = f"学号: {row[0]}\n姓名: {row[1]}\n性别: {row[2]}\n出生时间: {birth_date_str}\n专业: {row[4]}\n总学分: {row[5]}\n备注: {row[6] if row[6] else '无'}"
messagebox.showinfo("查询结果", details)
else:
messagebox.showinfo("查询结果", f"未找到学号为 {student_id} 的学生")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"查询失败: {str(e)}")
class UpdateWindow:
"""修改学生信息窗口"""
def __init__(self, parent, values):
self.parent = parent
self.original_id = values[0] # 保存原始学号
self.window = tk.Toplevel(parent.root)
self.window.title("修改学生信息")
self.window.geometry("500x450")
self.window.configure(bg=parent.bg_color)
# 中心化窗口
self.window.update_idletasks()
width = self.window.winfo_width()
height = self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry(f'{width}x{height}+{x}+{y}')
# 创建标题
title_label = tk.Label(
self.window,
text="修改学生信息",
font=self.parent.title_font,
bg=self.parent.bg_color,
fg="#333333"
)
title_label.pack(pady=(20, 30))
# 创建表单框架
form_frame = tk.Frame(self.window, bg=self.parent.bg_color)
form_frame.pack(padx=40)
self.create_widgets(form_frame, values)
# 创建按钮框架
button_frame = tk.Frame(self.window, bg=self.parent.bg_color)
button_frame.pack(pady=30)
# 更新按钮
update_btn = self.parent.create_button(
button_frame,
"更新",
self.update,
"#FF9800",
"#F57C00"
)
update_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=20)
# 取消按钮
cancel_btn = self.parent.create_button(
button_frame,
"取消",
self.window.destroy,
self.parent.delete_bg,
self.parent.delete_hover
)
cancel_btn.pack(side=tk.LEFT, padx=10, ipady=5, ipadx=20)
def create_widgets(self, parent, values):
"""创建表单组件"""
# 字段定义
fields = [
("学号:", "id_entry"),
("姓名:", "name_entry"),
("性别:", "gender_var"), # 特别注意:这是单选按钮,不是输入框
("出生时间 (YYYY/MM/DD):", "birth_entry", "1997/02/10"),
("专业:", "major_entry", "计算机"),
("总学分:", "credits_entry", "50"),
("备注:", "remarks_entry")
]
row = 0
for field in fields:
label_text = field[0]
field_name = field[1]
default_value = field[2] if len(field) > 2 else ""
# 标签
label = tk.Label(
parent,
text=label_text,
font=self.parent.label_font,
bg=self.parent.bg_color,
fg="#333333",
anchor="e"
)
label.grid(row=row, column=0, padx=10, pady=10, sticky="e")
# 如果是性别字段,创建单选按钮
if field_name == "gender_var":
gender_frame = tk.Frame(parent, bg=self.parent.bg_color)
gender_frame.grid(row=row, column=1, padx=10, pady=10, sticky="w")
gender_value = values[2] if values[2] else "男"
self.gender_var = tk.StringVar(value=gender_value)
tk.Radiobutton(gender_frame, text="男", variable=self.gender_var,
value="男", font=self.parent.label_font,
bg=self.parent.bg_color, selectcolor=self.parent.bg_color).pack(side=tk.LEFT, padx=10)
tk.Radiobutton(gender_frame, text="女", variable=self.gender_var,
value="女", font=self.parent.label_font,
bg=self.parent.bg_color, selectcolor=self.parent.bg_color).pack(side=tk.LEFT, padx=10)
row += 1
continue
# 对于其他字段,创建输入框
entry = tk.Entry(
parent,
width=30,
font=self.parent.label_font,
relief=tk.GROOVE,
bd=2
)
entry.grid(row=row, column=1, padx=10, pady=10, sticky="w")
# 根据字段名设置默认值
if field_name == "id_entry":
entry.insert(0, values[0])
elif field_name == "name_entry":
entry.insert(0, values[1])
elif field_name == "birth_entry":
entry.insert(0, values[3])
elif field_name == "major_entry":
entry.insert(0, values[4])
elif field_name == "credits_entry":
entry.insert(0, str(values[5]))
elif field_name == "remarks_entry":
entry.insert(0, values[6] if values[6] else "")
elif default_value:
entry.insert(0, default_value)
# 保存引用
setattr(self, field_name, entry)
row += 1
def update(self):
"""更新学生信息"""
# 获取输入值
student_id = self.id_entry.get().strip()
name = self.name_entry.get().strip()
gender = self.gender_var.get()
birth_date = self.birth_entry.get().strip()
major = self.major_entry.get().strip()
credits = self.credits_entry.get().strip()
remarks = self.remarks_entry.get().strip()
# 验证输入
if not student_id or not name:
messagebox.showwarning("输入错误", "学号和姓名不能为空")
return
# 如果学号已修改,检查新学号是否已存在
if student_id != self.original_id:
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT student_id FROM students WHERE student_id = %s", (student_id,))
if cursor.fetchone():
messagebox.showwarning("输入错误", f"学号 {student_id} 已存在")
cursor.close()
return
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"验证学号失败: {str(e)}")
return
# 更新数据
try:
cursor = self.parent.connection.cursor()
# 转换日期格式
if birth_date:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y/%m/%d')
except ValueError:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y-%m-%d')
except ValueError:
messagebox.showwarning("日期格式错误", "请使用YYYY/MM/DD或YYYY-MM-DD格式")
return
else:
birth_date_obj = None
# 转换学分
try:
credits_int = int(credits) if credits else 0
except ValueError:
credits_int = 0
# 执行更新
update_query = """
UPDATE students
SET student_id = %s, \
name = %s, \
gender = %s, \
birth_date = %s, \
major = %s, \
total_credits = %s, \
remarks = %s
WHERE student_id = %s \
"""
update_values = (student_id, name, gender, birth_date_obj, major, credits_int,
remarks if remarks else None, self.original_id)
cursor.execute(update_query, update_values)
self.parent.connection.commit()
cursor.close()
messagebox.showinfo("更新成功", f"学生 {name} 的信息已更新")
# 刷新主窗口数据
self.parent.load_data()
# 关闭窗口
self.window.destroy()
except Error as e:
messagebox.showerror("数据库错误", f"更新失败: {str(e)}")
def main():
root = tk.Tk()
app = StudentManagementSystem(root)
root.mainloop()
if __name__ == "__main__":
main()
创建数据库sql:
python
-- 删除原有数据库
DROP DATABASE IF EXISTS student_management;
-- 创建新数据库,指定字符集
CREATE DATABASE student_management
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE student_management;
-- 创建表,指定字符集
CREATE TABLE students (
student_id VARCHAR(10) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
gender VARCHAR(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
birth_date DATE,
major VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
total_credits INT,
remarks TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

运行效果:

python代码:
python
import tkinter as tk
from tkinter import ttk, messagebox
import mysql.connector
from mysql.connector import Error
from datetime import datetime
class StudentManagementSystem:
def __init__(self, root):
self.root = root
self.root.title("学生信息管理系统")
self.root.geometry("1000x500")
# 数据库连接配置
self.db_config = {
'host': 'localhost',
'user': 'root', # 修改为你的MySQL用户名
'password': 'root', # 修改为你的MySQL密码
'database': 'student_management'
}
# 初始化数据库连接
self.connection = None
self.connect_db()
self.create_table_if_not_exists()
# 创建GUI
self.create_widgets()
self.load_data()
def connect_db(self):
"""连接到MySQL数据库"""
try:
self.connection = mysql.connector.connect(**self.db_config)
if self.connection.is_connected():
print("成功连接到MySQL数据库")
except Error as e:
messagebox.showerror("数据库连接错误", f"无法连接到数据库: {str(e)}")
self.root.quit()
def create_table_if_not_exists(self):
"""如果表不存在则创建表"""
try:
cursor = self.connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS students
(
student_id
VARCHAR
(
10
) PRIMARY KEY,
name VARCHAR
(
50
) NOT NULL,
gender VARCHAR
(
2
),
birth_date DATE,
major VARCHAR
(
50
),
total_credits INT,
remarks TEXT
)
""")
self.connection.commit()
print("表已准备好")
except Error as e:
messagebox.showerror("数据库错误", f"创建表失败: {str(e)}")
def create_widgets(self):
"""创建GUI组件"""
# 按钮框架
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=10)
# 按钮
tk.Button(btn_frame, text="查询", command=self.query_student, width=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="插入", command=self.insert_student, width=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="删除", command=self.delete_student, width=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="修改", command=self.update_student, width=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="刷新", command=self.load_data, width=10).pack(side=tk.LEFT, padx=5)
# 搜索框架
search_frame = tk.Frame(self.root)
search_frame.pack(pady=5)
tk.Label(search_frame, text="学号/姓名搜索:").pack(side=tk.LEFT, padx=5)
self.search_var = tk.StringVar()
self.search_entry = tk.Entry(search_frame, textvariable=self.search_var, width=30)
self.search_entry.pack(side=tk.LEFT, padx=5)
tk.Button(search_frame, text="搜索", command=self.search_data, width=10).pack(side=tk.LEFT, padx=5)
# 表格框架
table_frame = tk.Frame(self.root)
table_frame.pack(pady=10, padx=20, fill=tk.BOTH, expand=True)
# 创建表格
columns = ("学号", "姓名", "性别", "出生时间", "专业", "总学分", "备注")
self.tree = ttk.Treeview(table_frame, columns=columns, show="headings", height=15)
# 设置列标题和宽度
column_widths = {"学号": 80, "姓名": 80, "性别": 60, "出生时间": 100, "专业": 100, "总学分": 80, "备注": 150}
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=column_widths[col])
# 垂直滚动条
scrollbar = ttk.Scrollbar(table_frame, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
# 布局
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定双击事件
self.tree.bind('<Double-Button-1>', self.on_double_click)
def load_data(self):
"""从数据库加载数据到表格"""
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
try:
cursor = self.connection.cursor()
cursor.execute("SELECT * FROM students ORDER BY student_id")
rows = cursor.fetchall()
# 插入数据到表格
for row in rows:
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 处理None值
values = list(row)
for i in range(len(values)):
if values[i] is None:
values[i] = ""
values[3] = birth_date_str
self.tree.insert("", tk.END, values=values)
cursor.close()
print(f"加载了 {len(rows)} 条记录")
except Error as e:
messagebox.showerror("数据库错误", f"加载数据失败: {str(e)}")
def search_data(self):
"""搜索学生信息"""
search_text = self.search_var.get().strip()
if not search_text:
messagebox.showwarning("搜索提示", "请输入搜索内容")
return
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
try:
cursor = self.connection.cursor()
query = """
SELECT * \
FROM students
WHERE student_id LIKE %s \
OR name LIKE %s
ORDER BY student_id \
"""
search_pattern = f"%{search_text}%"
cursor.execute(query, (search_pattern, search_pattern))
rows = cursor.fetchall()
if rows:
for row in rows:
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 处理None值
values = list(row)
for i in range(len(values)):
if values[i] is None:
values[i] = ""
values[3] = birth_date_str
self.tree.insert("", tk.END, values=values)
messagebox.showinfo("搜索结果", f"找到 {len(rows)} 条记录")
else:
messagebox.showinfo("搜索结果", "未找到相关记录")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"搜索失败: {str(e)}")
def insert_student(self):
"""打开插入学生窗口"""
InsertWindow(self)
def query_student(self):
"""查询特定学生信息"""
QueryWindow(self)
def delete_student(self):
"""删除选中的学生"""
selected_item = self.tree.selection()
if not selected_item:
messagebox.showwarning("删除提示", "请先选择要删除的学生")
return
# 获取选中的学生学号
student_id = self.tree.item(selected_item[0])['values'][0]
student_name = self.tree.item(selected_item[0])['values'][1]
# 确认删除
confirm = messagebox.askyesno("确认删除", f"确定要删除学生 {student_name} (学号: {student_id}) 吗?")
if confirm:
try:
cursor = self.connection.cursor()
cursor.execute("DELETE FROM students WHERE student_id = %s", (student_id,))
self.connection.commit()
# 从表格中移除
self.tree.delete(selected_item[0])
messagebox.showinfo("删除成功", f"已删除学生 {student_name}")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"删除失败: {str(e)}")
def update_student(self):
"""修改选中的学生信息"""
selected_item = self.tree.selection()
if not selected_item:
messagebox.showwarning("修改提示", "请先选择要修改的学生")
return
# 获取选中的学生信息
values = self.tree.item(selected_item[0])['values']
UpdateWindow(self, values)
def on_double_click(self, event):
"""双击表格行时显示详细信息"""
selected_item = self.tree.selection()
if selected_item:
values = self.tree.item(selected_item[0])['values']
details = f"学号: {values[0]}\n姓名: {values[1]}\n性别: {values[2]}\n出生时间: {values[3]}\n专业: {values[4]}\n总学分: {values[5]}\n备注: {values[6]}"
messagebox.showinfo("学生详细信息", details)
def __del__(self):
"""关闭数据库连接"""
if self.connection and self.connection.is_connected():
self.connection.close()
print("数据库连接已关闭")
class InsertWindow:
"""插入学生信息窗口"""
def __init__(self, parent):
self.parent = parent
self.window = tk.Toplevel(parent.root)
self.window.title("插入学生信息")
self.window.geometry("400x350")
self.create_widgets()
def create_widgets(self):
tk.Label(self.window, text="学号:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
self.id_entry = tk.Entry(self.window, width=30)
self.id_entry.grid(row=0, column=1, padx=10, pady=10)
tk.Label(self.window, text="姓名:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
self.name_entry = tk.Entry(self.window, width=30)
self.name_entry.grid(row=1, column=1, padx=10, pady=10)
tk.Label(self.window, text="性别:").grid(row=2, column=0, padx=10, pady=10, sticky="e")
self.gender_var = tk.StringVar(value="男")
gender_frame = tk.Frame(self.window)
gender_frame.grid(row=2, column=1, padx=10, pady=10, sticky="w")
tk.Radiobutton(gender_frame, text="男", variable=self.gender_var, value="男").pack(side=tk.LEFT)
tk.Radiobutton(gender_frame, text="女", variable=self.gender_var, value="女").pack(side=tk.LEFT)
tk.Label(self.window, text="出生时间 (YYYY/MM/DD):").grid(row=3, column=0, padx=10, pady=10, sticky="e")
self.birth_entry = tk.Entry(self.window, width=30)
self.birth_entry.grid(row=3, column=1, padx=10, pady=10)
self.birth_entry.insert(0, "1990/01/01")
tk.Label(self.window, text="专业:").grid(row=4, column=0, padx=10, pady=10, sticky="e")
self.major_entry = tk.Entry(self.window, width=30)
self.major_entry.grid(row=4, column=1, padx=10, pady=10)
self.major_entry.insert(0, "计算机")
tk.Label(self.window, text="总学分:").grid(row=5, column=0, padx=10, pady=10, sticky="e")
self.credits_entry = tk.Entry(self.window, width=30)
self.credits_entry.grid(row=5, column=1, padx=10, pady=10)
self.credits_entry.insert(0, "50")
tk.Label(self.window, text="备注:").grid(row=6, column=0, padx=10, pady=10, sticky="e")
self.remarks_entry = tk.Entry(self.window, width=30)
self.remarks_entry.grid(row=6, column=1, padx=10, pady=10)
# 按钮
btn_frame = tk.Frame(self.window)
btn_frame.grid(row=7, column=0, columnspan=2, pady=20)
tk.Button(btn_frame, text="插入", command=self.insert, width=10).pack(side=tk.LEFT, padx=10)
tk.Button(btn_frame, text="取消", command=self.window.destroy, width=10).pack(side=tk.LEFT, padx=10)
def insert(self):
"""插入新学生到数据库"""
# 获取输入值
student_id = self.id_entry.get().strip()
name = self.name_entry.get().strip()
gender = self.gender_var.get()
birth_date = self.birth_entry.get().strip()
major = self.major_entry.get().strip()
credits = self.credits_entry.get().strip()
remarks = self.remarks_entry.get().strip()
# 验证输入
if not student_id or not name:
messagebox.showwarning("输入错误", "学号和姓名不能为空")
return
# 验证学号是否已存在
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT student_id FROM students WHERE student_id = %s", (student_id,))
if cursor.fetchone():
messagebox.showwarning("输入错误", f"学号 {student_id} 已存在")
cursor.close()
return
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"验证学号失败: {str(e)}")
return
# 插入数据
try:
cursor = self.parent.connection.cursor()
# 转换日期格式
if birth_date:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y/%m/%d')
except ValueError:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y-%m-%d')
except ValueError:
messagebox.showwarning("日期格式错误", "请使用YYYY/MM/DD或YYYY-MM-DD格式")
return
else:
birth_date_obj = None
# 转换学分
try:
credits_int = int(credits) if credits else 0
except ValueError:
credits_int = 0
# 执行插入
cursor.execute("""
INSERT INTO students (student_id, name, gender, birth_date, major, total_credits, remarks)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""",
(student_id, name, gender, birth_date_obj, major, credits_int, remarks if remarks else None))
self.parent.connection.commit()
cursor.close()
messagebox.showinfo("插入成功", f"学生 {name} 已成功添加")
# 刷新主窗口数据
self.parent.load_data()
# 关闭窗口
self.window.destroy()
except Error as e:
messagebox.showerror("数据库错误", f"插入失败: {str(e)}")
class QueryWindow:
"""查询学生信息窗口"""
def __init__(self, parent):
self.parent = parent
self.window = tk.Toplevel(parent.root)
self.window.title("查询学生信息")
self.window.geometry("300x150")
self.create_widgets()
def create_widgets(self):
tk.Label(self.window, text="请输入学号:").pack(pady=10)
self.query_id_entry = tk.Entry(self.window, width=30)
self.query_id_entry.pack(pady=5)
btn_frame = tk.Frame(self.window)
btn_frame.pack(pady=20)
tk.Button(btn_frame, text="查询", command=self.query, width=10).pack(side=tk.LEFT, padx=10)
tk.Button(btn_frame, text="取消", command=self.window.destroy, width=10).pack(side=tk.LEFT, padx=10)
def query(self):
"""查询学生信息"""
student_id = self.query_id_entry.get().strip()
if not student_id:
messagebox.showwarning("查询提示", "请输入学号")
return
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT * FROM students WHERE student_id = %s", (student_id,))
row = cursor.fetchone()
if row:
# 格式化日期
birth_date = row[3]
if birth_date:
birth_date_str = birth_date.strftime('%Y/%m/%d')
else:
birth_date_str = ""
# 显示查询结果
details = f"学号: {row[0]}\n姓名: {row[1]}\n性别: {row[2]}\n出生时间: {birth_date_str}\n专业: {row[4]}\n总学分: {row[5]}\n备注: {row[6] if row[6] else '无'}"
messagebox.showinfo("查询结果", details)
else:
messagebox.showinfo("查询结果", f"未找到学号为 {student_id} 的学生")
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"查询失败: {str(e)}")
class UpdateWindow:
"""修改学生信息窗口"""
def __init__(self, parent, values):
self.parent = parent
self.original_id = values[0] # 保存原始学号
self.window = tk.Toplevel(parent.root)
self.window.title("修改学生信息")
self.window.geometry("400x350")
self.create_widgets(values)
def create_widgets(self, values):
tk.Label(self.window, text="学号:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
self.id_entry = tk.Entry(self.window, width=30)
self.id_entry.grid(row=0, column=1, padx=10, pady=10)
self.id_entry.insert(0, values[0])
tk.Label(self.window, text="姓名:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
self.name_entry = tk.Entry(self.window, width=30)
self.name_entry.grid(row=1, column=1, padx=10, pady=10)
self.name_entry.insert(0, values[1])
tk.Label(self.window, text="性别:").grid(row=2, column=0, padx=10, pady=10, sticky="e")
self.gender_var = tk.StringVar(value=values[2] if values[2] else "男")
gender_frame = tk.Frame(self.window)
gender_frame.grid(row=2, column=1, padx=10, pady=10, sticky="w")
tk.Radiobutton(gender_frame, text="男", variable=self.gender_var, value="男").pack(side=tk.LEFT)
tk.Radiobutton(gender_frame, text="女", variable=self.gender_var, value="女").pack(side=tk.LEFT)
tk.Label(self.window, text="出生时间 (YYYY/MM/DD):").grid(row=3, column=0, padx=10, pady=10, sticky="e")
self.birth_entry = tk.Entry(self.window, width=30)
self.birth_entry.grid(row=3, column=1, padx=10, pady=10)
self.birth_entry.insert(0, values[3])
tk.Label(self.window, text="专业:").grid(row=4, column=0, padx=10, pady=10, sticky="e")
self.major_entry = tk.Entry(self.window, width=30)
self.major_entry.grid(row=4, column=1, padx=10, pady=10)
self.major_entry.insert(0, values[4])
tk.Label(self.window, text="总学分:").grid(row=5, column=0, padx=10, pady=10, sticky="e")
self.credits_entry = tk.Entry(self.window, width=30)
self.credits_entry.grid(row=5, column=1, padx=10, pady=10)
self.credits_entry.insert(0, values[5])
tk.Label(self.window, text="备注:").grid(row=6, column=0, padx=10, pady=10, sticky="e")
self.remarks_entry = tk.Entry(self.window, width=30)
self.remarks_entry.grid(row=6, column=1, padx=10, pady=10)
self.remarks_entry.insert(0, values[6])
# 按钮
btn_frame = tk.Frame(self.window)
btn_frame.grid(row=7, column=0, columnspan=2, pady=20)
tk.Button(btn_frame, text="更新", command=self.update, width=10).pack(side=tk.LEFT, padx=10)
tk.Button(btn_frame, text="取消", command=self.window.destroy, width=10).pack(side=tk.LEFT, padx=10)
def update(self):
"""更新学生信息"""
# 获取输入值
student_id = self.id_entry.get().strip()
name = self.name_entry.get().strip()
gender = self.gender_var.get()
birth_date = self.birth_entry.get().strip()
major = self.major_entry.get().strip()
credits = self.credits_entry.get().strip()
remarks = self.remarks_entry.get().strip()
# 验证输入
if not student_id or not name:
messagebox.showwarning("输入错误", "学号和姓名不能为空")
return
# 如果学号已修改,检查新学号是否已存在
if student_id != self.original_id:
try:
cursor = self.parent.connection.cursor()
cursor.execute("SELECT student_id FROM students WHERE student_id = %s", (student_id,))
if cursor.fetchone():
messagebox.showwarning("输入错误", f"学号 {student_id} 已存在")
cursor.close()
return
cursor.close()
except Error as e:
messagebox.showerror("数据库错误", f"验证学号失败: {str(e)}")
return
# 更新数据
try:
cursor = self.parent.connection.cursor()
# 转换日期格式
if birth_date:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y/%m/%d')
except ValueError:
try:
birth_date_obj = datetime.strptime(birth_date, '%Y-%m-%d')
except ValueError:
messagebox.showwarning("日期格式错误", "请使用YYYY/MM/DD或YYYY-MM-DD格式")
return
else:
birth_date_obj = None
# 转换学分
try:
credits_int = int(credits) if credits else 0
except ValueError:
credits_int = 0
# 执行更新
cursor.execute("""
UPDATE students
SET student_id = %s,
name = %s,
gender = %s,
birth_date = %s,
major = %s,
total_credits = %s,
remarks = %s
WHERE student_id = %s
""",
(student_id, name, gender, birth_date_obj, major, credits_int, remarks if remarks else None,
self.original_id))
self.parent.connection.commit()
cursor.close()
messagebox.showinfo("更新成功", f"学生 {name} 的信息已更新")
# 刷新主窗口数据
self.parent.load_data()
# 关闭窗口
self.window.destroy()
except Error as e:
messagebox.showerror("数据库错误", f"更新失败: {str(e)}")
def main():
root = tk.Tk()
app = StudentManagementSystem(root)
root.mainloop()
if __name__ == "__main__":
main()