任务管理器这类工具功能清晰、需求稳定,适合作为 Qt 入门到进阶的练手项目。本文版本包含四项核心能力:
明确边界很重要。很多 Qt 项目一开始就追求“大而全”,结果界面逻辑和数据逻辑纠缠在一起,后期难以维护。本文坚持一个原则:界面只负责展示和交互,数据由独立模型管理。
建议使用 CMake 管理构建,Qt 6 为当前主流版本。一个清晰的小项目结构可以这样组织:
taskmanager/
├── CMakeLists.txt
├── main.cpp
├── mainwindow.h/.cpp
├── taskmodel.h/.cpp
└── taskrepository.h/.cppmainwindow 负责界面,taskmodel 继承 QAbstractTableModel 承载数据,taskrepository 封装 SQLite 读写。这样拆分后,界面替换或存储方式变更都不会牵动全局。CMake 中只需声明 find_package(Qt6 REQUIRED COMPONENTS Widgets Sql),并把源文件加入可执行目标即可。
Qt 的两大基石是信号槽和 Model/View。信号槽负责对象间解耦通信,Model/View 则把数据和视图分离,避免手动维护大量控件。任务列表天然适合用 QTableView 配合自定义模型展示,而不是逐条创建 QTableWidgetItem。
下面是一个精简的模型片段,展示如何让视图自动感知数据变化:
class TaskModel : public QAbstractTableModel {
Q_OBJECT
public:
int rowCount(const QModelIndex& = {}) const override { return m_tasks.size(); }
int columnCount(const QModelIndex& = {}) const override { return 3; }
QVariant data(const QModelIndex &idx, int role) const override {
if (role != Qt::DisplayRole) return {};
const auto &t = m_tasks[idx.row()];
return idx.column() == 0 ? t.title
: idx.column() == 1 ? (t.done ? "已完成" : "待办")
: t.createdAt;
}
void addTask(const Task &t) {
beginInsertRows({}, m_tasks.size(), m_tasks.size());
m_tasks.append(t);
endInsertRows();
}
private:
QList<Task> m_tasks;
};关键点在于 beginInsertRows / endInsertRows 成对调用——这是 Model/View 的契约,视图依赖它们刷新。若省略,界面不会更新,这是初学者最常见的坑之一。信号槽方面,工具栏的“添加”按钮通过 connect 连接到主窗口槽函数,再由槽函数更新模型,界面无需知道数据从哪来。
本地工具不必上重型数据库,Qt 自带的 SQLite 驱动足够。taskrepository 中封装建表和增删改查,主窗口只调用仓库接口。一个建表与插入的示例如下:
bool TaskRepository::add(const Task &t) {
QSqlQuery q(m_db);
q.prepare("INSERT INTO tasks(title, done, created_at) VALUES(?,?,?)");
q.addBindValue(t.title);
q.addBindValue(t.done ? 1 : 0);
q.addBindValue(t.createdAt);
return q.exec();
}务必使用 prepare 加 addBindValue 的参数绑定方式,而不是字符串拼接,既能防注入,也能正确处理引号和特殊字符。数据库文件放在 QStandardPaths::AppDataLocation 下,避免写死路径导致跨平台失败。
开发完成后,发布环节常被低估。Windows 上可用 windeployqt 自动收集 Qt 运行库;macOS 使用 macdeployqt 生成 .app;Linux 则需确认 libQt6Widgets、libQt6Sql 及 SQLite 插件随包分发。建议在 CI 中分别构建三平台产物,避免“本机可运行、换机就缺 DLL”的尴尬。此外,Qt 6 默认使用高 DPI 缩放,发布前应在不同缩放比例下检查布局是否错位。
第一,不要在主线程做耗时操作。文件扫描、网络请求应放入 QThread 或 QtConcurrent,否则界面会卡死。第二,注意对象生命周期。Qt 的父子对象机制能自动释放子对象,但跨线程传递的对象需谨慎。第三,界面与数据解耦。把业务逻辑写进按钮槽函数是坏习惯,应下沉到模型或服务层。第四,尽早建立日志与异常处理,qInstallMessageHandler 可以把 Qt 日志统一收集。第五,用 qmake 还是 CMake:新项目优先 CMake,Qt 6 已全面转向 CMake 生态。
C++/Qt 项目的价值不在于堆砌控件,而在于清晰的分层:界面负责交互,模型负责数据,仓库负责持久化,信号槽负责串联。本文的任务管理器虽然简单,却覆盖了 Qt 桌面开发的主要骨架——Model/View、信号槽、SQLite 和跨平台发布。把最小闭环跑通后,再逐步加入撤销重做、标签分类、导入导出和自动更新,项目就会自然生长。对想深入 Qt 的开发者来说,先写出一个“能长期用下去”的小工具,比追求一个庞大框架更有意义。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。