QT-——QTimer QMessageBox 线程 主线程 UI之间的联系

大家如果经常开发QT Application程序,肯定会将一些耗时的动作放到线程中,主线程再去做其它的事情,那么就会存在这样的一个问题,线程是否和主线程一样,是否可随意的调用UI?随意的创建类?鉴于这两个问题,请继续往下看。

首先说线程吧,我有一篇博客已经介绍了3中使用方式,我在这里只说信号和槽的那种,如果connect的第五个参数设为Qt::DirectConnection则槽函数处于线程中,如果是Qt::QueuedConnection则槽函数处于主线程中,如果该槽函数需要执行一些很耗时的动作,那么就必须用直连,保证在子线程中执行,这种方式就会存在一个问题,我能否访问UI,一是可以访问的,因为这个槽函数是主线程中定义的,所以它可以调用this指针,指针是关键,也就是说,想让子线程访问UI,那么就得把主Ui的指针传过去,一般是用run开启线程的话,就得弄一个全局的this指针对象,在子线程中extern声明,然后就可以访问了。队列连接,不存在该问题,因为处于主线程中,可随意访问。

我这里为啥要提QTimer QMessageBox,你们肯定遇到过这样的错误,Qtimer QMessageBox不能再另外一个线程中创建或使用,也就是说你在子线程中无法用这玩意,那么要怎么用呢,很简单,emit信号发出去,让主线程去处理。

QTimer我在提一点,它的timeout槽是处于创造它的线程中的,而且创建它和调用它的线程必须是同一个,不然就会出现不能在另外一个线程中使用,对于这个问题可通过timer::moveToThread函数来改变它的所属线程然后就可以用了,所以如果是想定时的刷新UI,并且这个刷新过程很耗时,可以采用这样的一个方式,先在主线程中开启定时器,setSingleShot设为false,表示每隔一段时间就会触发,在槽函数中,开启一个线程,然后等待线程跑完,这里可使用QEventLoop,跑完以后,关闭线程,这样即可达到线程刷新UI的目的,你们肯定会问计时器会不会没等线程跑完又触发了?我告诉你,不会,Qtimer的触发机制是上一个的槽函数运行结束,才开始算时间,触发下一次,不信你们可以去试。其实说白了这个方式就是为了开个线程,并且为这个线程绑定一个QTimer计时器,绑定的实现方式有两种,一种是自己写个类继承Qthread,然后在里面创建Qtimer,在调用Start,当Timeout时和主UI进行通讯,另外一种方式是不用继承,通过MoveToThread函数将Timer绑定到创建的线程中去,然后再通过Thread的开启来开启Timer,这样就可以,需要注意的是这里的Timeout和主线程连接需要将第五个参数改为直连,这样才能保证Timer它在子线程中,不然运行会报错,因为默认是在主线程中执行!,两种方式代码如下

1、m_timer.setSingleShot(false);
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(on_timer_solt()), Qt::DirectConnection);
    thread = new QThread();   
    m_timer.moveToThread(thread);
    connect(thread, SIGNAL(started()), &m_timer, SLOT(start()));
    thread->start();

2、class Worker :public QThread {

    Q_OBJECT

public:

    Worker(MyClass *parent);

    virtual ~Worker(){}

    void run();

    MyClass *timerReceiver;

};

Worker::Worker(MyClass *parent)

    : QThread(parent) {

    timerReceiver = parent;

}

void Worker::run()

{

    QTimer *timer = new QTimer(this);

    timer->setInterval(100);

    connect(timer, SIGNAL(timeout()), timerReceiver, SLOT(onTimeout()), Qt::DirectConnection);

    timer->start();

    exec();

    return;

}

对于上面那点,还有另外一种做法,QApplication:processEvents()这个函数,其功能和开线程作用一样,将耗时的程序进入到消息循环处理,可以防止界面阻塞(卡住)  

再提一个函数sleep(x)这个函数作用是让程序等待x毫秒,但是有个弊端就是,它会让UI未响应,在它的这段时间里,可以用一个事件循环来替换它,

  1. QEventLoop loop;

  2. QTimer::singleShot(x, &loop, SLOT(quit()));

  3. loop.exec();

UI为啥会卡,其实原理就是,比如说人视频能接受的帧率FPS为60,也就是说1000ms内画面要刷新60次才不会觉得卡,放到程式里就是每隔1000/60 =  16ms刷新一次UI,当耗时超过16ms时,看上去就会卡,刷新UI的这部分可看成一个事件循环(事件循环一般都是由exec()开启,事件循环实际上类似于一个事件队列,对列入的事件依次的进行处理,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出),耗时超过16ms时,此时需要立即处理一次事件循环,可使用processEvents函数,它的作用就是将耗时间的东西踢出来,自己一个去慢慢耗。让程式继续去执行其它的动作。

就说这么多了吧,这些小东西,还是有用的,面试官也很喜欢问。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于您提到的问题,当使用 QTimer 时,确保它在线程中是很重要的。如果 QTimer 不在线程中,它可能无法正常工作或触发相应的槽函数。 要确保 QTimer线程中运行,可以使用以下方法之一: 1. 在线程中创建 QTimer 对象:将 QTimer 对象的创建和启动放在线程中,以确保它运行在正确的上下文中。 2. 使用信号和槽机制:如果您的 QTimer 对象在另一个线程中被创建,您可以通过使用信号和槽机制将其与线程连接起来。这可以确保槽函数在线程上执行,从而避免线程不匹配的问题。 下面是一个示例代码,演示了如何在线程中使用 QTimer: ```cpp #include <QApplication> #include <QTimer> int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建 QTimer 对象 QTimer timer; // 设置定时器间隔为 1000 毫秒(1 秒) timer.setInterval(1000); // 连接定时器的超时信号到槽函数 QObject::connect(&timer, &QTimer::timeout, [](){ // 这里是定时器超时时执行的代码 // 在这里可以更新 UI 或执行其他任务 }); // 启动定时器 timer.start(); return app.exec(); } ``` 请注意,在上述示例代码中,QTimer 对象是在线程中创建的,并且超时信号连接到了一个匿名 lambda 表达式的槽函数中。这个槽函数将在定时器超时时执行相应的操作。 希望这可以帮助到您!如果您还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值