基于之前的点名器设计,对点名器进行了一些小的改进,使其教学更加方便。
改进点:在开始程序界面需要先选择班级内容,才可以进入到主界面,支持点名时候加成绩:A、B、C、D,并支持导出CSV文件格式。
增加功能:班级考勤管理,点名时候可以选择改同学是否请假或者迟到、早退,并可以导出成CSV文件,使其更直观记录考勤。
正在努力看UI设计的书,对于直男审美,界面惨不忍睹!!!
后续功能无限期开发中…….
代码部分:
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()