QCoreApplication::processEvents用法及耗时操作导致界面冻结卡死问题解决

目录

1. 函数说明

2. 需求应用场景举例

2.1. 场景1

2.2. 场景2


1. 函数说明

processEvents函数如下:

[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents)
[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int ms)

可以看到有两个重载函数,且都是QCoreApplication静态成员函数,第二个函数多了超时时间参数,基本同第一函数功能类似。其作用是:

  • 如果正在处理事件的时间超过了指定的时间限制 ms,processEvents 函数仍然会等待事件处理完成,然后返回。
  • 如果事件处理完成前的时间限制 ms 达到了,那么 processEvents 函数将立即返回,即使事件队列中仍有待处理的事件。
  • 为调用线程即调用本函数所在的线程处理所有阻塞事件,具体处理哪些类型的阻塞事件由本函数的参数flags决定。当在程序的某个耗时操作偶尔调用一下本函数,可以解决界面冻结、卡死、显示不出来的问题(见后面的例子)。

       本函数的QEventLoop::ProcessEventsFlags参数是枚举类型,各枚举值说明如下: 

枚举值具体值作用及含义

AllEvents

0x00

所有事件,但DeferredDelete被特殊处理,具体参见Qt Assist中的QObject::deleteLater()的说明

ExcludeUserInputEvents

0x01

不处理鼠标、键盘事件。注意:鼠标、键盘事件不会被丢弃,它只是不处理而已,等下次调用processEvents函数且参数不是ExcludeUserInputEvents时才处理鼠标、键盘事件

ExcludeSocketNotifiers

0x02

不处理套接字通知事件。注意:套接字通知事件不会被丢弃,它只是不处理而已,等下次调用processEvents函数且参数不是ExcludeSocketNotifiers时才处理鼠标、键盘事件

WaitForMoreEvents

0x04

如果没有阻塞事件,则一直等待

可以通过位或操作(|),将上述参数联合起来用,如: 

QEventLoop::ProcessEventsFlags(QEventLoop::AllEvents | QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);

上述代码的意思是:除了不处理键盘、鼠标、套接字通知事件外,其它事件都处理。 

2. 需求应用场景举例

2.1. 场景1

如下代码:

CQueryDataProgressDlg exportProgressDlg(this); // 进度提示框窗体
exportProgressDlg.show();
exportProgressDlg.setRange(1,100); // 进度条范围为[1, 100]
exportProgressDlg.setTip(QstringLiteral("正在导出数据,请稍候..."));
for (auto pageIndex = 0, exportProgress = 1; pageIndex < 100; ++pageIndex, ++exportProgress) // 数据共100页
{
    //QCoreApplication::processEvents();
    //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);

    // 按页导出数据库中的数据
    exportDataToLocal(pageIndex);  // 此句代码非常耗时

    // 每导出1页数据,进度条就向前推进一步
    exportProgressDlg.setPos(exportProgress);
}

     读者不必纠结上述代码的完整实现,可以将上述代码理解为伪代码,CQueryDataProgressDlg 可以理解为一个进度提示框窗体,上面的代码是按页索引(pageIndex)导出数据库中的数据到本地。其中exportDataToLocal函数负责按页索引查询数据库并将查询结果保存到本地,其非常耗时,每导出一页数据,CQueryDataProgressDlg窗体表示的进度条就前进一格,这种边执行边告知用户执行进度的需求,在现实中很常见,然而,如果注释掉或不加第7行代码,则进度条窗体压根不会显示出来,因为导出数据很耗时导致Qt的事件循环被阻塞,进度提示框窗体事件得不到执行而不显示、进度条不会更新。加入第7行代码后,进度条窗体显示如下且进度条能按预定设想的那样向前走动,且按住鼠标左键拖动窗体时,窗体能被拖动;可以尝试在该窗体加入一个编辑框,此时编辑框能接受键盘输入。

如果将第7行换成第8行,即processEvents函数的参数改为QEventLoop::ExcludeUserInputEvents,则按住鼠标左键拖动窗体时,窗体不能被拖动,即鼠标事件被屏蔽了,可以尝试在该窗体加入一个编辑框,此时编辑框不接受任何键盘输入。

2.2. 场景2

线程中有死循环,导致没有机会处理事件,执行槽函数。 

// Work类的槽函数:

// 先触发这个槽函数,里面有while循环,会卡住线程
void Worker::doWork() 
{
    qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId() << "\n";

    // 循环10次,每次1s
    int i = 1;
    while (i < 10)
    {
        i++;
        Sleep(1000);
        qDebug() << "second : " << i << " s\n";

        //QCoreApplication::processEvents();    // 
    }
}

// 再触发这个槽函数,会因为线程在做while循环,没有机会得到执行
void Worker::doWork2() 
{
    qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId();
}

----------------------------------------------------------------------------------------------------------

// Main函数中的代码
auto *worker = new Worker;
_workerThread = new QThread(this);
worker->moveToThread(_workerThread);    // worker的槽函数都在_workerThread线程中执行

connect(this, SIGNAL(operate()), worker, SLOT(doWork(int)));
connect(this, SIGNAL(operate2()), worker, SLOT(doWork2(int)));
    
// 启动线程
_workerThread->start();

emit operate();    // 先让线程中的while循环跑起来
emit operate2();   // 再触发doWork2槽函数,但是因为子线程中doWork在while循环,
                   // 此事件没有机会被处理,直至while循环结束才得到doWork2执行的机会。
结果1:
注掉QCoreApplication::processEvents(); 阻塞情况:

Worker::doWork  Thread ID:  0x4260 

second :  2  s
second :  3  s
second :  4  s
second :  5  s
second :  6  s
second :  7  s
second :  8  s
second :  9  s

Worker::doWork2  Thread ID:  0x4260
结果2:
放开注释 QCoreApplication::processEvents(); 解决阻塞问题后:

Worker::doWork  Thread ID:  0x4260 

second :  2  s

Worker::doWork2  Thread ID:  0x4260        // 执行processEvents时,如果有事件被阻塞了,立刻执行其槽函数

second :  3  s
second :  4  s
second :  5  s
second :  6  s
second :  7  s
second :  8  s
second :  9  s

场景2的代码摘自:Qt processEvents - 解决线程中事件阻塞(如槽函数被阻塞)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值