Qt/C++项目 学生成绩管理系统

时间:2024-04-13 18:34:25

  1. 直观的 QT 图形界面:采用 QT 构建的用户友好界面,提供清晰的菜单选项,确保用户轻松导航和访问各项功能。

  2. 数据库驱动的数据存储:系统使用数据库技术安全高效地存储学生信息,保障数据的完整性和可靠性。

  3. 全面的基本功能:包括添加、删除、修改和查询学生数据,支持模糊查找,便捷地获取所需信息。

  4. 高级统计功能:系统具备先进的数据统计功能,提供图形化展示,使成绩分析直观易懂,支持对学生表现的全面分析。

  5. 数据的批量导入和导出:提供批量导入学生信息的能力,方便从其他系统迁移数据;同时支持将数据以 CSV 格式导出,便于备份或在其他应用中使用。

  6. 内置帮助文档:配备完善的帮助文档,用户可通过按 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_OBJECT
    
public:
    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, Score
    studentTable->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();
}