基于PyQt5的一种班级管理设计——V1.2版
基于PyQt5的一种班级管理设计——V1.2版

基于PyQt5的一种班级管理设计——V1.2版

基于之前的点名器设计,对点名器进行了一些小的改进,使其教学更加方便。

改进点:在开始程序界面需要先选择班级内容,才可以进入到主界面,支持点名时候加成绩:A、B、C、D,并支持导出CSV文件格式。

增加功能:班级考勤管理,点名时候可以选择改同学是否请假或者迟到、早退,并可以导出成CSV文件,使其更直观记录考勤。

后续功能无限期开发中…….

运行程序后弹框
导入TXT名单成功
新增考勤界面
考勤记录案例
导出文件案例

代码部分:

import sys
import random
import re
import csv
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QRadioButton, QVBoxLayout, QHBoxLayout, \
    QWidget, QFileDialog, QMessageBox, QSlider, QDialog, QDialogButtonBox, QAction
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QFont


class StartupDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.filename = None
        self.initUI()

    def initUI(self):
        self.setWindowTitle('选择班级名单')
        self.setGeometry(400, 400, 300, 100)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
        self.buttonBox.accepted.connect(self.load_file)
        self.layout = QVBoxLayout()
        self.message = QLabel("请加载学生名单开始使用")
        self.layout.addWidget(self.message)
        self.layout.addWidget(self.buttonBox)
        self.setLayout(self.layout)

    def load_file(self):
        fname = QFileDialog.getOpenFileName(self, '选择班级名单', './', "文本文件 (*.txt)")
        if fname[0]:
            self.filename = fname[0]
            self.accept()  # 正确地关闭对话框
        else:
            QMessageBox.warning(self, "警告", "必须加载学生名单才能使用程序。")


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

        self.title = '班级管理系统'
        self.left = 100
        self.top = 100
        self.width = 800
        self.height = 600
        self.students = {}
        self.currentIndex = 0
        self.timer = QTimer(self)
        self.speed = 50  # 默认速度
        self.initUI()
        self.load_names(filename)

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        # 状态栏和菜单栏
        self.statusbar = self.statusBar()
        self.menubar = self.menuBar()
        self.create_menu()

        self.label = QLabel("点名系统已就绪", self)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setStyleSheet("color: #1E90FF;")
        self.update_font_size("点名系统已就绪")

        # 控制按钮
        self.btn_prev = QPushButton('上一个学生', self)
        self.btn_next = QPushButton('下一个学生', self)
        self.btn_absent = QPushButton('请假', self)
        self.btn_early = QPushButton('早退', self)
        self.btn_late = QPushButton('迟到', self)
        self.btn_start = QPushButton('开始', self)
        self.btn_export = QPushButton('导出数据', self)

        # 加分按钮
        self.btn_grade_a = QPushButton('A', self)
        self.btn_grade_b = QPushButton('B', self)
        self.btn_grade_c = QPushButton('C', self)
        self.btn_grade_d = QPushButton('D', self)
        self.btn_grade_a.clicked.connect(lambda: self.update_grade('A'))
        self.btn_grade_b.clicked.connect(lambda: self.update_grade('B'))
        self.btn_grade_c.clicked.connect(lambda: self.update_grade('C'))
        self.btn_grade_d.clicked.connect(lambda: self.update_grade('D'))

        # 点名模式选择
        self.radio_sequence = QRadioButton('顺序点名', self)
        self.radio_random = QRadioButton('随机点名', self)
        self.radio_sequence.setChecked(True)

        # 速度调节滑块
        self.speed_slider = QSlider(Qt.Horizontal, self)
        self.speed_slider.setMinimum(1)
        self.speed_slider.setMaximum(100)
        self.speed_slider.setValue(self.speed)
        self.speed_slider.setTickPosition(QSlider.TicksBelow)
        self.speed_slider.setTickInterval(10)
        self.speed_slider.valueChanged.connect(self.speed_changed)

        self.speed_label = QLabel(f'速度: {self.speed}', self)

        self.layout_widgets()
        self.connect_buttons()

    def create_menu(self):
        # 创建菜单选项
        self.menu_random = QAction('随机点名', self)
        self.menu_attendance = QAction('考勤管理', self)
        self.menu_random.triggered.connect(lambda: self.switch_mode(True))
        self.menu_attendance.triggered.connect(lambda: self.switch_mode(False))
        menu = self.menubar.addMenu('模式')
        menu.addAction(self.menu_random)
        menu.addAction(self.menu_attendance)

    def layout_widgets(self):
        # 布局管理
        radio_layout = QHBoxLayout()
        radio_layout.addWidget(self.radio_sequence)
        radio_layout.addWidget(self.radio_random)

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.btn_prev)
        button_layout.addWidget(self.btn_start)
        button_layout.addWidget(self.btn_next)
        button_layout.addWidget(self.btn_export)

        grade_layout = QHBoxLayout()
        grade_layout.addWidget(self.btn_grade_a)
        grade_layout.addWidget(self.btn_grade_b)
        grade_layout.addWidget(self.btn_grade_c)
        grade_layout.addWidget(self.btn_grade_d)

        status_layout = QHBoxLayout()
        status_layout.addWidget(self.btn_absent)
        status_layout.addWidget(self.btn_early)
        status_layout.addWidget(self.btn_late)

        slider_layout = QHBoxLayout()
        slider_layout.addWidget(self.speed_label)
        slider_layout.addWidget(self.speed_slider)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addLayout(radio_layout)
        layout.addLayout(button_layout)
        layout.addLayout(status_layout)
        layout.addLayout(grade_layout)
        layout.addLayout(slider_layout)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

    def connect_buttons(self):
        # 连接按钮事件
        self.btn_prev.clicked.connect(self.prev_student)
        self.btn_next.clicked.connect(self.next_student)
        self.btn_absent.clicked.connect(lambda: self.update_status('请假'))
        self.btn_early.clicked.connect(lambda: self.update_status('早退'))
        self.btn_late.clicked.connect(lambda: self.update_status('迟到'))
        self.btn_start.clicked.connect(self.toggle_start_point_name)
        self.btn_export.clicked.connect(self.export_data)

    def load_names(self, filepath):
        with open(filepath, 'r', encoding='utf-8') as file:
            all_names = [line.strip() for line in file.readlines()]
            valid_names = [name for name in all_names if re.match(r'^[\u4e00-\u9fa5]+$', name)]
            self.students = {name: {'status': '正常出勤', 'grade': ''} for name in valid_names}
            invalid_names_count = len(all_names) - len(valid_names)
            if not self.students:
                QMessageBox.warning(self, "警告", "名单为空或无有效中文名,请检查文件!")
            else:
                QMessageBox.information(self, "成功", f"一共加载了{len(self.students)}个中文姓名,{invalid_names_count}个非中文姓名被忽略。")
                self.currentIndex = 0
                self.display_current_student()

    def switch_mode(self, is_random):
        # 切换模式显示
        self.btn_prev.setVisible(not is_random)
        self.btn_next.setVisible(not is_random)
        self.btn_absent.setVisible(not is_random)
        self.btn_early.setVisible(not is_random)
        self.btn_late.setVisible(not is_random)
        self.btn_grade_a.setVisible(is_random)
        self.btn_grade_b.setVisible(is_random)
        self.btn_grade_c.setVisible(is_random)
        self.btn_grade_d.setVisible(is_random)
        self.radio_sequence.setVisible(is_random)
        self.radio_random.setVisible(is_random)
        self.btn_start.setVisible(is_random)
        self.speed_slider.setVisible(is_random)
        self.speed_label.setVisible(is_random)

    def next_student(self):
        self.currentIndex = (self.currentIndex + 1) % len(self.students)
        self.display_current_student()

    def prev_student(self):
        self.currentIndex = (self.currentIndex - 1 + len(self.students)) % len(self.students)
        self.display_current_student()

    def random_student(self):
        self.currentIndex = random.randint(0, len(self.students) - 1)
        self.display_current_student()

    def display_current_student(self):
        names = list(self.students.keys())
        current_name = names[self.currentIndex]
        self.label.setText(current_name + " - " + self.students[current_name]['grade'])
        self.update_font_size(current_name)

    def update_status(self, status):
        current_name = self.label.text().split(" - ")[0]
        if current_name in self.students:
            self.students[current_name]['status'] = status
            QMessageBox.information(self, "更新状态", f"{current_name}的状态已更新为{status}")

    def update_grade(self, grade):
        current_name = self.label.text().split(" - ")[0]
        if current_name in self.students:
            self.students[current_name]['grade'] = grade
            self.label.setText(current_name + " - " + grade)
            QMessageBox.information(self, "更新分数", f"{current_name}的分数已更新为{grade}")

    def toggle_start_point_name(self):
        if self.timer.isActive():
            self.timer.stop()
            self.btn_start.setText("开始")
        else:
            self.btn_start.setText("停止")
            if self.radio_sequence.isChecked():
                self.timer.timeout.connect(self.next_student)
            elif self.radio_random.isChecked():
                self.timer.timeout.connect(self.random_student)
            self.timer.start(2000 // self.speed)

    def speed_changed(self, value):
        self.speed = value
        self.speed_label.setText(f'速度: {self.speed}')
        if self.timer.isActive():
            self.timer.start(2000 // self.speed)

    def export_data(self):
        # 导出数据到CSV文件
        filename, _ = QFileDialog.getSaveFileName(self, "保存文件", "./", "CSV文件 (*.csv)")
        if filename:
            with open(filename, 'w', newline='', encoding='utf-8-sig') as csvfile:  # 使用utf-8-sig以确保中文不乱码
                fieldnames = ['姓名', '状态', '分数']
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                for name, data in self.students.items():
                    writer.writerow({'姓名': name, '状态': data['status'], '分数': data['grade']})
            QMessageBox.information(self, "导出成功", "数据已成功导出到CSV文件。")

    def update_font_size(self, name):
        if len(name) <= 2:
            self.label.setFont(QFont('Arial', 80, QFont.Bold))
        elif len(name) <= 4:
            self.label.setFont(QFont('Arial', 60, QFont.Bold))
        else:
            self.label.setFont(QFont('Arial', 48, QFont.Bold))


def main():
    app = QApplication(sys.argv)
    startup_dialog = StartupDialog()
    if startup_dialog.exec_() == QDialog.Accepted:
        ex = App(startup_dialog.filename)
        ex.show()
        sys.exit(app.exec_())


if __name__ == '__main__':
    main()

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注