PyQt+Excel学生信息管理系统,增删改查全开源

目录

一、引言

二、GUI界面设计

1.上传学生信息

2.增加学生信息

3.删除学生信息

4.修改学生信息

5.查找学生信息

6.实时更新excel表

7.界面设计jiemian.py

三、主要程序详解

1.导入所需模块

2.初始化设置

3.增

4.删

5.改

6.查

7.表格实时更新显示

8.自定义函数

四、总程序代码


一、引言

在现实生活中,有些教育管理仍困于纸质表格与手动录入的低效循环,因此,想做一款融合Python逻辑力量、PyQt优雅界面与Excel轻量存储的本地化系统 ------它既不依赖云端,又不索取权限,只需一个Excel文件作为数据库 ,便可让学籍数据的增、删、改、查从繁琐的手工操作,蜕变为指尖轻点的流畅体验。无需数据库服务器,无需复杂部署,只用最熟悉的Excel承载最核心的数据,用最专业的PyQt重构最直观的交互,让(教育)信息管理者(当然其他行业也可以,只需微调代码)轻松驾驭一套真正属于自己的学生信息管理工具。

二、GUI界面设计

使用PyQt5进行界面的搭建,最终的增、删、改、查界面如下:

信息管理者可在"增加"页面新增学生信息;在"删除"页面将身份证号码作为主键,删除该学生的全部信息;在"修改"页面根据身份证号码匹配已在数据库中的学生,并修改其任意字段所对应的信息;在"查找"页面以身份证号码查找学生是否存在。
当然,上述操作界面下方的表格也会根据用户的操作(增、删、改、查),实时更新excel数据库并显示最新的学生信息。

1.上传学生信息

在程序运行的初始阶段,除对用户表示欢迎之外,更为重要的是需要询问信息管理者是否具有"学生信息表";如果用户选择"Yes",则弹出"请选择文件"界面,选择相应且满足格式的文件并上传后,会弹出"上传成功"的提示;如果用户选择"No",则会自动下载Excel文件"学生信息表模板"至当前目录下。

下载的Excel文件"学生信息模板"如下,按列录入学生信息再保存上传即可。

这里需要注意: 当填写完上述的空模板文件后,无论是选择"OK",还是直接关闭该对话框,均会提示"学生信息表是否填写完毕"。此时若选择"Yes",则会跳转到上传文件的界面,反之则会一直提示"学生信息表是否填写完毕",直至上传的文件没有问题之后,才会进入到主程序界面并即刻显示excel文件中的数据。

2.增加学生信息

依次填写"年级"、"班级"、"学号"、"身份证号"、"姓名"信息后,点击"添加",新增信息默认添加至表格的末尾。需要注意的是,这五项信息缺一不可,均为必填字段 ,若有一项是空白,则会弹出提示"请填写全部信息!",直至满足条件后,才会添加成功(这里并未对每个输入框信息格式做特别要求,均按照字符串进行处理,若有需求,可自行添加正则表达式规定特定输入格式)。此外,若添加的学生在表格中已经存在(根据身份证号码进行判断),则会弹出"该生已存在,无法新增"的提示。

3.删除学生信息

删除指定学生信息,仅需要该生的身份证号码即可。点击"删除"后,自行将数据库中该生的全部信息删除,若下方还有学生,则依次向上递补,同时显示"删除成功"。若该生不存在,点击"删除"后,则无法删除。

4.修改学生信息

首先,同样通过输入身份证号码来判断学生是否存在,若不存在,提示"无法修改";若存在,则需要通过下拉框来选择被修改字段名称(修改值不能为空,否则出现"填写全部信息"的提示),最后单击"修改",显示"修改成功",并实时更新在下方表格中。但每点击一次"修改",只能修改一个字段中的一个信息。

5.查找学生信息

首先,同样通过输入身份证号码来判断学生是否存在,若不存在,提示"不存在该生";若存在,则直接在下方表格中用蓝色定位到该生的整行所有信息。

6.实时更新excel表

以上增、删、改、查操作,不仅仅会实时更新在界面下方的表格中,还会实时同步更新到用户所上传的excel模板文件中

7.界面设计jiemian.py

最后附上通过pyuic5产生的GUI界面代码jiemian.py

python 复制代码
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'jiemian.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.setEnabled(True)
        Form.resize(600, 580)
        Form.setMinimumSize(QtCore.QSize(600, 580))
        Form.setMaximumSize(QtCore.QSize(600, 580))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/image1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        Form.setWindowIcon(icon)
        self.label_5 = QtWidgets.QLabel(Form)
        self.label_5.setGeometry(QtCore.QRect(370, 40, 141, 31))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(16)
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.label_8 = QtWidgets.QLabel(Form)
        self.label_8.setGeometry(QtCore.QRect(190, 10, 211, 31))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(18)
        font.setBold(False)
        font.setWeight(50)
        self.label_8.setFont(font)
        self.label_8.setObjectName("label_8")
        self.label_10 = QtWidgets.QLabel(Form)
        self.label_10.setGeometry(QtCore.QRect(450, 30, 71, 51))
        self.label_10.setText("")
        self.label_10.setPixmap(QtGui.QPixmap(":/image1.png"))
        self.label_10.setObjectName("label_10")
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setGeometry(QtCore.QRect(30, 80, 541, 171))
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.label = QtWidgets.QLabel(self.tab)
        self.label.setGeometry(QtCore.QRect(30, 20, 31, 21))
        self.label.setObjectName("label")
        self.lineEdit = QtWidgets.QLineEdit(self.tab)
        self.lineEdit.setGeometry(QtCore.QRect(60, 20, 161, 20))
        self.lineEdit.setObjectName("lineEdit")
        self.label_2 = QtWidgets.QLabel(self.tab)
        self.label_2.setGeometry(QtCore.QRect(30, 60, 31, 21))
        self.label_2.setObjectName("label_2")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.tab)
        self.lineEdit_2.setGeometry(QtCore.QRect(60, 60, 161, 20))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label_3 = QtWidgets.QLabel(self.tab)
        self.label_3.setGeometry(QtCore.QRect(290, 20, 41, 21))
        self.label_3.setObjectName("label_3")
        self.lineEdit_3 = QtWidgets.QLineEdit(self.tab)
        self.lineEdit_3.setGeometry(QtCore.QRect(350, 20, 161, 20))
        self.lineEdit_3.setText("")
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.lineEdit_4 = QtWidgets.QLineEdit(self.tab)
        self.lineEdit_4.setGeometry(QtCore.QRect(350, 60, 161, 20))
        self.lineEdit_4.setObjectName("lineEdit_4")
        self.label_4 = QtWidgets.QLabel(self.tab)
        self.label_4.setGeometry(QtCore.QRect(290, 60, 81, 21))
        self.label_4.setObjectName("label_4")
        self.label_6 = QtWidgets.QLabel(self.tab)
        self.label_6.setGeometry(QtCore.QRect(30, 100, 31, 21))
        self.label_6.setObjectName("label_6")
        self.lineEdit_5 = QtWidgets.QLineEdit(self.tab)
        self.lineEdit_5.setGeometry(QtCore.QRect(60, 100, 161, 20))
        self.lineEdit_5.setObjectName("lineEdit_5")
        self.pushButton = QtWidgets.QPushButton(self.tab)
        self.pushButton.setGeometry(QtCore.QRect(350, 100, 161, 23))
        self.pushButton.setObjectName("pushButton")
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.label_7 = QtWidgets.QLabel(self.tab_2)
        self.label_7.setGeometry(QtCore.QRect(130, 40, 111, 21))
        self.label_7.setObjectName("label_7")
        self.lineEdit_6 = QtWidgets.QLineEdit(self.tab_2)
        self.lineEdit_6.setGeometry(QtCore.QRect(250, 40, 161, 20))
        self.lineEdit_6.setObjectName("lineEdit_6")
        self.pushButton_2 = QtWidgets.QPushButton(self.tab_2)
        self.pushButton_2.setGeometry(QtCore.QRect(250, 80, 161, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        self.tabWidget.addTab(self.tab_2, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.lineEdit_7 = QtWidgets.QLineEdit(self.tab_3)
        self.lineEdit_7.setGeometry(QtCore.QRect(250, 20, 161, 20))
        self.lineEdit_7.setObjectName("lineEdit_7")
        self.label_9 = QtWidgets.QLabel(self.tab_3)
        self.label_9.setGeometry(QtCore.QRect(130, 20, 111, 21))
        self.label_9.setObjectName("label_9")
        self.label_11 = QtWidgets.QLabel(self.tab_3)
        self.label_11.setGeometry(QtCore.QRect(130, 50, 111, 21))
        self.label_11.setObjectName("label_11")
        self.comboBox = QtWidgets.QComboBox(self.tab_3)
        self.comboBox.setGeometry(QtCore.QRect(250, 50, 161, 22))
        self.comboBox.setObjectName("comboBox")
        self.label_12 = QtWidgets.QLabel(self.tab_3)
        self.label_12.setGeometry(QtCore.QRect(130, 80, 111, 21))
        self.label_12.setObjectName("label_12")
        self.lineEdit_8 = QtWidgets.QLineEdit(self.tab_3)
        self.lineEdit_8.setGeometry(QtCore.QRect(250, 80, 161, 20))
        self.lineEdit_8.setObjectName("lineEdit_8")
        self.pushButton_3 = QtWidgets.QPushButton(self.tab_3)
        self.pushButton_3.setGeometry(QtCore.QRect(250, 110, 161, 23))
        self.pushButton_3.setObjectName("pushButton_3")
        self.tabWidget.addTab(self.tab_3, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.pushButton_4 = QtWidgets.QPushButton(self.tab_4)
        self.pushButton_4.setGeometry(QtCore.QRect(250, 80, 161, 23))
        self.pushButton_4.setObjectName("pushButton_4")
        self.lineEdit_9 = QtWidgets.QLineEdit(self.tab_4)
        self.lineEdit_9.setGeometry(QtCore.QRect(250, 40, 161, 20))
        self.lineEdit_9.setObjectName("lineEdit_9")
        self.label_13 = QtWidgets.QLabel(self.tab_4)
        self.label_13.setGeometry(QtCore.QRect(130, 40, 111, 21))
        self.label_13.setObjectName("label_13")
        self.tabWidget.addTab(self.tab_4, "")

        self.retranslateUi(Form)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Student_Info_Management"))
        self.label_5.setText(_translate("Form", "Designed By"))
        self.label_8.setText(_translate("Form", "学生信息管理系统"))
        self.label.setText(_translate("Form", "年级"))
        self.label_2.setText(_translate("Form", "学号"))
        self.label_3.setText(_translate("Form", "班  级"))
        self.label_4.setText(_translate("Form", "身份证号"))
        self.label_6.setText(_translate("Form", "姓名"))
        self.pushButton.setText(_translate("Form", "添加"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "增加"))
        self.label_7.setText(_translate("Form", "请输入身份证号码:"))
        self.pushButton_2.setText(_translate("Form", "删除"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "删除"))
        self.label_9.setText(_translate("Form", "请输入身份证号码:"))
        self.label_11.setText(_translate("Form", "请选择修改字段:"))
        self.label_12.setText(_translate("Form", "修  改  为:"))
        self.pushButton_3.setText(_translate("Form", "修改"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "修改"))
        self.pushButton_4.setText(_translate("Form", "查找"))
        self.label_13.setText(_translate("Form", "请输入身份证号码:"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "查找"))
import ziyuan_rc

三、主要程序详解

1.导入所需模块

python 复制代码
import sys,os
from jiemian import *
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHeaderView
import pandas as pd
from openpyxl import Workbook
from PyQt5.QtGui import QStandardItemModel, QStandardItem
# 保持窗口大小和qtdesigner中的一致
from PyQt5 import QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)

QTableView与表格相关;QHeaderView用于控制QTableView的列宽和行高等参数;QStandardItemModel和QStandardItem用于在tableview中添加(更新)数据,并设置模型到视图。不懂为啥需要导入Qtcore的,请看****************************一键曝光:Python+PyQt实现的文件目录透视镜****************************

2.初始化设置

python 复制代码
    def __init__(self):
        super(QWidget, self).__init__()
        self.setupUi(self)

        self.pushButton.clicked.connect(self.add_stu)
        self.pushButton_2.clicked.connect(self.delete_stu)
        self.pushButton_3.clicked.connect(self.modify_stu)
        self.pushButton_4.clicked.connect(self.find_stu)

        self.comboBox.addItems(['年级', '班级', '学号', '身份证号码', '姓名']) # 提前将用户修改字段内容写入

        self.shangchuan_flag = False
        # 判断是否上传
        while(self.shangchuan_flag==False):
            shangchuan_judge = QtWidgets.QMessageBox.question(None, "欢迎使用", "是否具有学生信息表?",
                                                   QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
            if shangchuan_judge==QtWidgets.QMessageBox.Yes:
                # 如果具有学生信息表,则直接上传
                self.excel_data=shangchuan()
                self.shangchuan_flag = True
            else:
                # 否则调用xiazai,下载模板后填写完毕,再重新上传
                xiazai()
                self.tianxie_flag = False
                while(self.tianxie_flag==False):
                    tianxie_judge = QtWidgets.QMessageBox.question(None, "提示", "学生信息表是否填写完毕?",
                                                                  QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
                    if tianxie_judge==QtWidgets.QMessageBox.Yes:
                        self.tianxie_flag=True
                        self.excel_data=shangchuan()
                        self.shangchuan_flag = True

        # 上传的数据存储在excel_data中,供后续使用
        # print(self.excel_data)

        self.table_view = QTableView(self)
        self.table_view.setGeometry(30, 280, 540, 270) # setGeometry(x, y, width, height)
        self.table_view.setEditTriggers(QTableView.NoEditTriggers) # 禁止编辑单元格
        self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整列宽
        self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整行高
        self.table_view.setAlternatingRowColors(True) # 设置交替行显示
        self.model = QStandardItemModel(len(self.excel_data), 5) # 创建行列
        self.model.setHorizontalHeaderLabels(['年级', '班级', '学号', '身份证号码', '姓名']) # 设置每列的标题

        self.xianshi()

将四个pushbutton分别绑定于add_stu、delete_stu、modify_stu、find_stu,并提前在"请选择修改字段"combobox中写入被修改字段名称;设置标志位shangchuan_flag,通过自定义函数shangchuan以判断用户是否上传学生信息表,且上传的学生信息表是否符合格式内容要求;若用户没有学生信息表模板,则调用自定义函数xiazai,待填写完成后在弹出的对话框中选择文件并上传即可。
完成以上程序后,获取用户上传的学生信息表内容并存储在excel_data中,供后续使用;接下来需要初始化tableview的相关参数:显示位置、大小(宽度和高度)、编辑单元格权限、手动调整行高列宽权限、交替行醒目区分显示 、行数、列数以及列名;最后通过xianshi实时显示更新。

3.增

python 复制代码
    def add_stu(self):
        # 判断新增信息是否写全
        if (self.lineEdit.text()!="") and (self.lineEdit_2.text()!="") and (self.lineEdit_3.text()!="") and (self.lineEdit_4.text()!="") and (self.lineEdit_5.text()!=""):
            if chazhao(self.lineEdit_4.text(), self.excel_data):
                # 已存在的学生不能再次新增
                QtWidgets.QMessageBox.critical(self, "新增", "该生已存在,无法新增!")
            else:
                # 通过len获取末尾行索引,通过loc直接按序写入整行数据
                self.excel_data.loc[len(self.excel_data)]={'年级':self.lineEdit.text(), '学号':self.lineEdit_2.text(), '班级':self.lineEdit_3.text(),
                                        '身份证号码':self.lineEdit_4.text(), '姓名':self.lineEdit_5.text()}
                # 及时通过xieru函数更新excel表内容
                xieru(self.excel_data)
                self.xianshi()
                QtWidgets.QMessageBox.information(self, "新增", "新增成功!")
        else:
            QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")

        # print(self.excel_data)

根据"与"判断5个输入框的内容是否为空,再根据自定义函数chazhao判断该生是否已经存在(依据lineedit_4所输的身份证号码),满足条件后通过len获取excel_data末尾索引,并通过loc按序写入整行数据。最后,通过自定义函数xieru及时更新excel文件中的数据,通过xianshi来更新tableview,并提示"新增成功"。

4.删

python 复制代码
    def delete_stu(self):
        if chazhao(self.lineEdit_6.text(), self.excel_data):
            # 通过身份证号判断学生是否在表中,不在表中的无法删除
            for i in range(len(self.excel_data)):
                # 通过遍历身份证号码获取行索引
                if str(self.excel_data['身份证号码'][i]) == self.lineEdit_6.text():
                    index = i
                    break
            self.excel_data = self.excel_data.drop(index) # 通过行索引删除整行数据
            self.excel_data = self.excel_data.reset_index(drop=True)  # 被删除数据位置不定,故删除后需要重新排序(更新行索引)

            xieru(self.excel_data)
            self.model.removeRows(0, self.model.rowCount())
            # 为及时更新数据,在删后需将tableview清空,再重新写入
            # 其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行处理
            self.xianshi()

            QtWidgets.QMessageBox.information(self, "删除", "删除成功!")
        else:
            QtWidgets.QMessageBox.critical(self, "删除", "该生不存在,无法删除!")

        # print(self.excel_data)

通过chazhao函数判断是否存在,只有存在的学生才能删除,否则提示"无法删除"。接下来通过遍历并匹配身份证号来获取行索引,一旦找到就break循环。退出循环后,通过刚才在循环中获取到的行索引index来删除drop整行数据。

注意:

1.被删除数据所处位置不定,所以在删除后需要重新更新所有行索引。

2.删除后也需要通过xieru来更新excel文件。

3.为及时更新数据,在删后需将tableview清空,再重新写入。其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行单独处理。

4.最后将最新数据xianshi在tableview中。

5.改

python 复制代码
    def modify_stu(self):
        if chazhao(self.lineEdit_7.text(), self.excel_data):
            # 通过身份证号判断学生是否在表中,不在表中的无法修改
            col_index = self.comboBox.currentIndex() # 列索引即combobox中items的索引,两者一致
            modify_content = self.lineEdit_8.text()
            if modify_content != "":
                for i in range(len(self.excel_data)):
                    if str(self.excel_data['身份证号码'][i]) == self.lineEdit_7.text():
                        row_index = i
                        break
                self.excel_data.iloc[row_index, col_index] = modify_content
                xieru(self.excel_data)
                self.xianshi()
                QtWidgets.QMessageBox.information(self, "修改", "修改成功!")
            else:
                QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
        else:
            QtWidgets.QMessageBox.critical(self, "修改", "该生不存在,无法修改!")

        # print(self.excel_data)

通过身份证号在chazhao函数中判断学生是否在表中,不在表中的无法修改;获取用户选择的combobox字段索引(也即excel_data中的列索引),再加上通过遍历得到的行索引将用户修改内容modify_content写入对应位置。最后xieru和xianshi。

6.查

python 复制代码
    def find_stu(self):
        try:
            row_index = [str(i) for i in self.excel_data['身份证号码']].index(self.lineEdit_9.text())
            # 根据身份证直接定位该学生所有信息
            self.table_view.selectRow(row_index)
            QtWidgets.QMessageBox.information(self, "查找", "该生存在!")

        except ValueError:
            QtWidgets.QMessageBox.critical(self, "查找", "不存在该生!")

"查"比较简单,未使用chazhao,而是直接根据列表推导式获取对应的行索引row_index,但需要注意要想实现数据定位selectRow到行,须将参数类型改为str整体用异常处理解决是否存在问题若存在则row_index中有相应具体值,否则会提示ValueError,这是一个很好的思路。

7.表格实时更新显示

python 复制代码
    def xianshi(self):
        # 给tableview中添加/更新数据
        # self.model.removeRows(0, self.model.rowCount()) # 为及时更新数据,在增、删、改后需将tableview清空
        for i in range(0, len(self.excel_data)):
            for j in range(0, 5):
                self.model.setItem(i, j, QStandardItem(str(self.excel_data.iloc[i, j])))  # 为保证数据显示完全,将所有数据类型转换为str

        # 设置模型到视图
        self.table_view.setModel(self.model)

        self.table_view.setColumnWidth(0,72) # 在 setModel() 之后再调用 setColumnWidth(),列宽设置才能生效
        self.table_view.setColumnWidth(1,72) # 单位为像素
        self.table_view.setColumnWidth(2,72)
        self.table_view.setColumnWidth(3,189)
        self.table_view.setColumnWidth(4,76)

实时更新并写入excel_data中的数据;在将模型映射到视图后,才能设置每行的列宽参数。

8.自定义函数

python 复制代码
def shangchuan():
    filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "请选择文件", "", "XLSX工作表 (*.xlsx)")  # 获取文件路径
    if filepath:
        # 获取原始df
        yuanshi_df = pd.read_excel(filepath)
        if not yuanshi_df.empty:
            QtWidgets.QMessageBox.information(None, "成功", "上传成功!")
            return yuanshi_df
        else:
            QtWidgets.QMessageBox.critical(None, "提示", "请检查操作步骤或上传非空文件!")
            shangchuan() # 直到上传非空文件
    else:
        QtWidgets.QMessageBox.critical(None, "提示", "请选择XLSX工作表类型!")
        shangchuan() # 直到成功选择类型并上传

通过递归使用户一直上传,除非上传了工作表类型,且文件非空。

python 复制代码
def xiazai():
    wb = Workbook()
    ws = wb.active
    # 写入列名
    ws['A1'] = '年级'
    ws['B1'] = '班级'
    ws['C1'] = '学号'
    ws['D1'] = '身份证号码'
    ws['E1'] = '姓名'

    # 设置列宽(身份证和姓名稍宽)
    ws.column_dimensions['D'].width = 30
    ws.column_dimensions['E'].width = 20

    wb.save('全校学生信息.xlsx')
    QtWidgets.QMessageBox.information(None, "成功", "学生信息表模板下载成功,请查看当前目录!")

定义"全校学生信息"模板文件的列名及列宽,并保存至当前目录下。

python 复制代码
def xieru(df):
    df.to_excel('全校学生信息.xlsx', index=False)

将参数df中的数据写入到excel文件中,起到实时更新excel文件的作用。

python 复制代码
def chazhao(xiang, zong):
    # 无论是数据库的哪一项基础操作(增、删、改、查),"查"就不调用了,因为要定位,还需要返回索引,这里就简单判断,返回索引放在class中了
    # 第一步均需进行查找,故将其写为函数,供随时调用
    if str(xiang) in [str(i) for i in list(zong['身份证号码'])]:  # 使用列表推导式将所有类型统一为str
        return True
    else:
        return False

使用列表推导式直接判断是否存在,若存在,返回布尔类型True,否则返回False。

四、总程序代码

python 复制代码
import sys,os
from jiemian import *
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHeaderView
import pandas as pd
from openpyxl import Workbook
from PyQt5.QtGui import QStandardItemModel, QStandardItem
# 保持窗口大小和qtdesigner中的一致
from PyQt5 import QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)

class login_interface(QWidget, Ui_Form):
    def __init__(self):
        super(QWidget, self).__init__()
        self.setupUi(self)

        self.pushButton.clicked.connect(self.add_stu)
        self.pushButton_2.clicked.connect(self.delete_stu)
        self.pushButton_3.clicked.connect(self.modify_stu)
        self.pushButton_4.clicked.connect(self.find_stu)

        self.comboBox.addItems(['年级', '班级', '学号', '身份证号码', '姓名']) # 提前将用户修改字段内容写入

        self.shangchuan_flag = False
        # 判断是否上传
        while(self.shangchuan_flag==False):
            shangchuan_judge = QtWidgets.QMessageBox.question(None, "欢迎使用", "是否具有学生信息表?",
                                                   QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
            if shangchuan_judge==QtWidgets.QMessageBox.Yes:
                # 如果具有学生信息表,则直接上传
                self.excel_data=shangchuan()
                self.shangchuan_flag = True
            else:
                # 否则调用xiazai,下载模板后填写完毕,再重新上传
                xiazai()
                self.tianxie_flag = False
                while(self.tianxie_flag==False):
                    tianxie_judge = QtWidgets.QMessageBox.question(None, "提示", "学生信息表是否填写完毕?",
                                                                  QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
                    if tianxie_judge==QtWidgets.QMessageBox.Yes:
                        self.tianxie_flag=True
                        self.excel_data=shangchuan()
                        self.shangchuan_flag = True

        # 上传的数据存储在excel_data中,供后续使用
        # print(self.excel_data)

        self.table_view = QTableView(self)
        self.table_view.setGeometry(30, 280, 540, 270) # setGeometry(x, y, width, height)
        self.table_view.setEditTriggers(QTableView.NoEditTriggers) # 禁止编辑单元格
        self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整列宽
        self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) # 禁止用户手动调整行高
        self.table_view.setAlternatingRowColors(True) # 设置交替行显示
        self.model = QStandardItemModel(len(self.excel_data), 5) # 创建行列
        self.model.setHorizontalHeaderLabels(['年级', '班级', '学号', '身份证号码', '姓名']) # 设置每列的标题

        self.xianshi()

    def add_stu(self):
        # 判断新增信息是否写全
        if (self.lineEdit.text()!="") and (self.lineEdit_2.text()!="") and (self.lineEdit_3.text()!="") and (self.lineEdit_4.text()!="") and (self.lineEdit_5.text()!=""):
            if chazhao(self.lineEdit_4.text(), self.excel_data):
                # 已存在的学生不能再次新增
                QtWidgets.QMessageBox.critical(self, "新增", "该生已存在,无法新增!")
            else:
                # 通过len获取末尾行索引,通过loc直接按序写入整行数据
                self.excel_data.loc[len(self.excel_data)]={'年级':self.lineEdit.text(), '学号':self.lineEdit_2.text(), '班级':self.lineEdit_3.text(),
                                        '身份证号码':self.lineEdit_4.text(), '姓名':self.lineEdit_5.text()}
                # 及时通过xieru函数更新excel表内容
                xieru(self.excel_data)
                self.xianshi()
                QtWidgets.QMessageBox.information(self, "新增", "新增成功!")
        else:
            QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")

        # print(self.excel_data)

    def delete_stu(self):
        if chazhao(self.lineEdit_6.text(), self.excel_data):
            # 通过身份证号判断学生是否在表中,不在表中的无法删除
            for i in range(len(self.excel_data)):
                # 通过遍历身份证号码获取行索引
                if str(self.excel_data['身份证号码'][i]) == self.lineEdit_6.text():
                    index = i
                    break
            self.excel_data = self.excel_data.drop(index) # 通过行索引删除整行数据
            self.excel_data = self.excel_data.reset_index(drop=True)  # 被删除数据位置不定,故删除后需要重新排序(更新行索引)

            xieru(self.excel_data)
            self.model.removeRows(0, self.model.rowCount())
            # 为及时更新数据,在删后需将tableview清空,再重新写入
            # 其实也应该在增和改中进行同样操作,但增和改没报错,只有删的数据有问题,所以只在这里进行处理
            self.xianshi()

            QtWidgets.QMessageBox.information(self, "删除", "删除成功!")
        else:
            QtWidgets.QMessageBox.critical(self, "删除", "该生不存在,无法删除!")

        # print(self.excel_data)

    def modify_stu(self):
        if chazhao(self.lineEdit_7.text(), self.excel_data):
            # 通过身份证号判断学生是否在表中,不在表中的无法修改
            col_index = self.comboBox.currentIndex() # 列索引即combobox中items的索引,两者一致
            modify_content = self.lineEdit_8.text()
            if modify_content != "":
                for i in range(len(self.excel_data)):
                    if str(self.excel_data['身份证号码'][i]) == self.lineEdit_7.text():
                        row_index = i
                        break
                self.excel_data.iloc[row_index, col_index] = modify_content
                xieru(self.excel_data)
                self.xianshi()
                QtWidgets.QMessageBox.information(self, "修改", "修改成功!")
            else:
                QtWidgets.QMessageBox.critical(self, "提示", "请填写全部信息!")
        else:
            QtWidgets.QMessageBox.critical(self, "修改", "该生不存在,无法修改!")

        # print(self.excel_data)

    def find_stu(self):
        try:
            row_index = [str(i) for i in self.excel_data['身份证号码']].index(self.lineEdit_9.text())
            # 根据身份证直接定位该学生所有信息
            self.table_view.selectRow(row_index)
            QtWidgets.QMessageBox.information(self, "查找", "该生存在!")

        except ValueError:
            QtWidgets.QMessageBox.critical(self, "查找", "不存在该生!")

    def xianshi(self):
        # 给tableview中添加/更新数据
        # self.model.removeRows(0, self.model.rowCount()) # 为及时更新数据,在增、删、改后需将tableview清空
        for i in range(0, len(self.excel_data)):
            for j in range(0, 5):
                self.model.setItem(i, j, QStandardItem(str(self.excel_data.iloc[i, j])))  # 为保证数据显示完全,将所有数据类型转换为str

        # 设置模型到视图
        self.table_view.setModel(self.model)

        self.table_view.setColumnWidth(0,72) # 在 setModel() 之后再调用 setColumnWidth(),列宽设置才能生效
        self.table_view.setColumnWidth(1,72) # 单位为像素
        self.table_view.setColumnWidth(2,72)
        self.table_view.setColumnWidth(3,189)
        self.table_view.setColumnWidth(4,76)

def shangchuan():
    filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "请选择文件", "", "XLSX工作表 (*.xlsx)")  # 获取文件路径
    if filepath:
        # 获取原始df
        yuanshi_df = pd.read_excel(filepath)
        if not yuanshi_df.empty:
            QtWidgets.QMessageBox.information(None, "成功", "上传成功!")
            return yuanshi_df
        else:
            QtWidgets.QMessageBox.critical(None, "提示", "请检查操作步骤或上传非空文件!")
            shangchuan() # 直到上传非空文件
    else:
        QtWidgets.QMessageBox.critical(None, "提示", "请选择XLSX工作表类型!")
        shangchuan() # 直到成功选择类型并上传

def xiazai():
    wb = Workbook()
    ws = wb.active
    # 写入列名
    ws['A1'] = '年级'
    ws['B1'] = '班级'
    ws['C1'] = '学号'
    ws['D1'] = '身份证号码'
    ws['E1'] = '姓名'

    # 设置列宽(身份证和姓名稍宽)
    ws.column_dimensions['D'].width = 30
    ws.column_dimensions['E'].width = 20

    wb.save('全校学生信息.xlsx')
    QtWidgets.QMessageBox.information(None, "成功", "学生信息表模板下载成功,请查看当前目录!")

def xieru(df):
    df.to_excel('全校学生信息.xlsx', index=False)

def chazhao(xiang, zong):
    # 无论是数据库的哪一项基础操作(增、删、改、查),"查"就不调用了,因为要定位,还需要返回索引,这里就简单判断,返回索引放在class中了
    # 第一步均需进行查找,故将其写为函数,供随时调用
    if str(xiang) in [str(i) for i in list(zong['身份证号码'])]:  # 使用列表推导式将所有类型统一为str
        return True
    else:
        return False

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = login_interface()
    w.show()
    sys.exit(app.exec_())

欢迎留言/私信沟通交流!

相关推荐
叫我辉哥e12 小时前
新手进阶Python:办公看板集成OA自动化+AI提醒+定时任务
人工智能·python·自动化
北京耐用通信2 小时前
从“电缆束缚”到“光纤自由”:耐达讯自动化Profibus总线光纤中继器在化工通信的跨越式升级
人工智能·科技·物联网·自动化·信息与通信
郑州光合科技余经理2 小时前
O2O上门预约小程序:全栈解决方案
java·大数据·开发语言·人工智能·小程序·uni-app·php
新诺韦尔API2 小时前
车架号查询接口对接全流程详解
大数据·开发语言·python·api
鼓风机发现2 小时前
让 AI 走进物理世界:基于 MCP 协议构建工业级 IoT 智能实验室与语音自动化控制中枢
人工智能·物联网·自动化
weixin_177297220692 小时前
旧物回收新风尚,绿色生活新篇章——小程序引领环保新潮流
小程序·生活
CHU7290352 小时前
智慧回收新体验:同城废品回收小程序的便捷功能探索
java·前端·人工智能·小程序·php
一人の梅雨2 小时前
中国制造网关键字搜索接口实战:跨境B2B视角的精准匹配与本地化适配方案
人工智能·python·制造
2501_916008892 小时前
在不越狱前提下导出 iOS 应用文件的过程,访问应用沙盒目录,获取真实数据
android·macos·ios·小程序·uni-app·cocoa·iphone