☰
用Qt打造个人日程管理工具:从需求到打包发布的完整实践
2026/10/21 7:36:21 网站建设 项目流程

简介:这是一套基于Qt开发的个人日程安排处理项目源码,面向具备C++基础、希望掌握Qt桌面应用开发的读者,演示了日程创建、分类管理、日期筛选、定时提醒等完整事务流程。项目综合运用QCalendarWidget日历组件、QDateTimeEdit时间编辑、QTableView模型视图、QTimer定时器以及SQLite数据持久化等Qt核心技术,能够帮助学习者理解GUI与业务逻辑分离的工程思想。资源共89个文件,包含15个cpp和15个h源文件、6个ui界面设计文件、11个dat日程数据文件、pro/sln工程配置文件以及编译好的exe可执行程序,压缩包整体仅10.83MB,结构完整便于按模块研读。目前已有468人学习参考。借助这套源码,可以快速搭建一个功能可用的个人日程工具,并对照实际代码掌握界面布局、信号槽联动、数据读写和提醒触发的实现细节,非常适合作为Qt课程设计、毕业设计或入门实战的参考范本。 最近在折腾个人时间管理,索性用 Qt 自己写了一个“个人日程安排处理事务”的小工具。起因很实在,市面上日程软件功能越来越重,数据还要同步到云端,我就想在本地快速记几件事、到点弹个提醒、按周看看自己的时间花在哪,顺手还能把 Qt 这些年攒的经验用上。Qt 做这类桌面工具确实顺手,C++ 原生性能、Widgets 组件成熟、跨平台编译方便,后期想加网络同步、串口联动也不缺库。这篇文章把整个实现过程、环境搭建、打包发布的细节,包括踩过的坑都完整记录下来,给正准备用 Qt 做类似小工具的朋友当个参考。

1. 先想清楚做什么:个人日程工具的需求边界

1.1 核心需求与功能拆分

个人日程安排处理事务,听起来范围很大,但真正常用的功能其实就三块:日程录入与编辑、到点提醒、按时间维度查看。我在动手前先把需求按“必须做、可以做、先不做”分类,避免一上来就把项目做膨胀。

必须做的有:日程的增删改查、日期时间选择、一次性/重复日程区分、到点弹窗提醒、本地数据保存。可以但有难度的有:周视图时间轴图形化、托盘常驻、QSS 美化界面。先不做的有:多人协作、日历订阅、跨设备云同步、语音助手这类偏重的功能。

这个分类很重要。很多人写个人工具失败,不是因为代码写不好,而是需求清单失控。一个最小可用版本,应该在一周内能写出核心闭环,而不是花一个月把所有想法都塞进第一版。

1.2 技术选型:为什么是 Qt Widgets 而不是其他方案

在选型上我也犹豫过。Electron 虽然界面好写,但启动速度和内存占用对一个小工具来说太奢侈;C# WPF 只在 Windows 上舒服,换到 Linux 又得重来;Qt Widgets 与 QML 之间,我最终选了 Widgets,原因是它写传统表单类界面最直接,日历控件、日期时间选择器都有现成封装,路由和状态管理也不需要像 QML 那样额外维护一套。

Qt 5.15.2 是这次的主力版本。原因很现实:Qt 6 对第三方组件的兼容还在过渡期,而 5.15.2 属于 LTS 分支,QCustomPlot 这类常用绘图库的适配非常成熟,网上资料多,遇到问题能搜到解决方案。编译器方面我用 MinGW 64-bit 做日常开发,发布时再切换到 MSVC 版本做兼容性验证。

2. Qt 版本选择与环境搭建的实操记录

2.1 版本与编译器组合怎么定

如果你也打算做桌面小工具,我的建议是 5.15.2 配 MinGW 起步。MinGW 工具链的好处是开源、免配置、Qt 官方在线安装包自带,不需要额外装 Visual Studio。等你要发布给其他 Windows 用户,或者要接某些只提供 MSVC 版 SDK 的第三方库时,再装一套 MSVC 2019 编译器也不迟。

另一个常被忽略的问题是编译器位数。现在绝大多数 Windows 是 64 位系统,但某些老库只有 32 位版本。做个人工具建议统一用 64 位,省得后续接库时遇到 ABI 不匹配的尴尬。顺带说一句,Qt 5.12.12 也是个稳妥选择,如果你的目标机器上有老系统兼容性要求,可以两个版本都装上,用 Qt Creator 的 Kit 切换即可。

2.2 国内镜像下载与安装组件选择

Qt 官方下载页面访问速度不稳定,我通常走清华镜像,地址格式是 https://mirrors.tuna.tsinghua.edu.cn/qt/online_installers/ 下的在线安装器,或者直接下载 archive 里的离线包。离线包的好处是安装速度快、失败率低,推荐下载 qt-opensource-windows-x86-64-5.15.2.exe 这类完整安装包。

安装时组件按需勾选,不要全选。我这次只勾了 Qt 5.15.2 下的 MinGW 8.1.0 64-bit、Qt Debugger 和 Qt Creator,外加 Sources 源码包,总占用大概 3GB 左右。源码包建议勾上,调试时按 F2 跳进 Qt 源码看实现非常有用,没有源码你连项目符号都看不了。

安装完成后,在 Qt Creator 里确认套件,注意看“Qt version”和“Compiler”是否都识别成功。经常有人卡在这里,编译器路径没配好,或者 qmake 版本号还是一串红色警告,后面编译必出问题。

3. 工程搭建与界面布局设计

3.1 新建工程与 UI 方式的选择

Qt Creator 里新建 Qt Widgets Application,类名 MainWindow,基类选 QMainWindow。UI 方面,我这次是 Qt Designer 拖控件和手写布局混着来:简单的按钮、列表用 Designer 拖,动态更新的区域手写代码。

说实话,像日程管理这种中小型界面,直接手写布局反而更清晰。Designer 生成的 ui_mainwindow.h 每次编译都重新生成,你要动态增删组件时,还得回到 .ui 文件里去改。不过 Designer 在摆静态控件时确实快,两种方式配合效率最高。如果你用 VS Code 配 Qt Designer,也是同样思路,.ui 文件最后还是要通过 uic 转成头文件编进项目。

3.2 主界面布局与信号槽连接

主界面我做了左右分栏:左边是 QCalendarWidget 日历控件,右边是 QListWidget 日程列表,下面一排按钮负责新增、编辑、删除。分栏用 QSplitter 包起来,用户能拖动比例,体验比固定布局好很多。

QSplitter *splitter = new QSplitter(this); splitter->addWidget(calendarWidget); splitter->addWidget(listWidget); splitter->setStretchFactor(0, 1); splitter->setStretchFactor(1, 2); setCentralWidget(splitter);

这里最容易忽略的是日历选择日期变化时要刷新列表,信号槽连接写不完整,就会出现翻日历没反应、点按钮没弹窗之类的“假死”现象。我习惯把刷新动作抽成一个 refreshList() 函数,日历选中日期、增删改完成、窗口重新显示时统一调用,这样逻辑只维护一处。

4. 核心功能实现:数据、提醒与持久化

4.1 日程数据结构与 JSON 存取

日程数据我用结构体保存在内存里,字段包括 id、标题、开始时间、是否重复、是否已提醒。因为个人工具数据量很小,不引入数据库,直接用 JSON 文件持久化。JSON 的好处是纯文本、可读、改起来方便,用 QJsonDocument 读写也就几行代码。

struct Schedule { int id; QString title; QDateTime startTime; int repeatType; // 0: 不重复, 1: 每天, 2: 每周 bool reminded; };

保存和加载的核心逻辑很简单,但要留意 QDateTime 转字符串时用 Qt::ISODate 格式,否则时间解析容易出时区偏差。中文环境下的日期格式最好也统一用 ISO 格式存入文件,界面展示时再通过 QLocale::toString 转成“2025-01-16 09:30”这种显示格式,避免乱码。

QJsonArray arr; for (const auto &s : scheduleList) { QJsonObject obj; obj.insert("id", s.id); obj.insert("title", s.title); obj.insert("startTime", s.startTime.toString(Qt::ISODate)); obj.insert("repeatType", s.repeatType); obj.insert("reminded", s.reminded); arr.append(obj); } QFile file("schedule.json"); if (file.open(QIODevice::WriteOnly)) file.write(QJsonDocument(arr).toJson(QJsonDocument::Indented));

4.2 重复日程的处理逻辑

重复日程是这次开发里最容易绕晕的地方。“每天重复”“每周重复”听起来简单,但对“到点是否提醒”影响很大。我的处理方式是:新增日程时选了重复类型,则只存一条基础日程,判断提醒时用日期差值取模来判断今天是否属于重复日。

比如一个“每周一晨会”的日程,startTime 存的是下周一 09:00,repeatType 为 2。检查时先算出今天相对 startTime 过了几天,如果对 7 取模等于 0,就认为是重复触发的日子,再把提醒时间临时算到今天的 09:00。这个逻辑还要处理“上周期已提醒过”的状态,所以我专门加了一个 remindedDate 字段,记录最后一次提醒的日期,避免同一个日程当天被提醒好几次。

4.3 提醒机制与托盘常驻

提醒我用的方案是 QTimer 定时轮询,每 30 秒检查一次当前时间,判断有没有日程落在提醒窗口内。Qtimer 不精确,但对分钟级提醒完全够用。真到秒级任务,那就得上多线程定时器了,个人日程场景没必要。

QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::checkRemind); timer->start(30000); void MainWindow::checkRemind() { QDateTime now = QDateTime::currentDateTime(); for (auto &s : scheduleList) { if (!s.reminded && s.startTime <= now && s.startTime.addSecs(15 * 60) >= now) { s.reminded = true; QMessageBox::information(this, "日程提醒", s.title + "\n" + s.startTime.toString("yyyy-MM-dd hh:mm")); } } }

另外我加了 QSystemTrayIcon 托盘图标,关闭主窗口时最小化到托盘而不是退出程序。这里有个小坑:托盘菜单的“退出”按钮要记得调用 qApp->quit(),而不是直接 close() 主窗口,否则任务管理器里进程还在,点击托盘图标又拉不起窗口。

5. 日程可视化的图形实现

5.1 为什么引入 QCustomPlot

文字列表虽然清楚,但一周的日程有没有分布不均、哪天最忙光看列表很难直观感知。我决定加一个“一周日程分布”视图,按星期统计每天日程数量,画成柱状图。绘图库选了 QCustomPlot,它开源、轻量、qmake 直接加源码文件就能用,不像 QChart 那样需要额外模块授权。

QCustomPlot 其实不仅能画柱状图,时域波形转频域曲线、实时数据流绘制都能做。我做日程管理时只用了它的基础图表能力,但它的 QCPGraph、QCPBar、QCPFinancial 等接口都是同一套坐标系逻辑,理解一个就通了大半。

5.2 QCustomPlot 集成与柱状图实现

集成 QCustomPlot 只需要两步:把 qcustomplot.h 和 qcustomplot.cpp 放进工程,然后在 .pro 文件里加上 printsupport 模块。版本我用的是 2.1.1,官方源码包直接塞进项目相对路径,不污染 Qt 安装目录,团队协作也方便。

QT += core gui widgets printsupport INCLUDEPATH += $$PWD/third_party/qcustomplot SOURCES += $$PWD/third_party/qcustomplot/qcustomplot.cpp

绘制柱状图时,横轴是周一到周日,纵轴是当天日程数量。QCPBars 接受两个 QVector,一个放横坐标刻度,一个放纵坐标数值,代码非常简洁。

QVector<double> days, counts; for (int d = 1; d <= 7; ++d) { days.append(d); counts.append(countByWeekday(d)); } QCPBars *bars = new QCPBars(customplot->xAxis, customplot->yAxis); bars->setData(days, counts); customplot->xAxis->setRange(0, 8); customplot->yAxis->setRange(0, maxCount + 1); customplot->xAxis->setLabel("星期"); customplot->yAxis->setLabel("日程数量"); customplot->replot();

5.3 展示密度之外的扩展思路

柱状图只是入门,后面我还想加一个按时间段展示的甘特图样式:横轴是 24 小时,纵轴是星期,每个日程画成一个色块。实现方式可以直接用 QCPFinancial 的蜡烛图接口魔改,也可以干脆重写 paintEvent 在 QWidget 上画矩形,前者省事,后者可控性更高。

如果你做的是信号处理相关项目,QCustomPlot 画完时域波形后,还能接 kissfft 这类 FFT 库把波形转换到频域再画曲线,原理都是把数据 setData 到 QCPGraph 上,只是数据来源从时间轴换成频率轴。绘图组件本身不挑数据,挑的是你给它的坐标系和数据维度。

6. 打包发布与常见报错排查

6.1 windeployqt 打包完整步骤

个人工具最终要发给别人用,打包发布这一步跑不掉。Qt 程序不能直接把 exe 拷走就完事,运行时会依赖 Qt5Core.dll、Qt5Gui.dll 等一堆动态库,还要求 plugins 目录里有对应平台插件。

正确做法是先用 Release 模式编译,然后在 Qt 命令行环境里执行:

cd /d 你的exe目录 windeployqt 日程安排.exe

windeployqt 会自动扫描 exe 依赖的 Qt 模块,把需要的 DLL、插件、翻译文件拷贝到同目录。执行完一定要检查 platforms 目录下有没有 qwindows.dll,这个文件缺失或版本不对,就是后面要说的那个经典报错。

6.2 “no Qt platform plugin could be initialized” 排查

“windows no qt platform plugin could be initialized” 这个报错我遇到过两次,一次是忘了执行 windeployqt,一次是从 5.15.2 的程序里手工拷 DLL 到另一个安装着 Qt 6 的机器上。原因都一样:程序找不到 platforms/qwindows.dll,或者找到的插件和 Qt 库版本不匹配。

解决办法分三步:确认 exe 同级目录存在 platforms/qwindows.dll;确认 qwindows.dll 与 exe 使用同一个 Qt 版本和编译器位数编译;最后用 Process Explorer 或 windeployqt 的 verbose 输出检查 DLL 搜索路径,避免系统里另一个 Qt 目录干扰加载顺序。Debug 和 Release 的依赖库绝不混用,这是最容易被忽略的坑。

6.3 跨平台发布的补充

如果你要把工具跑到 Linux 上,记得 Linux 下没有 windeployqt 的完全等价物,可以用 linuxdeployqt 打包 AppImage,或者直接 qmake 加 make 编出可执行文件后,写一个启动脚本设置 LD_LIBRARY_PATH 指向 Qt 安装目录的 lib 子目录。寻找 Qt 安装路径可以用

qmake -query QT_INSTALL_PREFIX

这个命令也会用在 CMake 脚本里自动拼接第三方库路径。麒麟这类国产 Linux 发行版装 Qt 也遵循同样逻辑,离线安装包解压后手动指定 qmake 路径即可。

7. 开发中的崩溃问题与调试技巧

7.1 常见崩溃场景复盘

开发两周,崩溃集中在三个地方:QListWidget 删除项后继续访问旧 item 指针、JSON 解析失败时 QJsonObject 取值直接崩、lambda 里捕获 this 后窗口被销毁。第一个是典型悬空指针,解决办法是删除前先 disconnect 相关信号,删除后立即置空指针。

JSON 解析的问题也很有代表性,很多崩溃不是代码逻辑错,而是数据文件被人为改坏或者版本升级后字段名对不上。现在我的 load 函数开头就判断 QJsonDocument::fromJson 的 error 返回值,并在界面打印错误位置,排查速度提升了一大截。

7.2 Qt Creator 与 VS Code 的调试取舍

调试工具我主力用 Qt Creator,断点、调用栈、局部变量查看都够用。有人喜欢 VS Code,装上 Qt 插件也能配置 Qt Designer 和 qmake,但 VS Code 的 CMake 配置对新手不太友好,尤其当项目里混合了 .ui 文件和 qrc 资源时,调试配置容易出错。2024 年的实际体验来看,Qt Creator 在 Qt 项目上的开箱即用程度仍然明显领先。

配置环境时还有个小技巧:国内镜像源除了装包,也可以配置 Qt 在线安装器的镜像加速。命令行里加上 --mirror 参数,比如指向清华镜像的 qt 路径,下载速度能快不少。

7.3 共享 pri 文件与工程组织

多个小工具共有一套公共代码时,我把它抽成一个 common.pri,在 .pro 里 include 进来,统一维护编译选项和第三方库路径。举例来说,几个项目都要用 QCustomPlot 和 QSerialPort,pri 里就集中写:

QT += serialport printsupport INCLUDEPATH += $$PWD/../third_party SOURCES += $$PWD/../third_party/qcustomplot/qcustomplot.cpp

这样每个子项目的 .pro 文件就只剩应用本身的源文件列表,干净也容易维护。

8. 后续扩展方向与开发体会

8.1 网络同步与外部设备联动

日程数据如果能跨设备同步,实用性会提高一截。Qt 里用 QNetworkAccessManager 发请求时要注意哪几个要点,正好踩过一个坑:POST 请求如果忘了设置 Content-Type 头,服务端可能直接回 405 “Request method 'POST' not supported”。另外 GET 和 POST 的用法差异很大,个人数据提交用 POST,不要把敏感信息挂到 URL 参数上。

串口联动也可以玩一下,比如日程到点后通过 QSerialPort 给外部设备发指令,或者读取门禁、打卡数据自动生成日程事件。Qt 的串口模块在 Qt Serial Port 里,配置波特率、数据位、停止位之后 open 就能收数据,但要注意不同平台的串口名称规则不一样,Windows 是 COM3,Linux 是 /dev/ttyUSB0。

8.2 第三方库调用与多语言支持

如果要接 Halcon 做日程卡片 OCR 识别,思路和接 QCustomPlot 类似:把 halcon 的 include 和 lib 路径加进 .pro,头文件 extern “C” 包好就能调用。Qt 程序多语言发布用 Qt Linguist 生成 .qm 文件,再通过 QTranslator 在启动时加载,界面文案就能动态切换。

8.3 开发过程中的一点体会

这次改造最大的收获,并不是代码量增长了多少,而是我意识到做个人工具要克制。每次想加新功能时先问自己:这个功能是不是最小闭环必需的?不是,就先记在 TODO 里。我第一版塞了月视图、统计报表、主题换肤,结果核心提醒功能反而拖到第二周才跑通。

另外建议尽早把 QCustomPlot 这类绘制组件引入项目,哪怕第一版只是简单画个柱状图,因为它会反向推动你的数据结构设计。为了让图表展示方便,我把日程数据从原来的散乱变量梳理成了统一的 QList 结构,数据模型清晰之后,列表、编辑器、提醒检查都跟着顺畅了。这个项目目前还在持续迭代,下一步准备把周时间轴的色块图完成,再把同步功能加上。如果你也在做类似的桌面工具,这几个方案可以直接抄,遇到具体问题欢迎一起聊。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询