QEventLoop的高阶使用——在UI线程使用时过滤用户输入事件

需求:        

在网络编程中,往往有这种需求,给服务器发送命令1,然后等待服务器应答命令1,然后根据应答的结果判断是否需要发送命令2,命令3.....要求不卡顿界面。

伪代码:
bool Test::send()
{
    //发送命令1
    int ret = syncSendData(command_1);  //向服务器发送command_1
    if(ret != 0)
    {
        return false;
    }

    // 命令1收到服务器的正确应答,发送命令2
    ret = syncSendData(command_2); 
    if(ret != 0)
    {
        return false;
    }

    // 命令2收到服务器的应答,发送命令3
    ret = syncSendData(command_3); 
    if(ret != 0)
    {
        return false;
    }
        .
        .
        .
    syncSendData(command_n); 
}

QEventLoop解读:

为了实现如上目的,我们想到用QEventLoop的事件循环功能。使用 QEventLoop 的事件循环能使UI界面正常响应,不会出现卡住的现象。但是 QEventLoop 美中不足的地方是,在调用exec()的时候,没有办法过滤掉用户的输入事件,往往导致槽函数重入。来看看函数exec可传入的参数:


QEventLoop::AllEvents:
All events. Note that DeferredDelete events are processed specially. See QObject::deleteLater() for more details.

QEventLoop::ExcludeUserInputEvents:
Do not process user input events, such as ButtonPress and KeyPress. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeUserInputEvents flag.

QEventLoop::ExcludeSocketNotifiers:
Do not process socket notifier events. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeSocketNotifiers flag.

QEventLoop::WaitForMoreEvents:
Wait for events if no pending events are available.

1)默认参数是QEventLoop::AllEvents,处理所有的事件,容易导致函数重入,用户连续多次点击按钮的话,则响应多次;

2)传QEventLoop::ExcludeUserInputEvents参数来避免函数重入,但请看官方解释:


QEventLoop::ExcludeUserInputEvents
Do not process user input events, such as ButtonPress and KeyPress. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeUserInputEvents flag.
 

 看到了没,用户输入的事件并不会被丢弃,而是等到QEventLoop 退出后再响应。按钮依然响应多次,只是响应的时间推迟。

实现过滤用户输入事件:

既然QEventLoop 没有提供在exec()退出前过滤掉用户的输入事件的参数,只能自己实现了,思路:继承QEventLoop,若在UI线程使用,则为qApp安装事件过滤器,在exec()退出之前过滤掉所有的用户的输入事件。

头文件:

#pragma once

#include <QEventLoop>
#include <QEvent>
#include <QThread>

class EventLoopFilter : public QEventLoop
{
    Q_OBJECT

public:
    EventLoopFilter(QObject* parent = nullptr);
    ~EventLoopFilter();
    int exec();
    void deleteLater();
    virtual bool eventFilter(QObject* watched, QEvent* event);

private:
    bool m_uiThread = false;
};

实现文件:

#include "stdafx.h"
#include "EventLoopFilter.h"
#include <QCoreApplication>
#include "QsLog.h"

EventLoopFilter::EventLoopFilter(QObject* parent) : QEventLoop(parent)
{
}

EventLoopFilter::~EventLoopFilter()
{
}

int EventLoopFilter::exec()
{
    if (qApp->thread() == QThread::currentThread()) //UI线程
    {
        qApp->installEventFilter(this);
        m_uiThread = true;
    }

    return QEventLoop::exec(QEventLoop::AllEvents);
}

void EventLoopFilter::deleteLater()
{
    if (m_uiThread)
    {
        qApp->removeEventFilter(this);
    }
    return QEventLoop::deleteLater();
}

bool EventLoopFilter::eventFilter(QObject* watched, QEvent* event)
{
    //过滤用户输入事件
    if (event->type() == QEvent::MouseButtonPress ||
        event->type() == QEvent::MouseButtonRelease ||
        event->type() == QEvent::MouseButtonDblClick ||
        event->type() == QEvent::KeyPress ||
        event->type() == QEvent::KeyRelease ||
        event->type() == QEvent::Wheel ||
        event->type() == QEvent::Enter ||
        event->type() == QEvent::Leave ||
        event->type() == QEvent::HoverEnter ||
        event->type() == QEvent::HoverLeave ||
        event->type() == QEvent::HoverMove)
    {
        return true;
    }
    return false;
}

使用示例:

//伪代码
int Test::syncSendData(const QByteArray& data, int timeout)
{  
    QPointer<EventLoopFilter> eventLoop = new EventLoopFilter();

    //此处将eventLoop对象指针传给命令接收线程

    //设置超时
    QTimer::singleShot(timeout, [eventLoop, data]()
    {
        if (eventLoop != nullptr && eventLoop->isRunning())
        {
            eventLoop->exit(TIME_OUT);
        }
    });

    sendData(data) //tcp发送
    ret = eventLoop->exec(); //在命令接收线程接收应答并匹配,匹配到则调用eventLoop对象的退出函数exit
    eventLoop->deleteLater();
    eventLoop = nullptr;
    return ret;
}

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值