学生成绩管理系统用Tkinter,sqlite3实现

简介的口水话就不过多介绍了,代码有点多,源码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": ''}),
                                ]
                            })
                        ]
                    })
                ]
            })
        ])

不足之处请见谅,维护开源和谐环境 ,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值