现代Qt开发教程(新手篇)1.11——定时器
现代Qt开发教程新手篇1.11——定时器相关仓库仍然已经开源正在积极火热的建设之中欢迎各位大佬提Issue和PR链接地址https://github.com/Awesome-Embedded-Learning-Studio/Tutorial_AwesomeQt1. 前言为什么需要定时器说实话我第一次需要在程序里做「定时任务」的时候第一反应是写一个死循环里面加sleep()。结果试完就后悔了——界面卡死程序无响应任务管理器直接显示「未响应」。后来我才发现Qt 早就给我们准备了一个优雅的解决方案QTimer。它不仅能让你的程序在指定时间后执行某个任务还能周期性地重复执行而且最重要的是——它不会阻塞你的界面。想象一下这些场景你需要每秒更新一次时钟显示、需要延迟 3 秒后自动关闭提示框、需要每隔 100ms 检查一次传感器数据、需要做一个简单的动画效果。这些场景如果用sleep()或者系统原生定时器 API你会陷入跨平台兼容性的地狱。而 QTimer 把这一切都封装好了Windows、Linux、macOS 通用而且和 Qt 的事件循环完美集成。所以这一篇我们不玩虚的直接把 QTimer 的核心用法搞清楚。从最简单的单次定时到重复定时、高精度定时再到常见的坑我们都一一覆盖。2. 环境说明本文档基于 Qt 6.x 编写所有示例代码和 API 调用都已验证兼容 Qt 6.2 版本。定时器功能位于 Qt Core 模块中不需要额外链接 GUI 模块。你可以在命令行程序、GUI 程序甚至自定义的事件循环中使用它。3. 核心概念讲解3.1 QTimer 基础用法QTimer 的使用非常简单。它本质上是一个「倒计时器」倒计时结束后会发射一个timeout()信号。你只需要把这个信号连接到你的槽函数就可以实现定时执行任务。QTimer*timernewQTimer(this);// 设置定时间隔毫秒timer-setInterval(1000);// 1000 毫秒 1 秒// 连接 timeout 信号到你的槽connect(timer,QTimer::timeout,[](){qDebug()定时器触发了;});// 启动定时器timer-start();setInterval()设置的是两次触发之间的时间间隔单位是毫秒。start()启动定时器之后每隔指定的间隔timeout()信号就会被发射一次。当你不需要定时器时调用stop()即可停止。QTimer 还有一个方便的构造方式可以直接在启动时指定间隔QTimer*timernewQTimer(this);timer-start(1000);// 等价于 setInterval(1000) start()3.2 单次定时器有些时候你只需要「延迟执行一次」而不是周期性重复。QTimer 提供了setSingleShot(true)来实现这个功能QTimer*timernewQTimer(this);timer-setSingleShot(true);// 只触发一次timer-setInterval(3000);// 3 秒后触发connect(timer,QTimer::timeout,[](){qDebug()三秒后只执行这一次;});timer-start();设置单次模式后定时器只会在第一次触发后自动停止。这对于「延迟执行」的场景特别有用比如延迟关闭提示框、延迟加载资源等。Qt 还提供了一个更简洁的静态方法singleShot()非常适合一次性任务// 静态方法3 秒后执行 LambdaQTimer::singleShot(3000,[](){qDebug()三秒后执行;});// 也可以指定接收对象QTimer::singleShot(3000,this,[](){qDebug()在 this 对象的上下文中执行;});singleShot()的优势是你不需要自己管理定时器对象的生命周期Qt 会自动处理。这对于简单的延迟任务非常方便。3.3 重复定时器默认情况下QTimer 是重复触发的。这就是最常见的「周期性任务」场景QTimer*timernewQTimer(this);timer-setInterval(100);// 100 毫秒 0.1 秒intcounter0;connect(timer,QTimer::timeout,[](){counter;qDebug()Tickcounter;if(counter10){timer-stop();// 执行 10 次后停止qDebug()定时器停止;}});timer-start();重复定时器会一直运行直到你调用stop()或者定时器对象被销毁。需要注意的是定时器的间隔不是绝对精确的。如果事件循环很忙或者你的槽函数执行时间过长下一次触发可能会延迟。你可以通过isActive()检查定时器是否正在运行通过remainingTime()获取距离下一次触发的剩余时间if(timer-isActive()){qDebug()还有timer-remainingTime()毫秒触发;}3.4 高精度定时需求QTimer 的精度取决于操作系统和硬件。在大多数现代系统上精度通常在 10-20 毫秒左右。这对于一般的应用足够了但如果你需要更高精度的定时比如游戏循环、音频处理可能需要考虑其他方案。如果你需要测量时间间隔而不是定时触发应该使用QElapsedTimer而不是 QTimerQElapsedTimer elapsedTimer;elapsedTimer.start();// 执行一些操作QThread::msleep(500);qint64 elapsedelapsedTimer.elapsed();// 经过的毫秒数qDebug()经过了elapsed毫秒;// 也可以检查是否超时if(elapsedTimer.hasExpired(1000)){qDebug()已经超过 1 秒了;}QElapsedTimer是一个高精度的计时器用于测量时间间隔而不是定时触发事件。它通常用于性能测量、超时检测等场景。很多朋友会在这里搞混其实它们的区别很清楚QTimer 是「定时触发事件」会在指定时间后发射信号适合周期性任务QElapsedTimer 是「测量时间间隔」像一个秒表适合计算两个时刻之间的时间差。如果你要做一个「倒计时 10 秒」的功能应该用 QTimer因为它可以每秒触发一次更新显示而 QElapsedTimer 可以用来测量实际经过了多少时间作为辅助验证。如果你真的需要高精度的周期性定时比如游戏引擎的 60 FPS可以考虑结合QTimer和QElapsedTimerclassHighPrecisionTimer:publicQObject{Q_OBJECTpublic:HighPrecisionTimer(QObject*parentnullptr):QObject(parent){m_timer.setInterval(16);// 约 60 FPSconnect(m_timer,QTimer::timeout,this,HighPrecisionTimer::onTick);}voidstart(){m_elapsedTimer.start();m_timer.start();}privateslots:voidonTick(){qint64 elapsedm_elapsedTimer.restart();// elapsed 是上一帧到现在的实际时间// 可以用来做插值、补偿等updateFrame(elapsed);}private:QTimer m_timer;QElapsedTimer m_elapsedTimer;voidupdateFrame(qint64 deltaMs);};4. 踩坑预防清单定时器用起来很简单但有些坑真的会让你血压拉满。这里集中列几个最常见的。第一个坑是定时器对象被提前销毁。如果你在函数里new QTimer但没设置 parent函数结束后 timer 就成了野指针不但内存泄漏定时器还可能被意外销毁导致程序崩溃。正确的做法永远是new QTimer(this)让它随 parent 一起销毁或者手动管理生命周期。第二个坑是在错误的线程使用定时器。定时器属于哪个线程就在哪个线程触发。如果你在主线程创建了定时器却想在工作线程里用它那它根本不会触发或者触发后槽函数在错误的线程执行。解决办法是用moveToThread()把定时器移到目标线程或者直接在目标线程中创建。第三个坑是槽函数执行时间过长。如果你在timeout的槽函数里搞了一个耗时操作比如QThread::sleep(2)整个事件循环都会被阻塞界面卡死不说其他定时器也会变得不准。正确的做法是槽函数里只做标记耗时操作丢给工作线程去处理。第四个坑比较低级但真的很常见——忘记调用start()。你配置了间隔、连接了信号槽但就是忘了启动然后困惑为什么连接了信号槽却什么都没发生。记住配置完间隔后必须调用start()。接下来做一个代码填空练习补全以下代码实现一个每 500 毫秒打印一次计数、打印 5 次后停止的定时器QTimer*timernewQTimer(______);// 设置 parenttimer-______(500);// 设置间隔intcount0;connect(timer,QTimer::timeout,[](){count;qDebug()Count:count;if(count5){timer-______();// 停止定时器qDebug()Done!;}});timer-______();// 启动定时器提示需要填入的分别是this、setInterval、stop、start。参考答案如下QTimer*timernewQTimer(this);// 设置 parenttimer-setInterval(500);// 设置间隔intcount0;connect(timer,QTimer::timeout,[](){count;qDebug()Count:count;if(count5){timer-stop();// 停止定时器qDebug()Done!;}});timer-start();// 启动定时器再看一个调试挑战下面这段代码有什么问题为什么定时器可能不会按预期工作classMyClass:publicQObject{Q_OBJECTpublic:MyClass(){QTimer*timernewQTimer;timer-setInterval(1000);connect(timer,QTimer::timeout,this,MyClass::onTimeout);timer-start();}privateslots:voidonTimeout(){qDebug()Timeout!;}};问题出在定时器没有设置 parent会导致内存泄漏。更严重的是如果 MyClass 被销毁定时器仍然存在但this已经无效访问时会直接崩溃。正确写法是new QTimer(this)这样 MyClass 销毁时定时器也会一起被清理。5. 练习项目我们要做一个小型秒表程序功能不多但正好练手。创建一个命令行或简单 GUI 程序实现启动、暂停、重置秒表的功能每秒更新显示的当前时间格式MM:SS并且可以设置倒计时模式倒计时结束后发出提示。你的程序应该能正确响应启动、暂停、重置操作每秒准时更新显示倒计时到达零时能触发提示信号。代码结构清晰定时器管理合理没有内存泄漏或崩溃风险。提示几个方向用一个 QTimer 作为计时核心连接它的timeout信号到更新显示的槽需要维护一个「当前秒数」的状态变量暂停时停止计时器但不重置这个值倒计时模式可以在更新显示的槽里检查是否到达零QElapsedTimer 可以用来做高精度计时减少误差累积。6. 官方文档参考Qt 文档 · QTimer 类 – QTimer 的完整 API 参考包含所有信号、槽和属性说明Qt 文档 · QElapsedTimer 类 – 高精度计时器类用于测量时间间隔Qt 文档 · QObject::startTimer – 低级定时器 API直接使用定时器 ID注以上链接已通过互联网检索验证均可在 Qt 官方网站访问到这里就大功告成了。掌握了 QTimer你就可以在 Qt 程序里自由地实现各种定时任务这大大扩展了程序的交互能力和自动化程度。定时器是 Qt 开发中最常用的工具之一多练习几次你就会发现它的强大之处。下一节我们会讲插件系统看看如何让程序支持动态扩展。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2577460.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!