继续事件的学习

四、事件的接受和忽略

1、事件可以被接受或忽略,被接受的事件不会再传递给其他对象,被忽略的事件会被传递给其他对象处理,或者该事件被丢弃(即没有对象处理该事件)

2、使用QEvent::accept()函数表示接受一个事件,使用QEvent::ignore()函数表示忽略一个事件。也就是说若调用accept(),则事件不会传递给父对象,若调用ignore()则事件会向父对象传递。

3、Qt默认值是accept (接受事件),但在QWidget的默认事件处理函数(比如keyPressEvent())中,默认值是ignore(),因为这样可实现事件的传递(即子对象未处理就传递给父对象处理)。对事件的接受和忽略,最好是明确的调用accept()和ignore函数。

4、在event()函数中调用accept()或ignore()是没有意义的,event()函数通过返回一个bool值来告诉调用者是否接受了事件(true表示接受事件)。是否调用accept()是用于事件处理函数与event()函数之间通信的,而event()函数返回的bool值是用于与QApplication::notify()函数之间通信的。

5、注意:QCloseEvent(关闭事件)有一些不同,QCloseEvent::ignore()表示取消关闭操作,而QCloseEvent::accept()则表示让Qt继续关闭操作。为避免产生混淆,最好在closeEvent()处理函数的重新实现中显示地调用accept()和ignore()。

示例:事件的接受和忽略

#include <QApplication>

#include<QWidget>

#include<QKeyEvent>

#include<QMouseEvent>

#include<QPushButton>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QWidget{public:

//重写以下事件处理函数

void mousePressEvent(QMouseEvent *e){ cout<<"AmouseDwon"<<endl;} //鼠标按下

void mouseReleaseEvent(QMouseEvent *e){ cout<<"AmouseRelease"<<endl;} //鼠标释放

void keyPressEvent(QKeyEvent* e){ cout<<"AkeyDwon"<<endl;} //键盘按下

void keyReleaseEvent(QKeyEvent* e){ cout<<"AkeyRelease"<<endl;} }; //键盘释放

class C:public QPushButton{public:

void mousePressEvent(QMouseEvent *e){ cout<<"CmouseDwon"<<endl;

e->accept(); } //验证事件被接受后不会再被传递给父对象

void mouseReleaseEvent(QMouseEvent *e){ cout<<"CmouseRelease"<<endl;

QWidget::mouseReleaseEvent(e); }//验证QWidget默认为忽略事件

void keyPressEvent(QKeyEvent* e){ cout<<"CkeyDwon"<<endl;

e->ignore(); } //验证事件事件被忽略后会被传递给父对象

void keyReleaseEvent(QKeyEvent* e){ cout<<"CkeyRelease"<<endl;

//验证Qt的默认处理方式为接受事件(此处未明确调用accept()或ignore()函数

} };

int main(int argc, char *argv[]){

QApplication a(argc,argv); //在Qt中QApplication类型的对象只能有一个

A ma; C *mc=new C();

mc->setParent(&ma); //设置父对象为ma

mc->setText("AAA");

mc->move(22,22); //设置mc相对于ma的位置

ma.resize(333,222); //设置部件的大小

ma.show();

a.exec(); //在此处进入事件主循环。

return 0; }

程序运行结果及说明(见图)

1、当在键钮上按下鼠标时,调用

C::mousePressEvent()函数,该函数接受该

事件,因此事件不再传递。因此仅输出

CmouseDown

2、当释放鼠标时调用C::mouseReleaseEvent()

函数输出CmouseRelease,该函数调用

QWidget的默认事件处理函数忽略该事件,

因此事件传递给mc的父对象ma处理,

此时调用A::mouseReleaseEvent()函数输出AmouseRelease。

示例:event()函数与事件处理函数的关系。

#include <QApplication>

#include<QWidget>

#include<QMouseEvent>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QWidget{public:

bool event(QEvent* e){

if(e->type()==QEvent::MouseButtonPress) //处理鼠标事件

{cout<<"AE"<<endl;

mousePressEvent((QMouseEvent*)e); /*明确调用鼠标事件处理函数处理该事件,需要注意的是Qt对事件处理函数的默认处理方式是接受事件。*/

//return QWidget::event(e); /*也可通过父类的event函数间接的调用鼠标事件处理函数。

return 0; } //event()函数的返回值与事件处理函数没有关系,返回0只表示该事件可以传递给父对象处理,返回1则事件不再传递。*/

return QWidget::event(e); //其他事件使用父类的event函数处事

}

void mousePressEvent(QMouseEvent *e){cout<<"AK"<<endl;} };

int main(int argc, char *argv[]){

QApplication aa(argc,argv);

A ma; ma.resize(333,222); ma.show(); aa.exec(); return 0; }

五、事件过滤器

1、事件过滤器用于拦截传递到目标对象的事件,这样可以实现监视目标对象事件的作用

2、Qt实现事件过滤器的步骤如下:

①、Qt调用

void QObject::installEventFilter (QObject* filterObj)

把filterObj对象设置安装(或注册)为事件过滤器,filterObj也称为过滤器对象。事件过滤器通常在构造函数中进行注册。

②、在上一步注册的filterObj对象,通过调用

bool QObject::eventFilter(QObject* obj, QEvent* e);

来接收拦截到的事件。也就是说拦截到的事件在filterObj对象中的eventFilter函数中处理。eventFilter的第一个参数obj指向的是事件本应传递到的目标对象。③、使用QObject::removeEventFilter(QObject *obj)函数可以删除事件过滤器。

3、事件过滤器处理事件的规则①、过滤器对象的eventFilter()函数可以接受或拒绝拦截到的事件,若该函数返回false,则表示事件需要作进一步处理,此时事件会被发送到目标对象本身进行处理(注意:这里并未向父对象进行传递),若evetnFilter()返回true,则表示停止处理该事件,此时目标对象和后面安装的事件过滤器就无法获得该事件。②、若同一对象安装了多个事件过滤器,则最后安装的过滤器首先被激活。

4、为什么使用事件过滤器:使用事件过滤器可以简化程序代码。比如按钮1和标签1,对按下A键的事件响应相同的操作,若不使用事件过滤器,则需要分别子类化按钮和标签部件,并重新实现各自的事件处理函数。再如使用同一个子类化按钮的类C创建的按钮1和按钮2,对按下键A,按钮1和按钮2需要作不同的响应,若不使用事件过滤器,则他们的响应是相同的,若使用事件过滤器,则可以拦截按钮1或按钮2的事件并作特殊处理。

5、理解事件过滤器观察者模式:其原理为,设有一目标对象S,它有多个观察该对象的对象G1,G2,G3,当S发生变化时,S的观察者会依情形改变自身。应用于Qt事件过滤器,则是,首先使用S的成员函数installEventFilter函数把G1,G2,G3设置为S的观察者,所有本应传递给S的事件E,则先传递给观察者G1,G2,G3,然后观察者调用其成员函数eventFilter对传递进来的事件进行处理,若eventFilter返回true表示事件处理完毕,返回false则返回给被观察者S进行处理。见下图。


示例:事件过滤器的使用

#include <QApplication>

#include<QWidget>

#include<QMouseEvent>

#include<QPushButton>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QObject{public: //该类的对象用作过滤器对象,使用事件过滤器需继承QObject

bool eventFilter(QObject *w, QEvent *e){

if(e->type()==QEvent::MouseButtonPress)

{cout<<w->objectName().toStdString(); //验证w为事件本应到达的目标对象

cout<<"=Ak"<<endl;

return 1; //返回1表示该事件不再进一步处理

}

return 0;} /*返回0表示其余事件交还给目标对象处理,本例应返回0,否则添加了该过滤器的安钮会无法显示。*/

};

class B:public A{public: //继承自类A

bool eventFilter(QObject *w, QEvent *e){

if(e->type()==QEvent::MouseButtonPress){

cout<<w->objectName().toStdString()<<"=Bk"<<endl;

return 0;}

return 0;}

};

class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}};

class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}};

int main(int argc, char *argv[]){

QApplication a(argc,argv);

//创建对象,注意:本例父对象应先创建,以避免生命期过早结束

A ma; B mb; C mc; D *pd=new D; D *pd1=new D;

pd->setText("AAA"); pd->move(22,22); pd1->setText("BBB"); pd1->move(99,22);

//设置对象名

ma.setObjectName("ma"); mb.setObjectName("mb"); mc.setObjectName("mc");

pd->setObjectName("pd"); pd1->setObjectName("pd1");

//设置父对象

pd->setParent(&mc); pd1->setParent(&mc);

mb.setParent(&ma); //①

//注册过滤器对象

pd->installEventFilter(&mb); //②

pd1->installEventFilter(&ma); //③

mc.resize(333,222); mc.show(); a.exec();

return 0; }

示例:添加多个事件过滤器

#include <QApplication>

#include<QWidget>

#include<QMouseEvent>

#include<QPushButton>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QObject{public:

bool eventFilter(QObject *w, QEvent *e){

if(e->type()==QEvent::MouseButtonPress)

{ cout<<"A"<<endl;

return 0; }//此处应返回0,注意,返回1事件将停止传递。

return 0;} };

class B:public QObject{public:

bool eventFilter(QObject *w, QEvent *e){

if(e->type()==QEvent::MouseButtonPress){ cout<<"B"<<endl;return 0; }

return 0;} };

class C:public QObject{public:

bool eventFilter(QObject *w, QEvent *e){

static int i=0;

if(e->type()==QEvent::MouseButtonPress){ cout<<"C="<<i++<<endl;return 0; }

return 0;} };

int main(int argc, char *argv[]){

QApplication a(argc,argv); //在Qt中QApplication类型的对象只能有一个

//创建对象

A ma; B mb; C mc,mc1; //事件过滤器对象

QWidget w; QPushButton *pb=new QPushButton();

pb->setText("AAA"); pb->move(22,22);

pb->setParent(&w);//设置父对象

//注册过滤器对象

pb->installEventFilter(&ma); pb->installEventFilter(&mb);

pb->installEventFilter(&mc); pb->installEventFilter(&mc1);

w.resize(333,222); w.show(); a.exec(); return 0; }

六、自定义事件与事件的发送

1、发送事件由以下两个函数完成

static void QCoreApplication::postEvent (QObject* receiver, QEvent* event,

int priority=Qt::NormalEventPriority);

static bool QCoreApplication::sendEvent(QObject* receiver, QEvent* event)

 receiver:指向接收事件的对象

 event:表示需要发送的事件

 priority:表示事件的优先级,事件会按优先级排序,高优先级的事件排在队列的前面。其取值为枚举类型Qt::EventPriority中的枚举值,如下

Qt::HighEventPriority:值为1。

Qt::NormalEventPriority:值为0。

Qt::LowEventPriority:值为-1。优先级只是一个相对值,其值可取介于规定的最大值和最小值之间的任何值,比如可使priority参数的值为Qt::HighEventPriority +10。

2、发送事件(sendEvent)与发布事件(postEvent)

①、发布(post)事件:

 把事件添加到事件队列中,并立即返回。

发布事件必须在堆(比如使用new)上创建事件,因为事件被发布后,事件队列将获得事件的所有权并自动将其删除。发布事件后再访问该事件是不安全的。

发布事件还可以对事件进行合并(或称为压缩),比如在返回事件循环之前连续发布了多个相同的事件,则这多个相同的事件会自动合并为一个单一的事件。可合并的事件有鼠标移动事件、调整大小事件等。②、发送(send)事件:把事件直接发送给接收事件的目标对象。事件发送之后不会被删除,发送的事件通常创建在堆栈上。

3、自定义事件原理

①、基本原理:事件其实就是使用的一个整数值表示的,因此在创建自定义事件时,只须给事件指定一个整数值即可,在Qt中,这个整数值是通过枚举类型QEvent::Type定义的,事件的其他信息可以封装在一个自定义的类之中。

②、自定义的事件即可以是预定义类型,也可以是自定义类型的。

③、自定义类型的事件,需要定义一个事件编号,该编号必须大于QEvent :: User(其值为1000),小于QEvent::MaxUser(其值为65535)。

④、各种事件不能重叠(即QEvent::Type类型的值不能相同),小于QEvent::User的事件是Qt内部定义的事件,他们不会重叠,对于自定义的事件可以使用registerEventType()函数来保证事件不重叠,该函数原型如下:

static int QEvent::registerEventType ( int hint = -1 );

如果hint的值不会产生重叠,则会返回这个值;如果hint不合法,系统会自动分配一个合法值并返回。因此,可使用该函数的返回值创建Type类型的值。

4、创建自定义事件的方法和步骤①、可以使用以下方式创建自定义事件

使用QEvent的构造函数创建事件,其原型为:

QEvent(Type type);

示例:QEvent::Type t1=(QEvent::Type)1333; //定义事件编号

QEvent e(t); //创建事件e

 使用Qt已创建好的事件类型创建事件,比如使用QKeyEvent类创建键盘事件。

继承QEvent类,创建自定义事件。②、使用QCoreApplication::postEvent()或QCoreApplication::sendEvent()函数发送事件。③、可使用以下方法处理自定义事件

重写QObject::event()函数,在该函数内直接处理自定义事件或调用自定义的事件处理函数处理事件。

安装事件过滤器,然后在过滤器对象的eventFilter()函数中处理自定义事件。

当然,还可以重写QCoreApplication::notify()函数。

示例:自定义事件的使用

#include <QApplication>

#include<QWidget>

#include<QObject>

#include <iostream>

using namespace std;

QEvent::Type t1=(QEvent::Type)1333;

QEvent e(t1); //使用QEvent的构造函数在堆栈上创建自定义事件

class E:public QEvent{public: //子类化QEvent以创建自定义事件

//方式1:使用静态成员。

//使用静态成员主要是为了正确初始化父类部分QEvent,比如

//E():t2((QEvent::Type)1324),QEvent(t2){},若t2不是静态的,则则初始化之后t2为1324,但传递

//给QEvent的t2是一个不确定的值,因为按照C++规则,对父类部分的初始化先于数据成员的初始化。

static QEvent::Type t2; //注意:不要使用名称t,因为QEvent类之中有一个名称为t的成员变量。

E():QEvent(t2){}

//方式2:使用带一个参数的构造函数

QEvent::Type t3;

explicit E(QEvent::Type t4):t3(t4),QEvent(t4){}

};

QEvent::Type E::t2=(QEvent::Type)1334;

class A:public QWidget{public:

bool event(QEvent* e){ //重写event函数以处理自定义事件

if(e->type()==t1) //判断事件类型是否为t1

{cout<<"AE"<<e->type()<<",";

f1((E*)e); //调用自定义的处理函数处理该事件

return 1; }

if(e->type()==E::t2) {cout<<"BE"<<e->type()<<","; f2((QEvent*)e); return 1; }

if(e->type()==((E*)e)->t3){cout<<"CE"<<e->type()<<","; f3((E*)e); return 1;}

return QWidget::event(e);

} //event结束

//以下为处理自定义事件的事件处理函数

void f1(E *e){cout<<"F1"<<endl;}

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

void f3(E *e){cout<<"F3"<<endl;}

}; //类A结束。

int main(int argc, char *argv[]){ QApplication aa(argc,argv);

A ma; E me; E *pe=new E((QEvent::Type)1335);

//发布或发送事件

aa.sendEvent(&ma,&e); aa.sendEvent(&ma,&me); aa.postEvent(&ma,pe);

//aa.postEvent(&ma,&me); //错误,发布的事件me必须是在堆上创建的。

ma.resize(333,222);

ma.show(); aa.exec(); return 0; }

示例:使用事件过滤器处理自定义事件

#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);

class A:public QWidget{public:

bool event(QEvent* e){

if(e->type()==t1) {cout<<"AE"<<e->type()<<","; f1((QEvent*)e); return 1; }

if(e->type()==t2) {cout<<"BE"<<e->type()<<","; f2((QEvent*)e); return 1; }

return QWidget::event(e); } //event结束

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

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

};

class B:public QObject{public:

bool eventFilter(QObject *w, QEvent *e){

if(e->type()==t1){ cout<<"A"<<endl;return 1; }

if(e->type()==t2){ cout<<"B"<<endl;return 0; }

return 0;}

};

int main(int argc, char *argv[]){ QApplication aa(argc,argv);

A ma; B mb;

ma.installEventFilter(&mb); //安装事件过滤器

aa.sendEvent(&ma,&e1); aa.sendEvent(&ma,&e2);

ma.resize(333,222); ma.show(); aa.exec(); return 0; }

本例依次输出A B BE65535,F2, 其中65535为使用registerEventType函数为t2分配的合法的数值。可见,安装事件过滤器之后,发送的事件e1和e2会使用过滤器对象的eventFilter函数进行处理。

七、事件的传递顺序总结

1、event()函数、事件过滤器、和事件处理函数的调用顺序如下:

首先按逆序调用事件过滤器,然后调用event()函数,最后调用事件处理函数(注意:事件处理函数需在event()函数中明确或间接调用,否则不会调用事件处理函数)。

八、鼠标和键盘事件共同使用的类及函数

1、通过调用从QInputEvent::modifiers()函数可以查询键盘修饰键的状态。

2、使用QWidget::etEnable()函数可以启用或禁用部件的鼠标和键盘事件。

3、键盘修饰键(即Ctrl、Alt、Shift等键)使用枚举类型Qt::KeyboardModifier来描述,其取值如下表


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 一、QApplication、QGuiApplication、QCoreApplication简介 1、继承关系见...
    379755b27396阅读 1,254评论 0 1
  • 事件 事件(event)是由系统或者 Qt 本身在不同的时刻发出的。 一些事件在对用户操作做出响应时发出,如键盘事...
    人不知QAQ阅读 269评论 0 0
  • QObject三大核心功能:信号与槽,内存管理,事件处理 总览 1、谁来产生事件: 最容易想到的是我们的输入设备,...
    上官宏竹阅读 2,701评论 2 4
  • 笔者用Qt算是用了挺长时间了,当初入C++的坑就是因为需要用Qt设计上位机软件。现在打算总结一下一些当初觉得有点深...
    饮茶先啦靓仔阅读 50,540评论 2 26
  • 为什么在头文件中有的是使用前置声明,而有的是包含头文件? 如下代码: 前置声明(forward declarati...
    Joe_HUST阅读 1,268评论 0 6