-
直观的 QT 图形界面:采用 QT 构建的用户友好界面,提供清晰的菜单选项,确保用户轻松导航和访问各项功能。
-
数据库驱动的数据存储:系统使用数据库技术安全高效地存储学生信息,保障数据的完整性和可靠性。
-
全面的基本功能:包括添加、删除、修改和查询学生数据,支持模糊查找,便捷地获取所需信息。
-
高级统计功能:系统具备先进的数据统计功能,提供图形化展示,使成绩分析直观易懂,支持对学生表现的全面分析。
-
数据的批量导入和导出:提供批量导入学生信息的能力,方便从其他系统迁移数据;同时支持将数据以 CSV 格式导出,便于备份或在其他应用中使用。
-
内置帮助文档:配备完善的帮助文档,用户可通过按 F1 快捷键获取即时帮助,轻松了解系统的使用方法和功能细节。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QTableWidget>
#include <QDoubleSpinBox>
#include <QRadioButton>
#include <QLabel>
#include <QtCharts>
#include "studentmanager.h"
#include "student.h"using namespace QtCharts;class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);private:QPushButton *addButton;QPushButton *deleteButton;QPushButton *updateButton;QLineEdit *nameEdit;QDoubleSpinBox *scoreSpin;QTableWidget *studentTable;QPushButton *searchButton;QRadioButton *sortAscButton;QRadioButton *sortDescButton;QLineEdit *searchEdit;QLabel *analysisLabel;QChartView *chartView;QPushButton *exportButton;QPushButton *importButton;StudentManager studentManager;void setupUi();void connectSignalsSlots();void refreshStudentTable();void refreshStudentTable(const QList<Student>& students);void updateChart();QChart* createChart();int getNextStudentId();void updateAnalysis(const QList<Student>& students);void importData();void exportData();private slots:void addStudent();void deleteStudent();void updateStudent();void searchStudents();void sortStudents();void onExportButtonClicked();void onImportButtonClicked();};#endif // MAINWINDOW_H
/*** @brief 学生成绩管理系统 20240408* @author VX:Cgsjed*/
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QStringList>
#include <QHeaderView>
# define tc(a) QString::fromLocal8Bit(a)
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {setupUi();connectSignalsSlots();refreshStudentTable();
}void MainWindow::setupUi() {QWidget *centralWidget = new QWidget(this);QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);QHBoxLayout *inputLayout = new QHBoxLayout();nameEdit = new QLineEdit();scoreSpin = new QDoubleSpinBox();addButton = new QPushButton(tc("添加"));deleteButton = new QPushButton(tc("删除"));updateButton = new QPushButton(tc("刷新"));inputLayout->addWidget(new QLabel(tc("姓名")));inputLayout->addWidget(nameEdit);inputLayout->addWidget(new QLabel(tc("分数")));inputLayout->addWidget(scoreSpin);inputLayout->addWidget(addButton);inputLayout->addWidget(deleteButton);inputLayout->addWidget(updateButton);studentTable = new QTableWidget();studentTable->setColumnCount(3); // ID, Name, ScorestudentTable->setFixedWidth(300);QStringList headers = {tc("编号"), tc("姓名"), tc("分数")};studentTable->setHorizontalHeaderLabels(headers);studentTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);studentTable->setSortingEnabled(true);QHBoxLayout *searchLayout = new QHBoxLayout();searchButton = new QPushButton(tc("查询"));searchEdit = new QLineEdit();sortAscButton = new QRadioButton(tc("分数正序"));sortDescButton = new QRadioButton(tc("分数倒序"));sortAscButton->setChecked(true);exportButton = new QPushButton(tc("数据导出"), this);importButton = new QPushButton(tc("导入数据"), this);searchLayout->addWidget(exportButton);searchLayout->addWidget(importButton);searchLayout->addWidget(new QLabel(tc("输出查询的姓名")));searchLayout->addWidget(searchEdit);searchLayout->addWidget(searchButton);searchLayout->addWidget(sortAscButton);searchLayout->addWidget(sortDescButton);chartView = new QChartView(createChart());mainLayout->addLayout(inputLayout);mainLayout->addLayout(searchLayout);QHBoxLayout *searchLayout1 = new QHBoxLayout();searchLayout1->addWidget(studentTable);searchLayout1->addWidget(chartView);mainLayout->addLayout(searchLayout1);setCentralWidget(centralWidget);this->setWindowTitle(tc("学生成绩管理系统"));
}void MainWindow::connectSignalsSlots() {connect(addButton, &QPushButton::clicked, this, &MainWindow::addStudent);connect(deleteButton, &QPushButton::clicked, this, &MainWindow::deleteStudent);connect(updateButton, &QPushButton::clicked, this, &MainWindow::updateStudent);connect(searchButton, &QPushButton::clicked, this, &MainWindow::searchStudents);connect(sortAscButton, &QRadioButton::toggled, this, &MainWindow::sortStudents);connect(sortDescButton, &QRadioButton::toggled, this, &MainWindow::sortStudents);connect(exportButton, &QPushButton::clicked, this, &MainWindow::onExportButtonClicked);connect(importButton, &QPushButton::clicked, this, &MainWindow::onImportButtonClicked);}void MainWindow::addStudent() {QString name = nameEdit->text();double score = scoreSpin->value();if (name.isEmpty() || score < 0) {QMessageBox::warning(this, "Input Error", "Invalid name or score.");return;}Student student(getNextStudentId(), name, score);studentManager.addStudent(student);refreshStudentTable();updateChart();
}void MainWindow::deleteStudent() {int row = studentTable->currentRow();if (row == -1) {QMessageBox::warning(this, "Selection Error", "Please select a student to delete.");return;}int id = studentTable->item(row, 0)->text().toInt();studentManager.deleteStudent(id);refreshStudentTable();updateChart();
}void MainWindow::updateStudent()
{refreshStudentTable();
}void MainWindow::searchStudents() {QString name = searchEdit->text();auto students = studentManager.findStudentsByName(name);refreshStudentTable(students);updateChart();
}void MainWindow::sortStudents() {bool isAscending = sortAscButton->isChecked();studentTable->sortItems(2, isAscending ? Qt::AscendingOrder : Qt::DescendingOrder);
}void MainWindow::onExportButtonClicked()
{// 实现导出数据到文件的逻辑exportData();
}void MainWindow::onImportButtonClicked()
{// 实现从文件导入数据的逻辑importData();
}void MainWindow::refreshStudentTable() {auto students = studentManager.getAllStudents();refreshStudentTable(students);
}void MainWindow::refreshStudentTable(const QList<Student>& students) {studentTable->clearContents();studentTable->setRowCount(students.count());for (int i = 0; i < students.count(); ++i) {auto student = students[i];studentTable->setItem(i, 0, new QTableWidgetItem(QString::number(student.getId())));studentTable->setItem(i, 1, new QTableWidgetItem(student.getName()));studentTable->setItem(i, 2, new QTableWidgetItem(QString::number(student.getScore())));}studentTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);studentTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);studentTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
}void MainWindow::updateChart() {auto chart = createChart();chartView->setChart(chart);
}
QChart* MainWindow::createChart() {QBarSeries *series = new QBarSeries();QBarSet *set = new QBarSet(tc("及格线"));QStringList categories;// 从学生管理器获取数据并填充到柱状图中QList<Student> students = studentManager.getAllStudents();for (const Student &student : students) {*set << student.getScore(); // 将分数添加到柱状图categories << student.getName(); // 将学生名字添加到类别中}series->append(set);QChart *chart = new QChart();chart->addSeries(series);chart->setTitle(tc("班级成绩柱状图"));chart->setAnimationOptions(QChart::SeriesAnimations);QBarCategoryAxis *axisX = new QBarCategoryAxis();axisX->append(categories); // 设置类别为学生姓名chart->addAxis(axisX, Qt::AlignBottom);series->attachAxis(axisX);QValueAxis *axisY = new QValueAxis();axisY->setRange(0, 100);axisY->setTickInterval(10);chart->addAxis(axisY, Qt::AlignLeft);series->attachAxis(axisY);// 在 60 分位置绘制一条横线作为及格线QLineSeries *passLine = new QLineSeries();passLine->append(QPointF(-5, 60));passLine->append(QPointF(students.count(), 60)); // 覆盖整个 X 轴范围QPen pen(Qt::red, 2, Qt::DashLine); // 红色虚线passLine->setPen(pen);chart->addSeries(passLine);passLine->attachAxis(axisX);passLine->attachAxis(axisY);chart->legend()->setVisible(true);chart->legend()->setAlignment(Qt::AlignBottom);return chart;
}int MainWindow::getNextStudentId() {auto students = studentManager.getAllStudents();int maxId = 0;for (auto &student : students) {if (student.getId() > maxId)maxId = student.getId();}return maxId + 1;
}void MainWindow::importData() {QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("CSV (*.csv)"));if (fileName.isEmpty()) {return;}QFile file(fileName);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {QMessageBox::warning(this, tr("Error"), tr("Unable to open file"));return;}QTextStream stream(&file);// 忽略第一行标题if (!stream.atEnd()) {stream.readLine();}QSet<QString> existingNames;for (const auto &student : studentManager.getAllStudents()) {existingNames.insert(student.getName());}int nextId = getNextStudentId();while (!stream.atEnd()) {QString line = stream.readLine();QStringList strList = line.split(",");if (strList.size() >= 3) {QString name = strList.at(1).trimmed().replace("\"", "");double score = strList.at(2).trimmed().replace("\"", "").toDouble();// 仅添加不存在的新名字if (!existingNames.contains(name)) {Student student(nextId++, name, score);studentManager.addStudent(student);existingNames.insert(name);}}}file.close();refreshStudentTable();updateChart();
}void MainWindow::exportData()
{QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("CSV (*.csv)"));if (fileName.isEmpty()) {return;}QFile file(fileName);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {QMessageBox::warning(this, tr("Error"), tr("Unable to open file"));return;}QTextStream stream(&file);QStringList strList;// 写入标题for (int c = 0; c < studentTable->horizontalHeader()->count(); ++c) {strList << "\"" + studentTable->model()->headerData(c, Qt::Horizontal).toString() + "\"";}stream << strList.join(",") << "\n";// 写入数据for (int r = 0; r < studentTable->rowCount(); ++r) {strList.clear();for (int c = 0; c < studentTable->columnCount(); ++c) {strList << "\"" + studentTable->item(r, c)->text() + "\"";}stream << strList.join(",") << "\n";}file.close();
}