Qt事件系统:自定义事件的注册 (**)

目录

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

注册的好处:

Qt默认的事件,多个数千?数万个(具体忘记了)。

如果不采用注册的方式,则程序需要从头到尾地遍历一遍,这是非常没有效率的做法。甚至是不可能的,比如用户自定义的数据格式,这种数据格式的总量是无限的。

采用注册方式:则只需遍历 Qt系统默认的部分,以及用户注册新增的部分,即可。

==========================

注意区别:QEvent 和 EventFilter 过滤事件

在 Qt 里,一个事件就是一个对象,所有事件的祖先都来自于 QEvent

只要有一个事件发生(如鼠标单击事件),此时就会有一个 QEvent 对象被创建出来,然后开始各种传送。由于 Qt 事件系统是依托于元对象系统的,所以所有的 QObject 类都可以接收/处理 QEvent 事件。

Event的来源:

Some events, such as QMouseEvent and QKeyEvent, come from the window system;

一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;

some, such as QTimerEvent, come from other sources;

有些,如QTimerEvent,来自其他来源;

some come from the application itself.

一些来自应用程序本身。

——The Event System

Event的去向:

Qt’s main event loop (QCoreApplication::exec()) fetches “native window system events” from the event queue, translates them into “QEvents”, and sends the translated events to “QObjects”.

Qt的主事件循环(QCoreApplication::exec())从事件队列中获取“本机窗口系统事件”,将它们转换为“QEvents”,并将转换后的事件发送给“QObjects”。

——QEvent

产生一个事件

Qt 自带的事件类有很多很多,有些事件类还附带了简单的函数,例如 QResizeEvent 这个事件类就有 size() 和 oldSize() 函数;有些事件类还支持多种实际操作,比如 QMouseEvent 支持鼠标单击、双击、移动等,也就是说当鼠标进行这些操作的时候,都会产生一个 QMouseEvent 事件对象。

自定义的一个事件类

class MyEvent : public QEvent
{
public:
    MyEvent();
    MyEvent(int x, int y, int z);

    static const Type type;

    int x;
    int y;
    int z;
};

Qt 提供了一个函数 registerEventType() 专门用于自定义事件的注册。如下:
const QEvent::Type MyEvent::type = (QEvent::Type)QEvent::registerEventType()

发送一个事件

Qt 提供了三个 static 事件发送函数:sendEvent、postEvent、sendPostEvents。函数原型如下:

bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
void QCoreApplication::sendPostedEvents(QObject *receiver = Q_NULLPTR, int event_type 

直接发送:sendEvent

这是最好理解的,两个参数中一个是要发给谁,另一个是发送什么事件。这种方式的发送我们要等待对方处理,所以返回值是一个 bool 量来判断。对于许多事件类,有一个名为 isAccepted() 来告诉你是否被处理。因为事件被发送的时候,event 对象并不会被销毁,因此我们要在栈上创建 event 对象

发到队列:postEvent

我们创建一个 Qt 程序的时候,一般在 main 下面会看到 QCoreApplication a(argc, argv) 以及 return a.exec() 的字样。这其实就是开启了 Qt 事件循环来维护一个事件队列,exec 的本质就是不停的调用 processEvent() 函数从队列中获取事件来处理。而 postEvent() 的作用就是把事件发送到这个队列中去。

这种方式不需要等待处理结果,只要把事件发到队列中就可以了,所以返回值是 void。由于事件队列会持有发送的事件对象,在 post 的时候会将事件 delete 掉,所以我们必须在堆上创建 event 对象

在队列中立即处理:sendPostedEvents

看参数我们就可以知道,这个函数的作用就是立刻、马上将队列中的 event_type 类型的事件立马交给 receiver 进行处理。需要注意的是,来自窗口系统的事件并不由这个函数进行处理,而是 processEvent()

处理事件

事件的传递过程

QEvent的 accept()和ignore()

void MyPushButton::mousePressEvent(QMouseEvent *e)
{
    qDebug() << e->isAccepted();//true 这个是事件对象默认的即 在Qt中 事件的传递是默认接收的
    if(e->button() == Qt::LeftButton){//表示接受处理了该事件
        qDebug() << "LeftButton";
        //如果在这里调用了ignor函数
        e->ignore();//表示告诉Qt MyPushButton这个对象没有处理该事件,让Qt继续传递给别的对象。
        //但是这里要注意,这个事件继续传递的对象不是基类QPushButton,而是MyPushButton所属的的父组件
    }else{//表示忽略不处理该事件
        QPushButton::mousePressEvent(e);
    }
}

而accept()和ignore()函数的正确用法是用在重写CloseEvent函数中

void Widget::closeEvent(QCloseEvent *event)
{
    int ret = QMessageBox::question(this,"关闭窗口","是否要关闭窗口");
    if(ret == QMessageBox::Yes){
        event->accept();
    }else if(ret == QMessageBox::No){
        event->ignore();//继续传递给它的父组件
        QWidget::closeEvent(event);//如果本身就是最顶层的组件 则不需要该语句
    }
}

QEvent 和 EventFilter 过滤事件

测试代码

#include <QApplication>
#include<QWidget>
#include<QObject>
#include <iostream>
using namespace std;
QEvent::Type t1=(QEvent::Type)QEvent::registerEventType(1333);
QEvent e1(t1); //使用 QEvent 的构造函数创建自定义事件
//t2 的值与 t1 重复,使用 registerEventType 会自动产生一个合法的值
QEvent::Type t2=(QEvent::Type)QEvent::registerEventType(1333);
QEvent e2(t2);

//event 函数的作用就在于 事件的分发,在 even filter 之后执行
//传入的事件已被识别并且处理,则需要返回 true,否则返回 false。
//如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
class A:public QWidget
{
public:
    bool event(QEvent* e){
        if(e->type()==t1) {
            cout<<"AE"<<e->type()<<",";
            f1((QEvent*)e);
            //
            return true;
        }

        if(e->type()==t2) {
            cout<<"BE "<<e->type()<<",";
            f2((QEvent*)e);
            return true;
        }
        //调用父组件
        return QWidget::event(e);
    } //event 结束

    void f1(QEvent *e){cout<<"F1"<<endl;}
    void f2(QEvent *e){cout<<"F2"<<endl;}
};

//返回true为不再转发,false则让其继续被处理
class B:public QObject
{
public:
    bool eventFilter(QObject *w, QEvent *e)
    {
        Q_UNUSED(e);
        if(e->type()==t1)
        {
            cout<<"A"<<endl;
            //e->accept();
            //事件不再进行传播,拦截
            return true;
        }

        if(e->type()==t2)
        {
            cout<<"B"<<endl;
            //e->ignore();

            return false;
        }
        //也可直接 return false; 不进行拦截, 且不继续传播
        //return false;
        //当不确定是否继续传播时,按照父类的方法来处理
        //即调用父类的evenFilter函数
        return B::eventFilter(w, e);
    }
};

int main(int argc, char *argv[])
{
    QApplication aa(argc,argv);
    A ma; B mb;
    ma.installEventFilter(&mb); //安装事件过滤器
    aa.postEvent(&ma,&e1);
    aa.postEvent(&ma,&e2);
    aa.sendEvent(&ma,&e1);
    aa.sendEvent(&ma,&e2);
    ma.resize(333,222);
    ma.show();
    aa.exec();
    return 0;
}

定时器

QTimer和timerEvent的功能完全一致,QTimer更为清晰和灵活,而timerEvent更为简约和高效。大家在项目中使用定时器,还是要根据项目的具体情况而定。

//.h文件
private slots:
    void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
private:
    int timerId(0);    //用于区分不同的计时器,示例举了2个计时器的情况
    int timerId2(0);
    
//.cpp文件
timerId = startTimer(200);
timerId2 = startTimer(1000);
//timerEvent()函数内
if (event->timerId() == timerId)
{
    ...
}
else if (event->timerId() == timerId2)
{
    ...
    killTimer(timerId2)
}

reference

QT 事件的传递先后顺序_LearnLHC的博客-CSDN博客_qt事件传递顺序

Qt事件的接收和忽略_wei375653972的博客-CSDN博客

QObject的timerEvent使用 - 看看完了 - 博客园 (cnblogs.com)

Qt 中的事件系统 - 有什么难的呢? - 知乎 (zhihu.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值