学生成绩管理系统PyQt5+sqlite3数据库版

上一篇 学生成绩管理系统 是tkinter+sqlite3数据库版,为了满足更多人,PyQt5版的他来了,为了对新手友好一点,这篇文章我将会上完整代码,并且分开阐述。

老规矩:简介的口水话就不过多介绍了,代码有点多,如果想要源码的直接扫我,或QQ 1690361973

下面是程序的主要代码:

一.导入库,没有自行下载 pip install 库名 

# 复制粘贴库
import shutil
# 界面库
import tkinter as tk
from tkinter import ttk, messagebox,  filedialog
# 数据库
import sqlite3
# 处理文件
import os
# 处理导入的数据
import pandas as pd
# 处理背景图片
from PIL import Image, ImageTk

二.创建主页面类 MainApp,之后的内容都将在这个页面中显示

class MainApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("学生成绩管理系统")
        # 设置窗口大小
        # 获取当前屏幕的可用几何区域
        screen = QApplication.primaryScreen()
        available_rect = screen.availableGeometry()
        available_width = available_rect.width()
        available_height = available_rect.height()

        # 设置窗口大小为可用屏幕尺寸减去30像素,以确保窗口不会完全覆盖屏幕
        self.resize(available_width - 30, available_height - 30)

        # 获取屏幕尺寸并计算窗口居中的位置
        x = (available_width - self.width()) // 2
        y = (available_height - self.height()) // 2

        # 将窗口移动到屏幕中央
        self.move(5, -5)

        # 创建一个QStackedWidget对象,用于管理多个页面
        self.stack = QStackedWidget()
        # 将QStackedWidget设置为主窗口的中央控件
        self.setCentralWidget(self.stack)

        # 创建登录页面,并将切换到功能页面的方法传递给它
        self.login_app = LoginApp(self.switch_to_app)
        # 将登录页面添加到QStackedWidget中
        self.stack.addWidget(self.login_app)

        # 创建主页面
        self.main_page = App()
        # 将主页面添加到QStackedWidget中
        self.stack.addWidget(self.main_page)

    # 定义一个方法,用于切换到功能页面
    def switch_to_app(self):
        # 将当前显示的页面切换为功能页面
        self.stack.setCurrentWidget(self.main_page)

这里来讲一下如何实现的,首先主页面会按照给定的大小生成一个页面,并把登陆注册的内容加载进去,登录成功后,会调用切换页面的函数 switch_to_app 将当前显示的页面切换成功能页面

下面是这个类的展示页面

三.功能页面代码App类,这里代码有点多,这个页面的代码有点多,我分开来说

3.1创建类,初始化参数

class App(QMainWindow):
    def __init__(self):
        super().__init__()

        self.students = {}
        self.subjects = ['序号', '学号', "姓名", '课程名', '专业', '学分', '平时成绩', '期末成绩', '总评成绩', '绩点']
        self.entries = {}
        self.initUI()

3.2功能的主页面

# 主页面
    def initUI(self):
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QHBoxLayout(self.central_widget)  # 使用水平布局

        # 创建一个新的垂直布局,并将其放在左边
        self.button_layout = QVBoxLayout()
        self.button_layout.setSpacing(10)  # 设置按钮之间的间距
        self.button_layout.setAlignment(Qt.AlignTop)  # 设置垂直布局顶部对齐
        self.layout.addLayout(self.button_layout)

        # 添加顶部间距(固定高度)
        top_spacer = QSpacerItem(20, 50, QSizePolicy.Minimum, QSizePolicy.Fixed)  # 调整高度
        self.button_layout.addItem(top_spacer)

        # 添加按钮到垂直布局,并设置样式
        self.button_add = QPushButton('添加学生成绩')
        self.button_add.setStyleSheet(
            "QPushButton { font-size: 16px; font-family: Arial; font-weight: bold; color: black; min-height: 20px; }")
        self.button_add.clicked.connect(lambda: self.show_page('add'))
        self.button_layout.addWidget(self.button_add)

        self.button_query = QPushButton('查询学生成绩')
        self.button_query.setStyleSheet(
            "QPushButton { font-size: 16px; font-family: Arial; font-weight: bold; color: black; min-height: 20px; }")
        self.button_query.clicked.connect(lambda: self.show_page('query'))
        self.button_layout.addWidget(self.button_query)

        self.button_list = QPushButton('所有学生成绩')
        self.button_list.setStyleSheet(
            "QPushButton { font-size: 16px; font-family: Arial; font-weight: bold; color: black; min-height: 20px; }")
        self.button_list.clicked.connect(lambda: self.show_page('list'))
        self.button_layout.addWidget(self.button_list)

        # 将 QTabWidget 放在右边
        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True)  # 设置标签页可关闭
        self.tab_widget.tabCloseRequested.connect(self.close_page)  # 连接关闭信号到槽函数
        self.layout.addWidget(self.tab_widget)

        self.pages = {}
        self.current_page = 1
        self.page_size = 24  # 每页显示的行数

3.3打开标签页

# 打开标签页
    def show_page(self, page):
        if page not in self.pages:
            if page == 'add':
                self.pages[page] = self.create_add_page()
                self.tab_widget.addTab(self.pages[page], '添加学生成绩')
            elif page == 'query':
                self.pages[page] = self.create_query_page()
                self.tab_widget.addTab(self.pages[page], '查询学生成绩')
            elif page == 'list':
                self.pages[page] = self.create_list_page()
                self.tab_widget.addTab(self.pages[page], '所有学生成绩')
        else:
            index = self.tab_widget.indexOf(self.pages[page])
            if index == -1:
                if page == 'add':
                    self.pages[page] = self.create_add_page()
                    self.tab_widget.addTab(self.pages[page], '添加学生成绩')
                elif page == 'query':
                    self.pages[page] = self.create_query_page()
                    self.tab_widget.addTab(self.pages[page], '查询学生成绩')
                elif page == 'list':
                    self.pages[page] = self.create_list_page()
                    self.tab_widget.addTab(self.pages[page], '所有学生成绩')
        self.tab_widget.setCurrentWidget(self.pages[page])

3.4关闭标签页窗口

# 关闭窗口
    def close_page(self, index):
        widget = self.tab_widget.widget(index)
        for key, value in self.pages.items():
            if value == widget:
                del self.pages[key]
                break
        self.tab_widget.removeTab(index)

3.5创建 添加数据 功能的页面

# 添加数据标签页
    def create_add_page(self):
        # 创建一个新的 QWidget 作为页面的框架
        frame = QWidget()
        # 创建一个垂直布局,并将其添加到框架中
        layout = QVBoxLayout(frame)

        # 创建一个水平布局,用于放置输入框和标签
        input_layout = QHBoxLayout()
        # 将水平布局添加到垂直布局中
        layout.addLayout(input_layout)

        # 创建一个字典,用于存储输入框
        self.entries = {}
        # 定义每个输入框的宽度,顺序与self.subjects[1:]对应
        widths = [200, 100, 200, 200, 50, 50, 50, 50, 50]
        # 设置字体,按钮背景颜色
        font = QFont('宋体', 13, QFont.Bold)
        button_stylesheet = "background-color: skyblue;"

        # 遍历科目列表(不包括第一项 "序号"),并设置输入框宽度
        for i, subject in enumerate(self.subjects[1:]):
            # 创建一个标签,并将其文本设置为当前科目
            label = QLabel(f'{subject}:')
            label.setFont(font)
            # 将标签添加到水平布局中
            input_layout.addWidget(label)
            # 创建一个输入框
            entry = QLineEdit()
            entry.setFont(font)
            # 设置输入框宽度
            entry.setFixedWidth(widths[i])
            # 将输入框添加到水平布局中
            input_layout.addWidget(entry)
            # 将输入框添加到字典中,键为科目名称
            self.entries[subject] = entry

        # 创建一个提交按钮
        submit_button = QPushButton('提交')
        # 设置字体,按钮背景颜色
        submit_button.setFont(font)
        submit_button.setStyleSheet(button_stylesheet)
        # 将提交按钮的点击事件连接到 save_student 函数
        submit_button.clicked.connect(self.save_student)
        # 将提交按钮添加到水平布局中
        input_layout.addWidget(submit_button)

        # 创建一个水平布局,用于放置导入和导入模板下载按钮
        button_layout = QHBoxLayout()
        # 将水平布局添加到垂直布局中
        layout.addLayout(button_layout)
        # 将按钮布局左对齐
        button_layout.setAlignment(Qt.AlignLeft)
        # 创建一个导入按钮
        import_button = QPushButton('导入')
        # # 设置字体,按钮背景颜色
        import_button.setFont(font)
        import_button.setStyleSheet(button_stylesheet)
        # 将导入按钮的点击事件连接到 import_students 函数
        import_button.clicked.connect(self.import_students)
        # 将导入按钮添加到水平布局
        button_layout.addWidget(import_button)

        # 创建一个导入模板下载按钮
        template_button = QPushButton('导入模板下载')
        # # 设置字体,按钮背景颜色
        template_button.setFont(font)
        template_button.setStyleSheet(button_stylesheet)
        # 将导入模板下载按钮的点击事件连接到 template_download 函数
        template_button.clicked.connect(self.template_download)
        # 将导入模板下载按钮添加到水平布局
        button_layout.addWidget(template_button)

        # 创建一个表格,用于显示学生成绩
        self.tree = QTableWidget()
        # 设置表格的列数为科目列表的长度
        self.tree.setColumnCount(len(self.subjects))
        # 设置表格的水平表头标签为科目列表
        self.tree.setHorizontalHeaderLabels(self.subjects)
        # 隐藏垂直表头(行号)
        self.tree.verticalHeader().setVisible(False)
        # 设置表格的字体
        table_font = QFont('宋体', 12, QFont.Bold)
        self.tree.setFont(table_font)

        # 设置表格线条样式
        # 设置表格每个单元格的字体样式
        self.tree.setStyleSheet("""
            QTableWidget::item { 
                border: 1px solid #D3D3D3; /* 浅灰色边框 */
                padding: 0px; 
                margin: 0px; 
                font-family: 宋体;
                font-size: 12pt;
                font-weight: bold;
            }
            QHeaderView::section { 
                border: 1px solid #D3D3D3; /* 浅灰色边框 */ 
                font-family: 宋体;
                font-size: 12pt;
                font-weight: bold;
            }
            QTableWidget { 
                gridline-color: #D3D3D3; /* 浅灰色边框 */ 
                font-family: 宋体;
                font-size: 12pt;
                font-weight: bold;
            }
        """)
        # 将表格添加到垂直布局中
        layout.addWidget(self.tree)
        # 设置初始列宽比例
        widths = [90, 300, 150, 360, 360, 85, 90, 90, 90, 85]
        for i, width in enumerate(widths):
            # 设置每一列根据内容调整宽度
            self.tree.setColumnWidth(i, width)
        # 设置表头视图的调整模式
        header = self.tree.horizontalHeader()
        for i in range(self.tree.columnCount()):
            if i in [0, 1, 2, 3, 4, 5, 6, 7, 8]:  # 固定宽度的列
                header.setSectionResizeMode(i, QHeaderView.Fixed)
            else:  # 自适应宽度的列
                header.setSectionResizeMode(i, QHeaderView.Stretch)

        # 使窗口大小变化时,调整列宽
        self.tree.horizontalHeader().setStretchLastSection(True)
        # 返回创建的框架
        return frame

3.6创建 查询数据 的功能页面

# 查询单条数据标签页
    def create_query_page(self):
        # 创建一个新的 QWidget 作为页面的框架
        frame = QWidget()
        # 创建一个垂直布局,并将其添加到框架中
        layout = QVBoxLayout(frame)

        # 创建一个水平布局,用于放置搜索框和按钮
        search_layout = QHBoxLayout()
        # 将水平布局添加到垂直布局中
        layout.addLayout(search_layout)

        # 创建一个小部件容器,用于包含下拉框、输入框和按钮
        container = QWidget()
        container_layout = QHBoxLayout(container)

        # 创建一个下拉框,用于选择搜索选项(科目)
        self.search_option = QComboBox()
        # 添加科目列表(不包括第一项 "序号")到下拉框
        self.search_option.addItems(self.subjects[1:])
        # 设置下拉框的字体和大小
        combo_font = QFont('宋体', 16)
        self.search_option.setFont(combo_font)
        # 设置下拉框的大小
        self.search_option.setMinimumWidth(150)
        # 将下拉框添加到容器布局中
        container_layout.addWidget(self.search_option)

        # 创建一个输入框,用于输入搜索关键字
        self.entry_name_query = QLineEdit()
        # 设置输入框的字体和大小
        input_font = QFont('宋体', 16)
        self.entry_name_query.setFont(input_font)
        # 设置输入框的大小
        self.entry_name_query.setMinimumWidth(200)
        # 将输入框添加到容器布局中
        container_layout.addWidget(self.entry_name_query)

        # 设置字体,按钮背景颜色
        font = QFont('宋体', 13, QFont.Bold)
        button_stylesheet = "background-color: skyblue;"
        # 创建一个查询按钮
        query_button = QPushButton('查询')
        # 设置字体,按钮背景颜色
        query_button.setFont(font)
        query_button.setStyleSheet(button_stylesheet)
        # 将查询按钮的点击事件连接到 query_student 函数
        query_button.clicked.connect(self.query_student)
        # 将查询按钮添加到容器布局中
        container_layout.addWidget(query_button)

        # 将容器小部件添加到搜索布局中,并将其左对齐
        search_layout.addWidget(container)
        search_layout.setAlignment(container, Qt.AlignLeft)

        # 创建一个表格,用于显示查询结果
        self.query_tree = QTableWidget()
        # 设置表格的列数为科目列表的长度
        self.query_tree.setColumnCount(len(self.subjects))
        # 设置表格的水平表头标签为科目列表
        self.query_tree.setHorizontalHeaderLabels(self.subjects)
        # 隐藏垂直表头(行号)
        self.query_tree.verticalHeader().setVisible(False)
        # 设置表格的字体
        table_font = QFont('宋体', 12, QFont.Bold)
        self.query_tree.setFont(table_font)
        # 设置整行选择
        self.query_tree.setSelectionBehavior(QTableWidget.SelectRows)

        # 设置表格线条样式
        # 设置表格每个单元格的字体样式
        self.query_tree.setStyleSheet("""
                    QTableWidget::item { 
                        border: 1px solid #D3D3D3; /* 浅灰色边框 */
                        padding: 0px; 
                        margin: 0px; 
                        font-family: 宋体;
                        font-size: 12pt;
                        font-weight: bold;
                    }
                    QHeaderView::section { 
                        border: 1px solid #D3D3D3; /* 浅灰色边框 */ 
                        font-family: 宋体;
                        font-size: 12pt;
                        font-weight: bold;
                    }
                    QTableWidget { 
                        gridline-color: #D3D3D3; /* 浅灰色边框 */ 
                        font-family: 宋体;
                        font-size: 12pt;
                        font-weight: bold;
                    }
                    QTableWidget::item:selected { 
                        background-color: #87CEEB; /* 选中行的背景颜色,浅蓝色 */
                    }
                """)
        # 将表格添加到垂直布局中
        layout.addWidget(self.query_tree)

        # 获取表格的水平表头
        # header = self.query_tree.horizontalHeader()
        # 设置表格每一列的宽度
        for i, width in enumerate([90, 300, 150, 360, 360, 85, 90, 90, 90, 85]):
            # 设置每一列根据内容调整宽度
            self.query_tree.setColumnWidth(i, width)
        # 设置表头视图的调整模式
        header = self.query_tree.horizontalHeader()
        for i in range(self.query_tree.columnCount()):
            if i in [0, 1, 2, 3, 4, 5, 6, 7, 8]:  # 固定宽度的列
                header.setSectionResizeMode(i, QHeaderView.Fixed)
            else:  # 自适应宽度的列
                header.setSectionResizeMode(i, QHeaderView.Stretch)

        # 使窗口大小变化时,调整列宽
        self.query_tree.horizontalHeader().setStretchLastSection(True)

        # 创建一个修改按钮
        modify_button = QPushButton('修改')
        # 设置字体,按钮背景颜色
        modify_button.setFont(font)
        modify_button.setStyleSheet(button_stylesheet)
        # 将修改按钮添加到垂直布局中
        container_layout.addWidget(modify_button)
        modify_button.clicked.connect(self.update_student)

        # 创建一个删除按钮
        delete_button = QPushButton('删除')
        # 设置字体,按钮背景颜色
        delete_button.setFont(font)
        delete_button.setStyleSheet(button_stylesheet)
        # 将删除按钮添加到垂直布局中
        container_layout.addWidget(delete_button)
        delete_button.clicked.connect(self.delete_student)

        # 返回创建的框架
        return frame

3.7创建 查询所有数据 的功能页

# 查询所有数据标签页
    def create_list_page(self):
        # 创建一个新的 QWidget 作为页面的框架
        frame = QWidget()
        # 创建一个垂直布局,并将其添加到框架中
        layout = QVBoxLayout(frame)

        font = QFont('宋体', 16, QFont.Bold)
        button_stylesheet = "background-color: skyblue;"
        # 创建一个按钮,用于列出所有学生成绩
        list_button = QPushButton('列出所有学生成绩')
        # 设置字体,按钮背景颜色
        list_button.setFont(font)
        list_button.setStyleSheet(button_stylesheet)
        # 设置按钮的固定宽度
        list_button.setFixedWidth(200)
        # 将按钮的点击事件连接到 sh_page 函数
        list_button.clicked.connect(self.sh_page)

        # 创建一个导出按钮,用于导出表格数据为 xlsx
        export_button = QPushButton('导出数据')
        export_button.setFont(font)
        export_button.setStyleSheet(button_stylesheet)
        export_button.setFixedWidth(200)
        export_button.clicked.connect(self.export_data)

        # 创建一个水平布局来包含按钮
        h_layout = QHBoxLayout()
        h_layout.addStretch()  # 添加一个伸缩项,以便按钮居中
        h_layout.addWidget(list_button)
        h_layout.addStretch()  # 添加另一个伸缩项,以便按钮居中
        h_layout.addWidget(export_button)
        h_layout.addStretch()  # 添加另一个伸缩项,以便按钮居中

        # 将水平布局添加到垂直布局中
        layout.addLayout(h_layout)

        # 创建一个表格,用于显示学生成绩列表
        self.list_result = QTableWidget()
        # 设置表格的列数为科目列表的长度
        self.list_result.setColumnCount(len(self.subjects))
        # 设置表格的水平表头标签为科目列表
        self.list_result.setHorizontalHeaderLabels(self.subjects)
        # 隐藏垂直表头(行号)
        self.list_result.verticalHeader().setVisible(False)
        # 设置表格的字体
        table_font = QFont('宋体', 12, QFont.Bold)
        self.list_result.setFont(table_font)
        # 设置整行选择
        self.list_result.setSelectionBehavior(QTableWidget.SelectRows)

        # 设置表格线条样式
        # 设置表格每个单元格的字体样式
        self.list_result.setStyleSheet("""
                            QTableWidget::item { 
                                border: 1px solid #D3D3D3; /* 浅灰色边框 */
                                padding: 0px; 
                                margin: 0px; 
                                font-family: 宋体;
                                font-size: 12pt;
                                font-weight: bold;
                            }
                            QHeaderView::section { 
                                border: 1px solid #D3D3D3; /* 浅灰色边框 */ 
                                font-family: 宋体;
                                font-size: 12pt;
                                font-weight: bold;
                            }
                            QTableWidget { 
                                gridline-color: #D3D3D3; /* 浅灰色边框 */ 
                                font-family: 宋体;
                                font-size: 12pt;
                                font-weight: bold;
                            }
                            QTableWidget::item:selected { 
                                background-color: #87CEEB; /* 选中行的背景颜色,浅蓝色 */
                            }
                        """)
        # 将表格添加到垂直布局中
        layout.addWidget(self.list_result)

        # 设置表格每一列的宽度
        for i, width in enumerate([90, 300, 150, 360, 360, 85, 90, 90, 90, 85]):
            # 设置每一列根据内容调整宽度
            self.list_result.setColumnWidth(i, width)
        # 设置表头视图的调整模式
        header = self.list_result.horizontalHeader()
        for i in range(self.list_result.columnCount()):
            if i in [0, 1, 2, 3, 4, 5, 6, 7, 8]:  # 固定宽度的列
                header.setSectionResizeMode(i, QHeaderView.Fixed)
            else:  # 自适应宽度的列
                header.setSectionResizeMode(i, QHeaderView.Stretch)

        # 使窗口大小变化时,调整列宽
        self.list_result.horizontalHeader().setStretchLastSection(True)

        # 创建一个水平布局,用于放置分页控件
        pagination_layout = QHBoxLayout()
        # 将分页布局添加到垂直布局中
        layout.addLayout(pagination_layout)

        # 创建一个标签,用于显示当前页码
        self.page_label = QLabel('第 1 页')
        font = QFont('宋体', 14)
        self.page_label.setFont(font)
        # 将标签添加到分页布局中
        pagination_layout.addWidget(self.page_label)

        font = QFont('宋体', 16, QFont.Bold)
        button_stylesheet = "background-color: skyblue;"
        # 创建一个按钮,用于跳转到上一页
        prev_button = QPushButton('上一页')
        # 设置字体,按钮背景颜色
        prev_button.setFont(font)
        prev_button.setStyleSheet(button_stylesheet)
        # 将按钮的点击事件连接到 prev_page 函数
        prev_button.clicked.connect(self.prev_page)
        # 将按钮添加到分页布局中
        pagination_layout.addWidget(prev_button)

        # 创建一个按钮,用于跳转到下一页
        next_button = QPushButton('下一页')
        next_button.setFont(font)
        next_button.setStyleSheet(button_stylesheet)
        # 将按钮的点击事件连接到 next_page 函数
        next_button.clicked.connect(self.next_page)
        # 将按钮添加到分页布局中
        pagination_layout.addWidget(next_button)

        # 返回创建的框架
        return frame

3.8保存单条数据

# 保存单条数据
    def save_student(self):
        student_data = {subject: entry.text() for subject, entry in self.entries.items()}
        student_name = student_data['姓名']
        if student_name:
            self.students[student_name] = student_data
            filespath = os.path.abspath(os.path.dirname(__file__))
            conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
            cursor = conn.cursor()
            cursor.execute('''
            INSERT INTO achievement (Student_ID, Name, Course_Name, Major, Credit, Regular_Score, Final_Score, Overall_Score, GPA)
            VALUES (:学号, :姓名, :课程名, :专业, :学分, :平时成绩, :期末成绩, :总评成绩, :绩点)
            ''', student_data)
            conn.commit()
            cursor.close()
            conn.close()
            QMessageBox.information(self, '成功', '学生成绩已保存。')

            # 将数据添加到表格中
            new_index = self.tree.rowCount()
            self.tree.insertRow(new_index)

            # 在序号列填入自增序号
            self.tree.setItem(new_index, 0, QTableWidgetItem(str(new_index + 1)))

            # 从第一列开始填入其他数据
            for col, subject in enumerate(self.subjects):
                if subject in student_data:
                    self.tree.setItem(new_index, col, QTableWidgetItem(student_data[subject]))

            # 清空输入框
            for entry in self.entries.values():
                entry.clear()

3.9表格导入保存数据

# 表格导入保存
    def import_students(self):
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(self, '打开文件', '', 'Excel files (*.xlsx *.xls);;CSV files (*.csv)')
        if file_path:
            try:
                if file_path.endswith('.xlsx') or file_path.endswith('.xls'):
                    data = pd.read_excel(file_path)
                elif file_path.endswith('.csv'):
                    data = pd.read_csv(file_path)
                else:
                    QMessageBox.critical(self, '错误', '不支持的文件类型')
                    return

                filespath = os.path.abspath(os.path.dirname(__file__))
                conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
                cursor = conn.cursor()

                for index, row in data.iterrows():
                    student_data = {
                        "学号": row["学号"],
                        "姓名": row["姓名"],
                        "课程名": row["课程名"],
                        "专业": row["专业"],
                        "学分": row["学分"],
                        "平时成绩": row["平时成绩"],
                        "期末成绩": row["期末成绩"],
                        "总评成绩": row["总评成绩"],
                        "绩点": row["绩点"]
                    }
                    cursor.execute('''
                    INSERT INTO achievement (Student_ID, Name, Course_Name, Major, Credit, Regular_Score, Final_Score, Overall_Score, GPA)
                    VALUES (:学号, :姓名, :课程名, :专业, :学分, :平时成绩, :期末成绩, :总评成绩, :绩点)
                    ''', student_data)

                    new_index = self.tree.rowCount()
                    self.tree.insertRow(new_index)
                    for col, value in enumerate([new_index + 1] + list(student_data.values())):
                        self.tree.setItem(new_index, col, QTableWidgetItem(str(value)))

                conn.commit()
                cursor.close()
                conn.close()
                QMessageBox.information(self, '成功', '学生成绩已导入。')

            except Exception as e:
                QMessageBox.critical(self, '错误', f'导入数据时发生错误: {e}')

3.10导入模板下载

# 导入模板下载
    def template_download(self):
        try:
            filespath = os.path.abspath(os.path.dirname(__file__))
            source_path = fr'{filespath}/成绩导入模板.xlsx'  # 确保你有一个名为 成绩导入模板.xlsx 的模板文件
            default_filename = '成绩导入模板.xlsx'
            target_path, _ = QFileDialog.getSaveFileName(self, '保存文件', default_filename, 'Excel files (*.xlsx)')
            if target_path:
                shutil.copyfile(source_path, target_path)
                QMessageBox.information(self, '成功', '模板文件已下载。')
        except Exception as e:
            QMessageBox.critical(self, '错误', f'下载模板文件时发生错误: {e}')

3.11数据库查询单条数据,并显示到查询数据的页面上

# 查询数据
    def query_student(self):
        self.query_tree.setRowCount(0)  # 清空表格
        # 查询字段映射
        field_mapping = {
            '学号': 'Student_ID',
            '姓名': 'Name',
            '课程名': 'Course_Name',
            '专业': 'Major',
            '学分': 'Credit',
            '平时成绩': 'Regular_Score',
            '期末成绩': 'Final_Score',
            '总评成绩': 'Overall_Score',
            '绩点': 'GPA'
        }
        # 获取查询条件和内容
        search_option = self.search_option.currentText()
        search_value = self.entry_name_query.text()
        # print(search_option,search_value)
        # 获取数据库字段名称
        db_field = field_mapping.get(search_option)
        print(search_option, search_value, db_field)

        filespath = os.path.abspath(os.path.dirname(__file__))
        conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
        cursor = conn.cursor()
        cursor.row_factory = sqlite3.Row
        # 根据选择的条件生成SQL查询语句
        # 使用等于操作符
        query = f"SELECT * FROM achievement WHERE {db_field} = ?"
        params = (search_value,)  # 这里不需要使用 LIKE 通配符
        cursor.execute(query, params)
        results = cursor.fetchall()
        # print(results)
        cursor.close()
        conn.close()

        # 遍历查询结果,并将数据添加到表格中
        for row_index, row_data in enumerate(results, start=1):
            # print(row_index, row_data)
            new_index = self.query_tree.rowCount()
            print('new_index', new_index)
            self.query_tree.insertRow(new_index)
            # 设置自增序号
            self.query_tree.setItem(new_index, 0, QTableWidgetItem(str(row_index)))
            # 添加其他数据列
            for col, value in enumerate(row_data):
                if col == 0:  # 跳过 ID 列
                    continue
                print(col, value)
                self.query_tree.setItem(new_index, col, QTableWidgetItem(str(value)))

3.12数据库查询所有数据的函数,这部分功能需要根据窗口大小计算显示的条数,获取总条数,计算分多少页显示,实现翻页的效果

# 窗口大小改变时重新计算分页
    def resizeEvent(self, event):
        if event.oldSize().height() != -1:  # 检查是否为初始大小变化事件
            self.sh_page()
        super().resizeEvent(event)

    # 查询所有数据
    def sh_page(self):
        window_height = self.list_result.height()
        row_height = 31
        self.page_size = window_height // row_height
        filespath = os.path.abspath(os.path.dirname(__file__))
        conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM achievement")
        rows = cursor.fetchall()
        cursor.close()
        conn.close()

        self.list_result.setRowCount(0)  # 清空表格

        start_index = (self.current_page - 1) * self.page_size
        end_index = start_index + self.page_size
        page_rows = rows[start_index:end_index]

        for idx, row in enumerate(page_rows, start=start_index + 1):
            row_count = self.list_result.rowCount()
            self.list_result.insertRow(row_count)
            self.list_result.setItem(row_count, 0, QTableWidgetItem(str(idx)))
            for col, item in enumerate(row[1:], start=1):
                self.list_result.setItem(row_count, col, QTableWidgetItem(str(item)))

        self.update_pagination_label()

    # 上一页
    def prev_page(self):
        if self.current_page > 1:
            self.current_page -= 1
            self.sh_page()

    # 下一页
    def next_page(self):
        self.current_page += 1
        self.sh_page()

    # 获取总页数
    def update_pagination_label(self):
        filespath = os.path.abspath(os.path.dirname(__file__))
        conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM achievement")
        total_records = cursor.fetchone()[0]
        conn.close()

        self.total_pages = (total_records + self.page_size - 1) // self.page_size
        self.page_label.setText(f'第 {self.current_page} 页 共 {self.total_pages} 页')

3.13修改数据的函数

# 修改信息
    def update_student(self):
        selected_items = self.query_tree.selectedItems()
        if selected_items:
            selected_item = selected_items[0]
            selected_row = selected_item.row()
            values = [self.query_tree.item(selected_row, col).text() for col in range(self.query_tree.columnCount())]

            update_window = QDialog(self)
            update_window.setWindowTitle("修改学生信息")
            update_window.setModal(True)
            update_window.setFixedSize(400, 300)  # 设置窗口大小为 400x300

            update_entries = {}

            labels = ['学号', '姓名', '课程名', '专业', '学分', '平时成绩', '期末成绩', '总评成绩', '绩点']
            read_only_labels = ['学号', '姓名', '课程名']

            layout = QGridLayout(update_window)

            for i, label in enumerate(labels):
                layout.addWidget(QLabel(label, update_window), i, 0)
                if label in read_only_labels:
                    readonly_label = QLabel(values[i + 1])  # 修正索引
                    layout.addWidget(readonly_label, i, 1)
                else:
                    entry = QLineEdit(values[i + 1])  # 修正索引
                    layout.addWidget(entry, i, 1)
                    update_entries[label] = entry

            def save_changes():
                updated_values = []
                for i, label in enumerate(labels):
                    if label in read_only_labels:
                        updated_values.append(values[i + 1])  # 修正索引
                    else:
                        updated_values.append(update_entries[label].text())

                for col, value in enumerate(updated_values):
                    self.query_tree.setItem(selected_row, col + 1, QTableWidgetItem(value))  # 修正索引

                # 更新数据库
                student_id = values[1]
                student_name = values[2]
                course_name = values[3]

                update_data = {
                    '专业': update_entries['专业'].text(),
                    '学分': update_entries['学分'].text(),
                    '平时成绩': update_entries['平时成绩'].text(),
                    '期末成绩': update_entries['期末成绩'].text(),
                    '总评成绩': update_entries['总评成绩'].text(),
                    '绩点': update_entries['绩点'].text()
                }

                filespath = os.path.abspath(os.path.dirname(__file__))
                conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
                cursor = conn.cursor()

                cursor.execute('''
                    UPDATE achievement
                    SET Major = :专业, Credit = :学分, Regular_Score = :平时成绩, Final_Score = :期末成绩, Overall_Score = :总评成绩, GPA = :绩点
                    WHERE Student_ID = ? AND Name = ? AND Course_Name = ?
                ''', (*update_data.values(), student_id, student_name, course_name))

                conn.commit()
                cursor.close()
                conn.close()

                QMessageBox.information(self, "成功", "学生信息已更新。")
                update_window.accept()

            save_button = QPushButton("保存", update_window)
            save_button.clicked.connect(save_changes)
            layout.addWidget(save_button, len(labels), 1)

            update_window.exec_()

3.14删除数据

# 删除数据
    def delete_student(self):
        selected_items = self.query_tree.selectedItems()
        if selected_items:
            selected_item = selected_items[0]
            selected_row = selected_item.row()

            # 弹出确认对话框
            confirm = QMessageBox.question(self, "确认删除", "确定要删除选中的学生信息吗?", QMessageBox.Ok | QMessageBox.Cancel)
            if confirm == QMessageBox.Ok:
                # 获取选中行的数据
                item_data = [self.query_tree.item(selected_row, col).text() for col in
                             range(self.query_tree.columnCount())]
                print(item_data)

                # 从数据库删除记录
                filespath = os.path.abspath(os.path.dirname(__file__))
                conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
                cursor = conn.cursor()
                field_mapping = {
                    '学号': 'Student_ID',
                    '姓名': 'Name',
                    '课程名': 'Course_Name',
                    '专业': 'Major',
                    '学分': 'Credit',
                    '平时成绩': 'Regular_Score',
                    '期末成绩': 'Final_Score',
                    '总评成绩': 'Overall_Score',
                    '绩点': 'GPA'
                }

                placeholders = " AND ".join([f"{field_mapping[key]}=?" for key in field_mapping.keys()])
                query = f"DELETE FROM achievement WHERE {placeholders}"

                cursor.execute(query, item_data[1:])  # 跳过序号列
                conn.commit()
                cursor.close()
                conn.close()

                # 从表格中删除选中的行
                self.query_tree.removeRow(selected_row)
                QMessageBox.information(self, "成功", "学生信息已删除。")

3.15导出所有数据

# 导出所有数据
    def export_data(self):
        # 检查当前显示的表格中是否有数据
        if self.list_result.rowCount() == 0:
            QMessageBox.warning(self, "导出失败", "表格中没有数据,无法导出。")
            return

        # 查询数据库中的所有数据
        filespath = os.path.abspath(os.path.dirname(__file__))
        conn = sqlite3.connect(fr'{filespath}/StudentAchievements.db')
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM achievement")
        rows = cursor.fetchall()
        cursor.close()
        conn.close()

        if not rows:
            QMessageBox.warning(self, "导出失败", "数据库中没有数据,无法导出。")
            return

        # 获取表头,去掉序号列
        headers = [self.list_result.horizontalHeaderItem(i).text() for i in range(1, self.list_result.columnCount())]

        # 获取所有数据,去掉ID列
        data = []
        for row in rows:
            data.append(row[1:])  # 跳过第一个元素(ID列)

        # 转换为 DataFrame 并导出
        df = pd.DataFrame(data, columns=headers)

        options = QFileDialog.Options()
        default_file_name = "学生成绩表.xlsx"
        file_path, _ = QFileDialog.getSaveFileName(self, "保存文件", default_file_name,
                                                   "Excel Files (*.xlsx);;All Files (*)", options=options)
        if file_path:
            df.to_excel(file_path, index=False)
            QMessageBox.information(self, "导出成功", f"数据已成功导出到 {file_path}")

3.16最后是run的代码

if __name__ == "__main__":
    app = QApplication([])
    main_app = MainApp()
    main_app.show()
    app.exec_()

四.各页面的展示

4.1添加学生成绩

4.2查询成绩

4.3修改

4.4删除

4.5列出所有成绩

至此,结束。

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

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyQt5是一个Python编程语言的工具包,用于创建图形用户界面(GUI)。SQLite是一个轻量级的嵌入式数据库引擎,它可以轻松地集成到应用程序中。QTableWidget是PyQt5中一个用于显示和编辑二维表格数据的小部件。 在PyQt5中,使用QTableWidget来显示SQLite数据库中的数据,可以按照以下步骤进行操作: 1. 首先,导入需要的模块:`from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem` 并连接到SQLite数据库:`import sqlite3` 2. 创建一个继承自QMainWindow的自定义窗口类,并在窗口的初始化方法中设置布局等属性。 3. 创建一个QTableWidget实例,并设置它的行数和列数:`table = QTableWidget(row_count, col_count)` 4. 连接到SQLite数据库:`conn = sqlite3.connect('your_database.db')` 5. 创建一个游标对象:`cursor = conn.cursor()` 6. 执行SQL查询语句获取数据库中的数据:`cursor.execute('SELECT * FROM your_table')` 7. 使用fetchall()方法获取查询结果:`data = cursor.fetchall()` 8. 遍历查询结果,并将数据逐行添加到QTableWidget中:`for row_number, row_data in enumerate(data):` `for column_number, column_data in enumerate(row_data):` `table.setItem(row_number, column_number, QTableWidgetItem(str(column_data)))` 9. 最后,关闭数据库连接:`conn.close()` 10. 将QTableWidget添加到窗口的主布局中:`self.setCentralWidget(table)` 通过以上步骤,就可以使用PyQt5SQLite数据库来显示自定义表格数据。在需要时,可以根据需求对QTableWidget进行自定义样式的设置,例如设置列宽、行高、表头等。 希望以上回答对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值