简介的口水话就不过多介绍了,代码有点多,源码Q_Q
1690361973
下面是程序的主要代码:
一.导入库,没有自行下载 pip install 库名
# 下载本地导入模板
import shutil
# UI库
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
# 操作sqlite数据库
import sqlite3
# os获取文件目录,pandas处理导入的数据
import os
import pandas as pd
# 处理背景图片
from PIL import Image, ImageTk
二.创建登录注册类LoginApp
class LoginApp:
def __init__(self, root, switch_to_app):
self.root = root
self.root.title("学生成绩管理")
self.screen_width = self.root.winfo_screenwidth()
self.screen_height = self.root.winfo_screenheight()
self.root.geometry(f'{self.screen_width}x{self.screen_height}')
self.switch_to_app = switch_to_app
self.create_widgets()
self.show_login_frame()
filespath = os.path.abspath(os.path.dirname(__file__))
# 连接到SQLite数据库
conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
userpassword TEXT NOT NULL
)
""")
conn.commit()
conn.close()
def create_widgets(self):
filespath = os.path.abspath(os.path.dirname(__file__))
# 创建背景Canvas
self.canvas = tk.Canvas(self.root, width=self.screen_width, height=self.screen_height)
self.canvas.pack(expand=True, fill='both')
# 加载背景图片
self.bg_image = Image.open(fr'{filespath}\123.ppm')
self.bg_image = self.bg_image.resize((self.screen_width, self.screen_height), Image.LANCZOS)
self.bg_photo = ImageTk.PhotoImage(self.bg_image)
self.canvas.create_image(0, 0, anchor='nw', image=self.bg_photo)
self.main_frame = ttk.Frame(self.canvas, style='MainFrame.TFrame')
self.canvas.create_window(self.screen_width//2, self.screen_height//2, anchor='center', window=self.main_frame)
self.login_frame = ttk.Frame(self.main_frame, style='InnerFrame.TFrame')
self.register_frame = ttk.Frame(self.main_frame, style='InnerFrame.TFrame')
# 创建样式
style = ttk.Style()
#style.configure('MainFrame.TFrame', background='#77ac30')
style.configure('InnerFrame.TFrame', background='#add8e6', relief='solid', borderwidth=1)
style.configure('TButton', font=('宋体', 12, 'bold'), background='#007acc', padding=8)
style.configure('TLabel', font=('宋体', 16, 'bold'))
style.configure('TEntry', padding=6)
# 设置登录页面大小
self.login_frame.configure(width=300, height=200)
self.login_frame.grid_propagate(False)
# 创建登录页面
self.username_label = ttk.Label(self.login_frame, text="用户名:", style='TLabel')
self.username_label.grid(row=0, column=0, pady=10, padx=10, sticky='w')
self.username_entry = ttk.Entry(self.login_frame, style='TEntry')
self.username_entry.grid(row=0, column=1, pady=10, padx=10, sticky='e')
self.password_label = ttk.Label(self.login_frame, text="密 码:", style='TLabel')
self.password_label.grid(row=1, column=0, pady=10, padx=10, sticky='w')
self.password_entry = ttk.Entry(self.login_frame, show="*", style='TEntry')
self.password_entry.grid(row=1, column=1, pady=10, padx=10, sticky='e')
self.login_button = ttk.Button(self.login_frame, text="登录", command=self.login, style='TButton')
self.login_button.grid(row=2, column=0, columnspan=2, padx=10,pady=10, sticky='w')
self.switch_to_register_button = ttk.Button(self.login_frame, text="注册", command=self.show_register_frame, style='TButton')
self.switch_to_register_button.grid(row=2, column=1, padx=10,pady=10, sticky='e')
# 设置注册页面大小
self.register_frame.configure(width=300, height=250)
self.register_frame.grid_propagate(False)
# 创建注册页面
self.reg_username_label = ttk.Label(self.register_frame, text="用户名:", style='TLabel')
self.reg_username_label.grid(row=0, column=0, pady=10, padx=10, sticky='w')
self.reg_username_entry = ttk.Entry(self.register_frame, style='TEntry')
self.reg_username_entry.grid(row=0, column=1, pady=10, padx=10, sticky='e')
self.reg_password_label = ttk.Label(self.register_frame, text="密 码:", style='TLabel')
self.reg_password_label.grid(row=1, column=0, pady=10, padx=10, sticky='w')
self.reg_password_entry = ttk.Entry(self.register_frame, show="*", style='TEntry')
self.reg_password_entry.grid(row=1, column=1, pady=10, padx=10, sticky='e')
self.register_button = ttk.Button(self.register_frame, text="注册", command=self.register, style='TButton')
self.register_button.grid(row=2, column=0, columnspan=2, pady=10)
self.switch_to_login_button = ttk.Button(self.register_frame, text="返回登录", command=self.show_login_frame, style='TButton')
self.switch_to_login_button.grid(row=3, column=0, columnspan=2, pady=10)
def show_login_frame(self):
self.register_frame.pack_forget()
self.login_frame.pack(expand=True)
def show_register_frame(self):
self.login_frame.pack_forget()
self.register_frame.pack(expand=True)
def login(self):
username = self.username_entry.get()
password = self.password_entry.get()
filespath = os.path.abspath(os.path.dirname(__file__))
# 连接到SQLite数据库
conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
cursor = conn.cursor()
cursor.execute("SELECT * FROM user WHERE username = ? AND userpassword = ?", (username, password))
result = cursor.fetchone()
conn.close()
if result:
messagebox.showinfo("登录成功", "登录成功!")
self.switch_to_app()
else:
messagebox.showerror("登录失败", "用户名或密码错误!")
def register(self):
username = self.reg_username_entry.get()
password = self.reg_password_entry.get()
if not username or not password:
messagebox.showerror("注册失败", "用户名和密码不能为空!")
return
filespath = os.path.abspath(os.path.dirname(__file__))
# 连接到SQLite数据库
conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
cursor = conn.cursor()
try:
cursor.execute("INSERT INTO user (username, userpassword) VALUES (?, ?)", (username, password))
conn.commit()
messagebox.showinfo("注册成功", "注册成功!")
self.show_login_frame()
except sqlite3.IntegrityError:
messagebox.showerror("注册失败", "用户名已存在!")
finally:
conn.close()
登录注册的页面:
注:这里需要提示一下, tkinter只能读取ppm格式的彩色图片,需要将jpg或者png图片转换一下才能使用
三.主页面代码App类,这里代码有点多,就展示页面的代码了,数据库操作没有在里面,自行研究的时候注意下
class App:
def __init__(self, root):
self.students = {}
self.root = root
self.root.title("学生成绩管理程序")
self.root.configure(bg='#77ac30')
self.screen_width = self.root.winfo_screenwidth()
self.screen_height = self.root.winfo_screenheight()
self.root.geometry(f'{self.screen_width}x{self.screen_height}')
self.pages = {}
self.tab_titles = {}
self.entries = {}
self.notebook = CustomNotebook(self.root,self.pages,self.tab_titles)
self.notebook.place(x=260, y=5, width=self.screen_width - 280, height=self.screen_height - 88)
self.subjects = ['序号', '学号', "姓名", '课程名', '专业', '学分', '平时成绩', '期末成绩', '总评成绩', '绩点']
self.button_add = tk.Button(root, text="添加学生成绩", bg='green', fg='white', font=('宋体', 16, 'bold'),
command=lambda: self.show_page('add'))
self.button_add.place(x=10, y=230)
self.button_query = tk.Button(root, text="查询学生成绩", bg='green', fg='white', font=('宋体', 16, 'bold'),
command=lambda: self.show_page('query'))
self.button_query.place(x=10, y=300)
self.button_list = tk.Button(root, text="所有学生成绩", bg='green', fg='white', font=('宋体', 16, 'bold'),
command=lambda: self.show_page('list'))
self.button_list.place(x=10, y=370)
self.frames = []
self.data = ''
self.current_page = 1
self.page_size = 40 # 每页显示的行数
# 根据被点击的按钮,显示指定的页面标签,如果标签不存在则创建新标签
def show_page(self, page):
# 检查页面是否已存在:if page not in self.pages
# self.pages 是一个字典,存储已创建的页面。
# 如果 page 不在 self.pages 中,表示该页面尚未创建
if page not in self.pages:
if page == 'add':
self.pages[page] = self.create_add_page()
self.add_tab(self.pages[page], '添加学生成绩', page)
elif page == 'query':
self.pages[page] = self.create_query_page()
self.add_tab(self.pages[page], '查询学生成绩', page)
elif page == 'list':
self.pages[page] = self.create_list_page()
self.add_tab(self.pages[page], '所有学生成绩', page)
self.notebook.select(self.pages[page])
# 将页面框架添加到 notebook 中并记录标签信息
def add_tab(self, frame, title, page_key):
self.notebook.add(frame, text=title)
self.tab_titles[title] = page_key
# 添加成绩
def create_add_page(self):
frame = tk.Frame(self.notebook, bg='#ebeadf')
input_widths = [18, 13, 30, 30, 10, 10, 10, 10, 10] # 设置输入框宽度
tree_widths = [1, 200, 100, 200, 200, 60, 60, 60, 60, 60] # 设置表格列宽度
for i, (subject, width) in enumerate(zip(self.subjects[1:], input_widths)):
tk.Label(frame, text=f"{subject}", bg='#ebeadf', font=('宋体', 16, 'bold')).grid(row=1, column=i, padx=10, pady=5, sticky='we')
entry = tk.Entry(frame, font=('宋体', 16), width=width)
entry.grid(row=2, column=i, padx=0, pady=5)
self.entries[subject] = entry
tk.Button(frame, text="提交", bg='green', fg='white', font=('宋体', 12, 'bold'), command=self.save_student).grid(row=2, column=len(self.subjects) + 1, padx=5, pady=10)
# 在frame中直接放置导入按钮,设置sticky参数来左对齐
tk.Button(frame, text="导入", bg='green', fg='white', font=('宋体', 12, 'bold'), command=self.import_students).grid(row=3, column=0, padx=10, pady=5, sticky='w')
tk.Button(frame, text="导入模板下载", bg='green', fg='white', font=('宋体', 12, 'bold'), command=self.template_download).grid(row=3, column=1, padx=0, pady=5, sticky='w')
# 表格
tree_frame = tk.Frame(frame, bg='#ebeadf')
tree_frame.grid(row=4, column=0, columnspan=len(self.subjects) + 2, pady=10, padx=10, sticky='we')
# 设置表格的字体样式
style = ttk.Style()
style.configure("Treeview.Heading", font=('宋体', 16, 'bold')) # 设置表头字体
style.configure("Treeview", font=('宋体', 16)) # 设置表格内容字体
self.tree = ttk.Treeview(tree_frame, columns=self.subjects, show='headings', height=40)
for col, widths in zip(self.subjects, tree_widths):
self.tree.heading(col, text=col)
self.tree.column(col, width=widths, anchor='center')
# 添加复制功能的右键菜单
self.add_copy_functionality(self.tree)
# 添加滚动条
scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscroll=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 设置列宽自适应
for i in range(len(self.subjects)):
frame.grid_columnconfigure(i, weight=1)
#frame.grid_columnconfigure(len(subjects), weight=1) # 使提交按钮也居中
return frame
# 查询成绩
def create_query_page(self):
frame = tk.Frame(self.notebook, bg='#ebeadf')
tree_widths = [50, 280, 160, 300, 300, 100, 100, 100, 100, 100] # 设置表格列宽度
# 标签和输入框
tk.Label(frame, text="请选择查询条件:", bg='#ebeadf', font=('宋体', 20, 'bold')).grid(row=0, column=0, padx=10, pady=5, sticky='we')
# 创建Combobox
self.search_option = tk.StringVar()
self.option_menu = ttk.Combobox(frame, textvariable=self.search_option, values=self.subjects[1:], font=('宋体', 16,'bold'))
self.option_menu.grid(row=0, column=1, padx=10, pady=5, sticky='we')
self.option_menu.current(0) # 设置默认选项为 '学号'
# 设置下拉框展开后的字体样式
style = ttk.Style()
style.configure('TCombobox', font=('宋体', 16, 'bold')) # 设置下拉框展开后的字体样式
# 标签和下拉输入框
tk.Label(frame, text="请输入查询内容:", bg='#ebeadf', font=('宋体', 20, 'bold')).grid(row=0, column=2, padx=10, pady=5, sticky='we')
self.entry_name_query = ttk.Combobox(frame, values=[], font=('宋体', 16), width=20)
self.entry_name_query.grid(row=0, column=3, padx=10, pady=5, sticky='we')
tk.Button(frame, text="查询", bg='blue', fg='white', font=('宋体', 16, 'bold'), command=self.query_student).grid(row=0, column=4, padx=10, pady=5, sticky='we')
# 表格
tree_frame = tk.Frame(frame, bg='#ebeadf')
tree_frame.grid(row=3, column=0, columnspan=len(self.subjects) + 2, pady=10, padx=10, sticky='we')
# 设置表格的字体样式
style = ttk.Style()
style.configure("Treeview.Heading", font=('宋体', 16, 'bold')) # 设置表头字体
style.configure("Treeview", font=('宋体', 16)) # 设置表格内容字体
# self.query_tree 是一个 ttk.Treeview 实例,用于在界面上显示表格
self.query_tree = ttk.Treeview(tree_frame, columns=self.subjects, show='headings', height=40)
self.query_tree.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky='we')
for col, widths in zip(self.subjects, tree_widths):
self.query_tree.heading(col, text=col)
self.query_tree.column(col, width=widths, anchor='center')
# 添加复制功能的右键菜单,传值为 一个 ttk.Treeview 实例 self.query_tree
self.add_copy_functionality(self.query_tree)
# 添加修改和删除按钮
tk.Button(frame, text="修改", bg='orange', fg='white', font=('宋体', 16, 'bold'), command=self.update_student).grid(row=1, column=0, padx=10, pady=10, sticky='we')
tk.Button(frame, text="删除", bg='red', fg='white', font=('宋体', 16, 'bold'), command=self.delete_student).grid(row=1, column=1, padx=10, pady=10, sticky='we')
for i in range(len(self.subjects)):
frame.grid_columnconfigure(i, weight=1)
return frame
# 列出所有成绩
def create_list_page(self):
frame = tk.Frame(self.notebook, bg='#ebeadf')
tree_widths = [50, 280, 160, 300, 300, 100, 100, 100, 100, 100] # 设置表格列宽度
middle_col = (len(self.subjects) + 2) // 2
tk.Button(frame, text="列出所有学生成绩", bg='blue', fg='white', font=('宋体', 16, 'bold'),
command=self.sh_page).grid(row=0, column=middle_col, padx=10, pady=5, sticky='we')
# 表格
tree_frame = tk.Frame(frame, bg='#ebeadf')
tree_frame.grid(row=3, column=0, columnspan=len(self.subjects) + 2, pady=10, padx=10, sticky='we')
# 设置表格的字体样式
style = ttk.Style()
style.configure("Treeview.Heading", font=('宋体', 16, 'bold')) # 设置表头字体
style.configure("Treeview", font=('宋体', 16)) # 设置表格内容字体
self.list_result = ttk.Treeview(tree_frame, columns=self.subjects, show='headings', height=40)
self.list_result.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky='we')
for col, widths in zip(self.subjects, tree_widths):
self.list_result.heading(col, text=col)
self.list_result.column(col, width=widths, anchor='center')
# 添加复制功能的右键菜单
self.add_copy_functionality(self.list_result)
# 分页控制区
pagination_frame = tk.Frame(frame, bg='#ebeadf')
pagination_frame.grid(row=4, column=0, columnspan=len(self.subjects) + 2, pady=10, padx=10, sticky='we')
self.page_label = tk.Label(pagination_frame, text="第 1 共 ?", bg='#ebeadf', font=('宋体', 16, 'bold'))
self.page_label.pack(side=tk.LEFT)
prev_button = tk.Button(pagination_frame, text="上一页", command=self.prev_page, font=('宋体', 16, 'bold'))
prev_button.pack(side=tk.LEFT, padx=10)
next_button = tk.Button(pagination_frame, text="下一页", command=self.next_page, font=('宋体', 16, 'bold'))
next_button.pack(side=tk.LEFT)
for i in range(len(self.subjects)):
frame.grid_columnconfigure(i, weight=1)
frame.grid_columnconfigure(len(self.subjects), weight=1) # 使提交按钮也居中
# 将创建好的 frame 对象添加到 self.frames 列表中,方便以后进行管理和操作
self.frames.append(frame)
# 将创建好的 frame 添加到 notebook 控件中,并显示为一个标签页
self.notebook.add(frame, text='Tab 1')
return frame
这里是主页面展示:
添加成绩:
查询,修改,删除:
列出所有成绩:
所有页面的单元格都支持复制,减少输入操作
四.运行
if __name__ == '__main__':
root = tk.Tk()
def switch_to_app():
for widget in root.winfo_children():
widget.destroy()
app = App(root)
login_app = LoginApp(root, switch_to_app)
root.mainloop()
总结:程序还有很大空间可以添加其他功能,其他的功能可以慢慢优化开发,欢迎收藏。
最后差点忘了,标签页的x按钮也要单独创建的,代码如下
class CustomNotebook(ttk.Notebook):
__initialized = False
def __init__(self, master,pages,tab_titles,*args, **kwargs):
if not self.__initialized:
self.__initialize_custom_style()
self.__initialized = True
kwargs["style"] = "CustomNotebook"
ttk.Notebook.__init__(self, master, *args, **kwargs)
self.pages = pages
self.tab_titles = tab_titles
self._active = None
self.bind("<ButtonPress-1>", self.on_close_press, True) # 左键单击按下
self.bind("<ButtonRelease-1>", self.on_close_release) # 左键单击松开
def on_close_press(self, event):
"""当按钮按在关闭按钮上时调用"""
element = self.identify(event.x, event.y)
if "close" in element:
index = self.index("@%d,%d" % (event.x, event.y))
self.state(['pressed'])
self._active = index
def on_close_release(self, event):
"""当关闭按钮上的按钮被释放时调用"""
if not self.instate(['pressed']):
return
element = self.identify(event.x, event.y)
index = self.index("@%d,%d" % (event.x, event.y))
if "close" in element and self._active == index:
page_text = self.tab(index, "text")
page_key = self.tab_titles.get(page_text)
self.notebook.forget(index)
# page_id = self.tabs()[index]
# self.forget(index)
# self.event_generate("<<NotebookTabClosed>>", data=page_id)
if page_key:
del self.pages[page_key]
if page_text:
del self.tab_titles[page_text]
self.state(["!pressed"])
self._active = None
def __initialize_custom_style(self):
style = ttk.Style()
self.images = (
tk.PhotoImage("img_close", data='''
R0lGODlhCAAIAMIBAAAAADs7O4+Pj9nZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg
d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU
5kEJADs=
'''),
tk.PhotoImage("img_closeactive", data='''
R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2cbGxsbGxsbGxsbGxsbGxsbGxiH5BAEKAAQALAAA
AAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU5kEJADs=
'''),
tk.PhotoImage("img_closepressed", data='''
R0lGODlhCAAIAMIEAAAAAOUqKv9mZtnZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg
d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU
5kEJADs=
''')
)
style.element_create("close", "image", "img_close",
("active", "pressed", "!disabled", "img_closepressed"),
("active", "!disabled", "img_closeactive"), border=8, sticky='')
style.layout("CustomNotebook", [("CustomNotebook.client", {"sticky": "nswe"})])
style.layout("CustomNotebook.Tab", [
("CustomNotebook.tab", {
"sticky": "nswe",
"children": [
("CustomNotebook.padding", {
"side": "top",
"sticky": "nswe",
"children": [
("CustomNotebook.focus", {
"side": "top",
"sticky": "nswe",
"children": [
("CustomNotebook.label", {"side": "left", "sticky": ''}),
("CustomNotebook.close", {"side": "left", "sticky": ''}),
]
})
]
})
]
})
])
不足之处请见谅,维护开源和谐环境 ,转载请注明出处。