1. 项目概述与核心价值
最近在整理一些C++和Qt的实战项目,发现很多朋友对如何将这两者结合起来,做一个有实际应用价值的桌面程序很感兴趣。正好,我之前花了不少时间,用C++和Qt完整地实现了一个中学生成绩管理系统。这不仅仅是一个简单的增删改查(CRUD)练习,它涉及到了从底层数据模型设计、数据库操作、到前端UI交互、再到业务逻辑封装的全链路开发。对于想深入理解Qt框架在C++项目中的应用,尤其是如何构建一个结构清晰、可维护的桌面应用,这个项目是个非常不错的切入点。
这个系统麻雀虽小,五脏俱全。它模拟了学校教务管理中一个非常核心的场景:管理学生的基本信息和各科成绩。你需要实现用户登录验证(总不能谁都能改成绩吧)、以表格形式展示所有学生数据、支持对数据的增删改查、能按各科成绩排序、还能一键生成成绩汇总报告。听起来功能点不少,但用Qt来构建,你会发现它的信号槽机制、模型视图框架、以及丰富的UI控件,能让这些功能的实现变得相当优雅和高效。
无论你是刚学完C++基础,想找个项目练手,巩固面向对象和STL容器的使用;还是已经对Qt有些了解,希望深入掌握其数据库模块(Qt SQL)和自定义模型视图;甚至是需要完成课程设计或毕业设计,这个项目都能提供一条清晰的实现路径。接下来,我会把这个项目的设计思路、关键技术点、踩过的坑以及完整的实现过程,毫无保留地拆解给你看。
2. 整体架构设计与技术选型考量
2.1 为什么选择C++和Qt这个技术栈?
首先得说说为什么是C++和Qt。C++自不必说,性能高、控制力强,是很多系统级软件和桌面应用的首选。而Qt是一个跨平台的C++图形用户界面应用程序框架,它最大的魅力在于其“一次编写,到处编译”的特性,以及极其成熟的信号与槽(Signals & Slots)机制,这让GUI事件处理变得非常直观。
对于成绩管理系统这类对响应速度和本地数据操作有要求的桌面应用,C++/Qt组合是黄金搭档。相比用Electron或Java Swing等方案,C++/Qt编译出的原生程序体积小、启动快、内存占用低。Qt还内置了SQLite驱动,这意味着我们不需要额外安装数据库服务,用单个.db文件就能管理所有数据,部署起来极其方便。这正是我们需要的:一个轻量、快速、且能独立运行的桌面工具。
2.2 核心功能模块拆解
在动手写代码之前,必须把系统要干什么想清楚。我把核心功能分解为以下几个模块:
- 用户认证模块:负责登录验证。需要设计一个登录界面,验证用户名和密码。密码不能明文存储,必须加密。这里会用到简单的加密算法(如MD5)和本地文件存储。
- 数据管理核心模块:这是系统的心脏。所有对学生信息的操作都发生在这里,包括:
- 增(Create):添加新学生记录。
- 删(Delete):按学号删除学生记录。
- 改(Update):修改已有学生的信息或成绩。
- 查(Retrieve):查询并展示所有学生数据。
- 数据展示与交互模块:主要通过Qt的
QTableWidget或QTableView配合模型,将数据库中的数据以表格形式友好地展示出来。并需要处理用户在表格上的交互,如双击编辑、点击表头排序等。 - 数据持久化模块:决定数据存哪里、怎么存。我们选择SQLite数据库,因为它无需配置,单个文件,非常适合桌面应用。Qt的
QSqlDatabase、QSqlQuery等类提供了完整的数据库操作接口。 - 业务逻辑与统计模块:除了基本的CRUD,还需要实现一些业务功能,比如计算每个学生的平均分、对单科或多科成绩进行排序、生成全班成绩的统计汇总(如平均分、最高分、最低分、及格率等)。
2.3 项目目录结构与类设计
一个清晰的项目结构是成功的一半。我的项目目录大致如下:
StudentScoreManager/ ├── CMakeLists.txt / stu-score-sys.pro # 项目构建文件 ├── src/ │ ├── main.cpp │ ├── LoginDialog.{hpp, cpp} # 登录对话框类 │ ├── MainWindow.{hpp, cpp} # 主窗口类 │ ├── StudentModel.{hpp, cpp} # 自定义数据模型类(可选) │ └── DatabaseManager.{hpp, cpp} # 数据库管理类(核心) ├── ui/ │ ├── login.ui # 登录界面设计文件 │ └── mainwindow.ui # 主界面设计文件 ├── resources/ # 资源文件(如图标) └── data/ └── scores.db # SQLite数据库文件(运行时生成)在类设计上,我遵循了单一职责原则:
LoginDialog:只负责展示登录界面、验证用户凭证。DatabaseManager:作为单例或全局访问点,封装所有数据库连接、初始化和原子操作(执行SQL语句)。这是数据层的核心,避免SQL语句散落在各处。MainWindow:主窗口,负责加载UI、初始化表格视图、连接各种按钮的信号到具体的槽函数,协调各个功能。StudentModel(可选但推荐):如果直接使用QSqlTableModel或QSqlQueryModel不能满足复杂的展示逻辑(比如在表格中直接显示计算出的平均分),可以继承QAbstractTableModel自定义一个模型,将数据获取和展示逻辑分离,这是Qt MVC的精华。
实操心得:一开始我图省事,把所有的数据库操作SQL都直接写在
MainWindow的各个槽函数里。结果代码又乱又长,改一个表字段名得找半天。后来抽象出DatabaseManager类,所有SQL语句集中管理,主窗口逻辑瞬间清爽,而且数据库逻辑可以独立测试和复用。这个重构非常值得。
3. 开发环境搭建与项目初始化
3.1 Qt开发环境配置详解
工欲善其事,必先利其器。对于Windows平台,我推荐使用Qt Online Installer安装Qt。在安装时,关键是要选对组件:
- Qt版本:选择长期支持版(LTS),如Qt 5.15.2或Qt 6.2+。我项目用的是Qt 6.4.1,新特性更多,但对编译器有要求。新手用Qt 5.15.2会更稳定,资料也多。
- 编译器:如果你选Qt 5.15.2,通常会搭配
MinGW 8.1.0 64-bit。如果选Qt 6.4.1,则需要MinGW 11.2.0 64-bit。务必确保安装器为你选择的Qt版本安装了对应的编译器套件(Kit)。 - 额外组件:勾选
Qt Creator(这是我们的IDE),以及Sources(Qt源码,方便调试时查看)。对于数据库应用,确保Qt SQL模块被选中(通常默认包含)。
安装完成后,打开Qt Creator,进入工具 -> 选项 -> Kits,检查是否自动检测到了你的Qt版本和编译器,并构成了一个可用的Kit。这是项目能编译运行的前提。
3.2 创建Qt Widgets Application项目
在Qt Creator中,选择文件 -> 新建文件或项目,选择Application -> Qt Widgets Application。给项目起名,比如StudentScoreManager。在Kit Selection页面,选择刚才配置好的Kit。在类信息页面,可以设置主窗口的类名,比如MainWindow,基类选择QMainWindow。这样Qt Creator会自动为你生成main.cpp、mainwindow.h、mainwindow.cpp和mainwindow.ui文件。
创建完成后,第一件事是修改项目配置文件(.pro文件),添加我们需要的模块。打开项目根目录下的.pro文件,确保有以下行:
QT += core gui sql # 关键!添加sql模块以使用数据库功能 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++17 # 使用C++17标准,方便使用一些现代语法 # 设置生成路径,让编译输出更整洁 DESTDIR = $$PWD/bin OBJECTS_DIR = $$PWD/build/.obj MOC_DIR = $$PWD/build/.moc RCC_DIR = $$PWD/build/.rcc UI_DIR = $$PWD/build/.ui添加QT += sql这一行至关重要,否则编译时找不到QSqlDatabase等头文件。
3.3 数据库设计与初始化
在编码之前,我们先设计数据库表结构。根据需求,一个students表至少需要这些字段:
CREATE TABLE IF NOT EXISTS students ( id TEXT PRIMARY KEY, -- 学号,设为主键 name TEXT NOT NULL, -- 姓名 class TEXT, -- 班级 chinese REAL DEFAULT 0, -- 语文成绩 math REAL DEFAULT 0, -- 数学成绩 english REAL DEFAULT 0, -- 英语成绩 average REAL GENERATED ALWAYS AS ((chinese + math + english) / 3) STORED -- 平均分(SQLite 3.31+支持生成列) );这里我用了SQLite 3.31版本之后支持的GENERATED ALWAYS AS列来自动计算并存储平均分。如果你的SQLite版本较旧,可以在插入或更新数据时用程序计算,或者通过视图(View)来动态计算。
注意事项:将学号(
id)设为TEXT类型而不是INTEGER,是因为学号可能包含字母(如“S001”),且通常不需要进行算术运算。设为PRIMARY KEY确保了学号的唯一性。REAL类型用于存储可能带小数的成绩。
数据库的初始化工作应该放在DatabaseManager类中。这个类在程序启动时,检查数据库文件是否存在,如果不存在则创建文件并建表。
4. 核心模块实现:从登录到数据管理
4.1 登录模块实现与密码安全
登录界面通常是一个独立的对话框。在Qt Creator中,右键项目,选择添加新文件 -> Qt -> Qt设计师界面类 -> Dialog without Buttons,命名为LoginDialog。在login.ui文件中,拖放两个QLabel(“用户名”、“密码”)、两个QLineEdit(一个用于密码的需设置echoMode为Password)、一个QPushButton(“登录”)。
逻辑层面,LoginDialog类需要做以下几件事:
- 读取存储的凭证:程序首次运行时,没有密码文件。可以设置一个默认账号密码(如admin/123456)。当用户第一次登录成功,将加密后的密码存储到本地文件(如
admin.dat)。下次启动时,从该文件读取。 - 密码加密:绝对不要明文存储密码!即使是本地文件。可以使用简单的MD5哈希。Qt提供了
QCryptographicHash类。
// 在DatabaseManager或一个工具类中 QString encryptPassword(const QString &password) { QByteArray hash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5); return QString(hash.toHex()); // 转换为十六进制字符串存储 }- 验证逻辑:用户点击登录按钮时,获取输入框文本,对输入的密码进行相同的加密,然后与存储的加密串比较。如果一致,调用
accept()关闭对话框并返回QDialog::Accepted;否则,弹出提示框。 - 主窗口调用:在
main.cpp或主窗口初始化前,创建LoginDialog实例并执行。如果登录失败,直接退出程序。
int main(int argc, char *argv[]) { QApplication a(argc, argv); LoginDialog login; if (login.exec() == QDialog::Accepted) { MainWindow w; w.show(); return a.exec(); } return 0; }4.2 数据库管理类(DatabaseManager)封装
这是整个系统的数据访问层,我强烈建议将其设计为单例模式,确保全局只有一个数据库连接。
// DatabaseManager.hpp #pragma once #include <QObject> #include <QSqlDatabase> #include <QSqlError> #include <QVector> #include <QStringList> struct Student { QString id; QString name; QString className; double chinese; double math; double english; double average; // 可以由数据库生成列或程序计算 }; class DatabaseManager : public QObject { Q_OBJECT public: static DatabaseManager& instance(); bool initialize(const QString& path = "data/scores.db"); // 初始化数据库连接和表 bool addStudent(const Student& stu); bool deleteStudentById(const QString& id); bool updateStudent(const Student& stu); QVector<Student> getAllStudents(); QVector<Student> getStudentsSortedBy(const QString& field, bool ascending = true); // 统计函数 QMap<QString, double> calculateSubjectStatistics(const QString& subject); // 计算某科的平均分、最高分等 // ... 其他辅助函数 QSqlDatabase database() const { return m_db; } QString lastError() const { return m_lastError; } private: explicit DatabaseManager(QObject* parent = nullptr); ~DatabaseManager(); DatabaseManager(const DatabaseManager&) = delete; DatabaseManager& operator=(const DatabaseManager&) = delete; QSqlDatabase m_db; QString m_lastError; };在.cpp文件中,实现initialize函数,负责打开数据库、执行建表SQL。其他函数则是对应SQL语句的封装,使用QSqlQuery来执行,并处理执行结果和错误。
踩坑记录:数据库操作一定要做好错误检查!每次执行
QSqlQuery.exec()后,都要检查query.lastError().isValid()。把错误信息记录到m_lastError或用qDebug()输出,这在调试时能救命。另外,注意SQL注入风险,所有用户输入的数据在拼接到SQL语句中时,必须使用参数化查询(prepare+bindValue),而不是字符串拼接。
4.3 主界面搭建与数据展示
主窗口(MainWindow)使用QMainWindow,中心区域放置一个QTableWidget来展示数据。周围放置各种功能按钮:“新增”、“删除”、“修改”、“保存”、“排序”、“汇总”等。
数据展示有两种主流方式:
- 使用
QSqlTableModel:这是最简单的方式。QSqlTableModel直接映射到数据库表,能自动处理数据的读取和写入。
这种方式快速,但灵活性稍差,比如计算列(平均分)需要额外处理。QSqlTableModel *model = new QSqlTableModel(this, DatabaseManager::instance().database()); model->setTable("students"); model->setEditStrategy(QSqlTableModel::OnManualSubmit); // 手动提交修改 model->select(); // 加载数据 ui->tableView->setModel(model); ui->tableView->setSortingEnabled(true); // 启用点击排序 - 使用自定义模型:继承
QAbstractTableModel,自己控制数据的来源和展示。这更复杂,但功能最强。你可以在data()函数中,根据不同的Qt::ItemDataRole返回不同的值,比如在Qt::DisplayRole时,从DatabaseManager获取的Student结构体中返回对应字段;在Qt::EditRole时,提供可编辑的数据。
我推荐新手先从// 在自定义模型的data函数中 QVariant StudentTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_students.size()) return QVariant(); const Student &stu = m_students.at(index.row()); int col = index.column(); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch(col) { case 0: return stu.id; case 1: return stu.name; // ... 其他列 case 6: return QString::number(stu.average, 'f', 2); // 平均分显示两位小数 } } return QVariant(); }QSqlTableModel开始,实现基本功能。当需要更复杂的交互(比如在表格内嵌自定义控件、复杂的格式显示)时,再考虑自定义模型。
4.4 增删改查(CRUD)功能实现
- 增(Create):点击“新增”按钮,弹出一个对话框(
QDialog),里面是各个字段的输入框。用户填写后,点击确定,在对话框的槽函数中,构造一个Student对象,调用DatabaseManager::addStudent()。成功后,需要刷新主表格的视图(如果是QSqlTableModel,调用model->select();如果是自定义模型,则重新从数据库加载数据并调用beginResetModel()/endResetModel())。 - 删(Delete):在表格中选中一行,或者提供一个输入框让用户输入学号。获取到要删除的学号后,调用
DatabaseManager::deleteStudentById()。务必弹出一个确认对话框(QMessageBox::question),防止误操作。 - 改(Update):如果使用
QSqlTableModel且设置了OnManualSubmit策略,用户可以直接在表格单元格里双击编辑。编辑完成后,需要点击一个“保存”按钮,调用model->submitAll()来提交所有挂起的修改。如果使用自定义模型,则需要捕获表格的doubleClicked信号,弹出一个编辑对话框(与新增对话框类似,但初始值填充为选中行的数据),修改后调用DatabaseManager::updateStudent()。 - 查(Retrieve):展示本身就是查询。可以增加一个搜索框,实现按姓名或学号模糊查询。这需要在
DatabaseManager中增加一个searchStudents函数,执行带LIKE的SQL语句。
实操技巧:对于“编辑”功能,一个常见的需求是防止误操作。我通常会在界面上加一个“编辑模式”开关(一个
QPushButton或QAction)。默认表格是只读的(setEditTriggers(QAbstractItemView::NoEditTriggers))。点击进入编辑模式后,再允许双击编辑。这样逻辑更清晰,也安全。
5. 高级功能与界面优化
5.1 数据排序与过滤
Qt的模型视图框架内置了排序功能。对于QSqlTableModel,设置ui->tableView->setSortingEnabled(true);后,点击表头即可排序,模型会自动生成ORDER BY子句的SQL并重新查询,非常高效。对于自定义模型,你需要重写sort函数,在里面根据排序列和顺序,对底层的数据容器(如QVector<Student>)进行排序,然后通知视图刷新。
过滤(或搜索)功能,可以通过在QSqlTableModel上设置过滤器实现:model->setFilter("name LIKE '%张%'"),然后model->select()。对于自定义模型,则需要在获取数据时,将过滤条件传递给DatabaseManager。
5.2 成绩统计与可视化
“成绩汇总”功能需要计算各科的统计数据。在DatabaseManager中实现一个函数:
QMap<QString, double> DatabaseManager::calculateSubjectStatistics(const QString& subject) { QMap<QString, double> stats; QSqlQuery query(m_db); QString sql = QString("SELECT AVG(%1), MAX(%1), MIN(%1), COUNT(*) FROM students WHERE %1 IS NOT NULL").arg(subject); if (query.exec(sql) && query.next()) { stats["average"] = query.value(0).toDouble(); stats["max"] = query.value(1).toDouble(); stats["min"] = query.value(2).toDouble(); stats["count"] = query.value(3).toInt(); // 还可以计算及格率等 QString sqlPass = QString("SELECT COUNT(*) FROM students WHERE %1 >= 60").arg(subject); if (query.exec(sqlPass) && query.next()) { int passCount = query.value(0).toInt(); stats["passRate"] = (stats["count"] > 0) ? (passCount * 100.0 / stats["count"]) : 0.0; } } return stats; }在主窗口中,调用这个函数获取数据,然后用QLabel或另一个表格展示出来,甚至可以尝试用Qt Charts模块画一个简单的柱状图来直观对比各科平均分,这会让你的项目增色不少。
5.3 界面美化与QSS应用
Qt的界面样式可以通过QSS(Qt Style Sheets)来美化,其语法类似CSS。你可以为不同的控件设置背景、边框、字体、颜色等。例如,让表格看起来更清爽:
/* 在主窗口的构造函数中加载样式表 */ QFile styleFile(":/style.qss"); styleFile.open(QFile::ReadOnly); QString styleSheet = QLatin1String(styleFile.readAll()); qApp->setStyleSheet(styleSheet); /* style.qss 内容示例 */ QTableWidget { border: 1px solid #e0e0e0; border-radius: 4px; gridline-color: #f0f0f0; background-color: white; } QHeaderView::section { background-color: #f8f9fa; padding: 8px; border: none; border-bottom: 2px solid #dee2e6; font-weight: bold; } QTableWidget::item { padding: 6px; } QTableWidget::item:selected { background-color: #e3f2fd; }通过QSS,可以轻松实现现代化、扁平化的界面风格,摆脱默认的陈旧外观。
5.4 数据导入与导出
一个实用的成绩管理系统应该支持从Excel或CSV文件导入数据,以及将表格数据导出为文件。Qt提供了QFile和QTextStream来处理文本文件。对于CSV,可以逐行读取/写入,用逗号分隔字段。对于更复杂的Excel(.xlsx),可以考虑使用第三方库,如QXlsx。
导入的基本流程是:打开文件对话框(QFileDialog)选择文件 -> 逐行读取解析 -> 验证数据格式 -> 调用DatabaseManager::addStudent批量插入(可以使用事务QSqlDatabase::transaction()来提高性能和数据一致性)。
导出的流程则是:从数据库获取所有数据 -> 格式化为CSV或HTML字符串 -> 打开文件对话框选择保存路径 -> 写入文件。
6. 项目构建、调试与打包发布
6.1 使用CMake管理Qt项目(现代推荐)
虽然Qt Creator默认使用.pro(qmake)文件,但CMake现在是更主流的跨平台构建工具。将qmake项目转换为CMake并不难。一个基本的CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.16) project(StudentScoreManager LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) find_package(Qt6 COMPONENTS Core Widgets Sql REQUIRED) # 查找Qt6组件 # 如果有资源文件 set(RESOURCES resources/resources.qrc) add_executable(StudentScoreManager src/main.cpp src/MainWindow.cpp src/MainWindow.hpp src/LoginDialog.cpp src/LoginDialog.hpp src/DatabaseManager.cpp src/DatabaseManager.hpp ${RESOURCES} ) target_link_libraries(StudentScoreManager PRIVATE Qt6::Core Qt6::Widgets Qt6::Sql )使用CMake后,你可以在Qt Creator、VS Code、CLion等多种IDE中打开项目,灵活性更高。
6.2 调试技巧与常见问题排查
- qDebug()是你的好朋友:在关键函数入口、数据库操作前后、信号槽连接处,使用
qDebug() << "Debug info: " << variable;输出信息,这是Qt下最快速的调试方式。 - 使用Qt Creator的调试器:设置断点,单步执行,查看变量值,这对于理解程序流程和查找逻辑错误至关重要。
- 数据库错误:最常见的错误是SQL语句错误或数据库连接失败。务必检查
QSqlDatabase::lastError()和QSqlQuery::lastError()。将执行的SQL语句用qDebug()打印出来,放到SQLite管理工具(如DB Browser for SQLite)里跑一下,能快速定位问题。 - 界面不显示/布局混乱:检查
.ui文件中的布局管理器(Layouts)是否正确应用。确保主窗口的中心部件(Central Widget)设置了布局。在代码中动态添加控件后,别忘了调用layout()->update()或adjustSize()。 - 信号槽不触发:第一,检查连接语句
connect(sender, SIGNAL(...), receiver, SLOT(...))或connect(sender, &Sender::signal, receiver, &Receiver::slot)是否正确。第二,确保槽函数声明在类的private slots:或public slots:区域(对于Qt5的新语法,普通函数也可作为槽)。第三,发送者和接收者的对象生命周期要确保有效(没有提前被销毁)。
6.3 打包发布为独立可执行文件
开发完成后,你需要将程序打包,以便在没有安装Qt和编译环境的电脑上运行。在Windows上,主要步骤如下:
- 以Release模式编译:在Qt Creator中,将构建套件切换到
Release模式,然后编译。这会在构建目录(如build-...-Release)下生成一个.exe文件。 - 使用
windeployqt工具:这是Qt自带的部署工具。打开Qt 6.4.1 MinGW 64-bit命令行(在开始菜单Qt文件夹下),导航到你的.exe文件所在目录,执行:
这个命令会自动扫描你的windeployqt StudentScoreManager.exe.exe文件依赖哪些Qt的DLL和插件,并把它们复制到当前目录。它还会复制必要的platforms、styles等文件夹。 - 手动补充依赖:
windeployqt有时会漏掉一些依赖,特别是你用了某些特定插件(如数据库驱动)。检查是否生成了sqldrivers文件夹,里面是否有qsqlite.dll。如果没有,需要从Qt安装目录的plugins/sqldrivers下手动复制过来。同样,确保有libgcc_s_seh-1.dll,libstdc++-6.dll,libwinpthread-1.dll(MinGW运行时库)也在目录中。 - 测试:将整个文件夹(包含
.exe和所有DLL)拷贝到一个新的、没有开发环境的电脑上,运行.exe,看是否能正常启动和操作数据库。 - 制作安装包(可选):可以使用
Inno Setup或NSIS等工具,将你的程序文件夹打包成一个漂亮的安装程序。
打包避坑指南:最大的坑就是运行时缺少DLL。一个笨但有效的方法是,在新电脑上运行你的程序,如果报错“缺少xxx.dll”,就回开发电脑上全局搜索这个dll,把它拷贝到发布目录。另外,确保数据库文件(
scores.db)的路径在代码中是相对路径(如"./data/scores.db"),并且打包时把这个空数据库或示例数据库也一起放进安装包。
7. 功能扩展与进阶思考
实现基础版本后,这个项目还有很大的扩展空间,可以作为一个持续学习的练手项目:
- 多用户与权限管理:当前是单用户。可以扩展为真正的多用户系统,在数据库增加
users表,区分admin和teacher角色。老师只能查看和修改自己所带班级的成绩,管理员拥有所有权限。 - 数据验证与业务规则:在
addStudent和updateStudent中加入更严格的验证。例如,学号不能重复,成绩必须在0-100之间,姓名不能为空等。这些规则最好在DatabaseManager层就进行校验,而不仅仅依赖UI。 - 使用模型/视图(Model/View)深度定制:用自定义模型替代
QSqlTableModel,实现更复杂的功能,比如:- 在表格中根据成绩高低显示不同颜色(通过重写
data()函数返回Qt::BackgroundRole)。 - 在表头添加复选框,实现全选/反选批量操作。
- 实现拖拽行来调整顺序(需重写
flags()和dropMimeData等函数)。
- 在表格中根据成绩高低显示不同颜色(通过重写
- 引入单元测试:为
DatabaseManager的核心函数编写单元测试(使用Qt Test框架),保证数据操作逻辑的正确性。这对于后续添加新功能、重构代码至关重要。 - 换用更现代的UI框架:尝试用Qt Quick(QML)来重写界面。QML声明式的语法和强大的动画效果,能做出更炫酷、更响应式的界面,适合对UI体验有更高要求的场景。
这个基于C++和Qt的中学生成绩管理系统,从技术上看,它串联了C++面向对象编程、Qt框架应用、SQLite数据库操作、软件工程的基本思想(模块化、分层设计)。从实践上看,它完整走过了桌面软件开发的需求分析、设计、编码、调试、打包发布的全流程。无论最终代码是几百行还是几千行,亲手把它做出来、跑起来、并且能打包给别人用的过程,带来的收获远大于阅读十篇教程。