-
直观的 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_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();
}