QT延时/等待/定时器怎么写?大概分为两类,一个是非阻塞延时,一个是阻塞延时,这里做一个小结,第三种耗时方法用的较少
一、阻塞型延时
Qt一般使用QThread::sleep()来延时,但是这样会阻塞住线程,体验太差,这样只可以用在子线程中。
QThread::msleep(50);//阻塞延时50ms
或者使用定时器:
void Delay_MSec_Suspend(unsigned int msec) {
QTime _Timer = QTime::currentTime().addMSecs(msec); while( QTime::currentTime() < _Timer ); }
二、非阻塞型延时
在等待中,不断强制进入当前线程的事件循环,这样可以把堵塞的事件都处理掉,从而避免程序卡死
可以处理本线程的事件循环
void Widget::sleep(unsigned int msec) {
QTime dieTime = QTime::currentTime().addMSecs(msec); while( QTime::currentTime() < dieTime ) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100); //这条语句能够使程序在while等待期间,去处理一下本线程的事件循环,处理事件循环最多100ms必须返回本语句, //如果提前处理完毕,则立即返回这条语句。这也就导致了该Delay_MSec函数的定时误差可能高达100ms。 } }
或者使用子事件循环,在子事件循环中,父事件循环仍然是可以执行的
void Delay_MSec(unsigned int msec) {
QEventLoop loop;//定义一个新的事件循环 QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数 loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出 }
三、耗时代码
例如后台需要载入图片,GUI响应太卡,可以使用这种方法
for(i=0; i < ; i++) {
//QCoreApplication::processEvents(QEventLoop::AllEvents); //去处理本线程的事件循环,避免本线程被堵塞 QCoreApplication::processEvents(QEventLoop::AllEvents, 5); //如果不够频繁,可以增加第二参数来缓解卡顿 for(j=0; j < ; j++) {
//QCoreApplication::processEvents(QEventLoop::AllEvents); //处理事件循环,不建议放在这里,可能过于频繁 doSomeThing(); } }
一般来说,processEvents()不宜被调用的过于频繁,也不宜被调用的不够频繁。过于频繁的话,一方面会使线程的响应更好,但另一方面会导致原本就耗时的任务变得更加耗时;不够频繁的话,显然可能会使GUI线程的响应变差,例如每500ms才被调用一次,那么GUI的事件循环就只能500ms才被处理一次,当然,这个问题可以通过设定processEvents()的第二个形参略微得到缓解,更好的做法是,保证被调的周期<200ms(再小一些更好,看程序需求),这样不至于肉眼可见的卡顿。
副作用:(特别注意!)
1、在点击按钮之后,这个20s的耗时任务开始执行,尚未执行完毕时,我们点击了GUI的关闭按钮,那么GUI会立即消失,但是这个耗时任务仍然会在后台执行,直到执行完毕,进程才会退出。解决办法:重写关闭事件,在关闭事件的函数中直接结束进程。
2、在点击按钮之后,这个20s的耗时任务开始执行,执行到第5秒时,我们再次点击了这个按钮,那么QT又会执行一个新的20s任务,这个新任务完成后,又会接着把第一个20s任务从上次被打断的第5秒继续执行。如果这个任务是可重入的,后果仅仅是被执行了两遍,如果任务不可重入,那情况就彻底糟糕了。解决办法:点击按钮后把这个按钮disable掉,执行完再enable
定时器的两种方法
一、QObject中的定时器的使用
1、 int QObject::startTimer ( int interval ) ;
这个是开启一个定时器的函数,他的参数interval是毫秒级别。当开启成功后会返回这个定时器的ID, 并且每隔interval 时间后会进入timerEvent 函数。直到定时器被杀死。
2、 void QObject::timerEvent ( QTimerEvent * event );
当定时器超时后,会进入该事件timerEvent函数,需要重写timerEvent函数,在函数中通过判断event->timerId()来确定定时器,然后执行某个定时器的超时函数。
3、 void QObject::killTimer ( int id );
通过从startTimer返回的ID传入killTimer 函数中杀死定时器,结束定时器进入超时处理。
以下是QObject中的定时器具体使用简单例子:
#define _MYTIMER_H #include <QObject> class MyTimer : public QObject {
Q_OBJECT public: MyTimer(QObject* parent = NULL); ~MyTimer(); void handleTimeout(); //超时处理函数 virtual void timerEvent( QTimerEvent *event); private: int m_nTimerID; }; #endif //_MYTIMER_H
#include "mytimer.h" #include<QDebug> #include <QTimerEvent> #define TIMER_TIMEOUT (5*1000) MyTimer::MyTimer(QObject *parent) :QObject(parent) {
m_nTimerID = this->startTimer(TIMER_TIMEOUT); } MyTimer::~MyTimer() {
} void MyTimer::timerEvent(QTimerEvent *event) {
if(event->timerId() == m_nTimerID){
handleTimeout(); } } void MyTimer::handleTimeout() {
qDebug()<<"Enter timeout processing function\n"; killTimer(m_nTimerID); }
二:使用QTimer定时器类
1、 首先创建一个定时器类的对象
QTimer *timer = new QTimer(this);
2、 timer 超时后会发出timeout()信号,所以在创建好定时器对象后给其建立信号与槽
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
3、 在需要开启定时器的地方调用void QTimer::start ( int msec );
这个start函数参数也是毫秒级别;
timer->start(msec );
4、 在自己的超时槽函数里面做超时处理。
以下是QTimer定时器类具体使用简单例子:
#ifndef _MYTIMER_H #define _MYTIMER_H #include <QObject> class QTimer; class MyTimer : public QObject {
Q_OBJECT public: MyTimer(QObject* parent = NULL); ~MyTimer(); public slots: void handleTimeout(); //超时处理函数 private: QTimer *m_pTimer; }; #endif //_MYTIMER_H
#include "mytimer.h" #include<QDebug> #include <QTimer> #define TIMER_TIMEOUT (5*1000) MyTimer::MyTimer(QObject *parent) :QObject(parent) {
m_pTimer = new QTimer(this); connect(m_pTimer, SIGNAL(timeout()), this, SLOT(handleTimeout())); m_pTimer->start(TIMER_TIMEOUT); } MyTimer::~MyTimer() {
} void MyTimer::handleTimeout() {
qDebug()<<"Enter timeout processing function\n"; if(m_pTimer->isActive()){
m_pTimer->stop(); } }