问题分析
最近在使用QT的时候发现了某些问题,查阅资料最后总结一下。我起初是想用QT在界面还在加载时加载一副动画,然后动画下面有加载的滚动条代表时间,由于测试所以界面加载没写很多东西很快就加载完成了。我就想让他加载慢点我看看效果。所以我想没过1秒就让滚动条进度加1。
问题解决
我定义一个QTimer变量让他在一边计时,每过多久就给我传过来一个数据。由于QSplashScreen在窗口初始化完成后就会消失,所以我不得不在住线程里面用个while让他在主窗口显示前阻塞在那里。这样的话就得开一个线程让QTimer进行计时。然后就逐步调入坑中…。通过qDebug() << "Thread_Time: " << QThread::currentThreadId();
逐步打印线程id最终发现只有run()
中的操作才在新的线程中。所以我在run()
中加入如下代码:
void myThread::run()
{ QTimer* timer = new QTimer();qDebug() << "run() : " << QThread::currentThreadId();timer->setInterval(10);connect(timer, SIGNAL(timeout()), this, SLOT(StartTime()),Qt::DirectConnection);//Qt::DirectConnection 重要timer->start();exec();//重要return;
}
void myThread::StartTime()
{ if (indx > 100){quit();return;}QMutexLocker locker(&mutex);indx++;emit TimeChanage(indx);qDebug() << "myThread::StartTime() : " << QThread::currentThreadId();
}
在connect
中第五个参数不能使用默认的,因为信号的发送者和接收者不在同一个线程中。void myThread::StartTime()
还在父线程中而run()
中的内容在新的线程中,所以要用Qt::DirectConnection
,你可以理解为他的作用是当接收到信号时,他就直接调用槽函数,不需要进行事件的循环。如果用默认的话(由于发送者和接收者不在一个线程中)那么他会匹配使用Qt::QueuedConnection,这样的后果是槽函数不会立刻被调用,他会进入主程序的事件循环中,由于我主程序已经阻塞了所以他永远都掉不了槽函数。并且QTimer的运行依赖于事件循环,所以新的线程中要自己手动的开启事件循环exec()。
QTimer事件循环的理解
QTimer的运行依赖于事件循环,计时器不会自行启动线程或事件循环。在没有事件循环的情况下,它不会做任何事情
。调用QTimer::start()后仅仅是在系统的定时器向量表中添加了一个定时器对象,但定时器并没有真正开启。使用start()方法,您只是告诉QTimer,当事件循环存在时,必须启动QTimer的逻辑,如果没有事件循环,则QTimer将无法工作。
定时器的开启需要通过processEvent()开始的一系列调用后才会真正得开启,这个过程中会处理定时器向量表中所有的定时器对象。那么实际exec()中也是在不断地调用processEvent()方法。
最终效果
下面这个滚动条会一秒增加一格,由于我主线程阻塞了所以他在加载的过程中点不了其他的东西,不过在实际的运用中不会这样写,在实际的运用中先获去需要初始化的窗口数然后利用for循环和QDateTime::currentDateTime()
来实现滚动条的加载。https://github.com/xu6666/QTimer-.git
结论
QTimer的运行依赖于事件循环,上面出现的问题是我自己写一个deom遇到的,在正常的项目中不会出现这种问题。
后续我会将源码贴上。