Qt事件

一、QApplication、QGuiApplication、QCoreApplication简介

1、继承关系见下图,其中左侧为顶级父类

QObject<-QCoreApplication<-QGuiApplication<-QApplication

2、一个程序中只能有一个QCoreApplication及其子类的对象。

3、QCoreApplication:主要提供无GUI程序的事件循环。

4、QGuiApplication:用于管理GUI程序的控制流和主要设置。

5、QApplication:该类专门为QGuiApplication提供基于QWidget的程序所需的一些功能,主要用于处理部件的初始化、最终化。主要职责如下:

使用用户的桌面设置初始化应用程序。

执行事件处理,也就是说该类能从底层系统接收并分发事件。比如,使用QCoreApplication::sendEvent()或QCoreApplication::postEvent()函数分发自定义事件。

解析常用命令行参数并设置其内部状态。

定义了应用程序的界面外观,可使用QApplication::setStyle()进行更改。

 指定应程程序如何分配颜色。

 使用QCoreApplication::translate()函数对字符串进行转换。

通过QApplication::desktop()函数处理桌面,通过QCoreApplication::clipboard()函数处理剪贴板。

管理应用程序的鼠标光标。比如使用QGuiApplication::setOverrideCuresor()函数设置光标等。

二、Qt对事件的描述及分类

1、事件:是由程序内部或外部产生的事情或某种动作的通称。比如用户按下键盘或鼠标,就会产生一个键盘事件或鼠标事件(这是由程序外部产生的事件);再如,当窗口第一次显示时,会产生一个绘制事件,以通知窗口需要重新绘制其自身,从而使该窗口可见(这是由程序内部产生的事件)。

2、事件和信号:

 他们两个是不同的概念,不要弄混淆。信号是由对象产生的,而事件则不一定是由对象产生的(比如由鼠标产生的事件),事件通常来自底层的窗口系统,但也可以手动发送自定义的事件,可见信号和事件的来源是不同的。

事件既可以同步使用,也可以异步使用(取决于调用sendEvent()还是postEvents()),而使用信号和槽总是同步的。事件的另一个好处是可以被过滤。

3、Qt中使用QEvent及其子类来描述事件(其继承关系见下图),比如QMouseEvent类用于描述与鼠标相关的事件,该类保存了与鼠标相关的大量信息,比如是哪一个键激发了该事件、产生该事件时鼠标的位置等。

4、事件的分类: ①、方式一:根据事件的来源和传递方式,事件可分为以下三大类

 自发事件:这是由窗口系统生成的,这些事件置于系统队列中,并由事件循环一个接一个地处理。

 发布的事件(Posted events):该类事件由Qt或应用程序生成,这些事件由Qt排队,并由事件循环处理。

 发送的事件(Sent events):该类事件由Qt或应用程序生成,这些事件直接发送到目标对象,不经过事件循环处理。

②、方式二:事件被细分为很多种类型(有一百多种),每一种类型使用QEvent类中的枚举常量进行表示,比如QMouseEvent管理的鼠标事件有鼠标双击、移动、按下等类型,这些类型分别使用QEvent::Type枚举类型中的枚举常量MouseButtonDblClick、MouseMove、MouseButtonPress表示。所有的类型分类请查阅帮助文档。可使用函数Type QEvent::type() const;获取事件的类型

5、使用Qt编程,几乎不需考虑事件,因为当产生某种事件时,Qt窗口部件都会发射一个相应的信号(即Qt会把事件转换为一个对应的信号),比如按钮被按下时,会产生一个MouseButtonPress事件,Qt会处理这一事件,并且会发射一个clicked()单击信号,程序员可以直接处理clicked()信号,而不必处理底层的事件。

6、对于事件,我们不需要知道Qt是怎样把事件转换为QEvent或其子类类型的对象的,程序员只需要知道怎样传递和处理这些事件即可。比如对于按下鼠标事件,不需要知道Qt是怎样把该事件转换为QMouseEvent类型的对象的,只需要知道怎样传递和处理该事件即可。

QEvent QDropEvent QCloseEvent QPaintEvent QInputEvent QResizeEvent QTimerEvent QContextMenuEvent QKeyEvent QMouseEvent QWheelEvent QDragMoveEvent QDragEnterEvent QActionEvent QChildEvent QDragLeaveEvent QEnterEvent QExposeEvent QFileOpenEvent QFocusEvent QGraphicsSceneEvent QHelpEvent QHideEvent QIconDragEvent QInputMethodEvent QInputMethodQueryEvent QMoveEvent QScrollEvent QPlatformSurfaceEvent QScrollPrepareEvent QShortcutEvent QShowEvent QStateMachine::SignalEvent QStateMachine::WrappedEvent QStatusTipEvent QWhatsThisClickedEvent QWindowStateChangeEvent QDynamicPropertyEvent QGestureEvent QHoverEvent QNativeGestureEvent QTabletEvent QTouchEvent QGraphicsSceneWheelEvent

……

三、事件的传递(或分发)及处理

1、事件传递步骤(见下图):

①、基本规则:若事件未被目标对象处理,则把事件传递给其父对象处理,若父对象仍未处理,则再传递给父对象的父对象处理,重复这个过程,直至这个事件被处理或到达顶级对象为止。注意:事件是在对象间传递的,这里是指对象的父子关系,而不是指类的父子关系。

②、在Qt中有一个事件循环,该循环负责从可能产生事件的地方捕获各种事件,并把这些事件转换为带有事件信息的对象,然后由Qt的事件处理流程分发给需要处理事件的对象来处理事件。

③、通过调用QCoreApplication::exec()函数启动事件主循环,主循环从事件队列中获取事件,然后创建一个合适的QEvent对象或其子类类型的对象来表示该事件,在此步骤中,事件循环首先处理所有发布的事件,直到队列为空,然后处理自发的事件,最后处理在自发事件期间产生的已发布事件。注意:发送的事件不由事件循环处理,该类事件会被直接传递给对象。

④、然后,Qt会调用QCoreApplication::notify()函数对事件进行传递(或分发)

⑤、最后,QObject对象调用QObject::event()函数接收事件,

event()函数是一个虚函数,是QObject对象处理事件的入口,

在QObject的子类中通常会重写event()函数,比如QWidget类就重写了event()函数。

 event()函数与事件处理函数的关系:event()函数负责把事件传递给目标对象并调用对应的事件处理函数处理事件,比如调用QWidget::keyPressEvent()函数处理键盘按下事件等,注意,QObject中的event()函数并不能实现该功能,这是通过QObject的子类中重写的event()函数实现的,比如调用QWidget::keyPressEvent()函数,就是由在QWidget类中重写的event()函数来完成的。

 event()函数对大多数事件都调用了默认的处理函数,但并不能包括全部的事件,因此,有时我们需要重写该函数,这也是我们处理Qt事件的方式之一。

注意:event()函数不会处理事件,他只是根据事件的类型进行事件的传递(或分发),若返回true则表示这个事件被接受并进行了处理,否则事件未被处理,需进行进一步传递或丢弃。

事件1 事件2 事件3->exec()->notify()->QObject::event()->keyPressEvent()->mousePressEvent()

2、事件的处理

①、任何QObject的子类都可以处理事件,QEvent及其子类虽然可以描述一个事件,但并不对事件进行处理。

②、事件处理函数

事件是由类的成员函数(通常为虚函数)处理的,通常把处理事件的成员函数称为事件处理函数。由此可见,处理事件需要两个要求:处理该事件的对象和该对象中用于处理该事件的事件处理函数。

Qt中对绝大多数常用类型的事件提供了默认的事件处理函数,并作了默认处理,这些事件处理函数位于QObject的子类中,比如QWidget::mousePressEvent()函数用于处理鼠标按下事件,QWidget::keyPressEvent()用于处理键盘按下事件等。对这些默认事件处理函数的调用是通过QObject::event()虚函数进行的,因此若默认的事件处理函数不能满足用户要求(比如对Tab键的处理),则可以通过重新实现event()函数来达到目的。下面为QWidget类的部分源代码(qwidget.cpp)

bool QWidget::event(QEvent *event){

switch (event->type()) {

//调用QWidget类的默认事件处理函数

case QEvent::MouseButtonPress:

mousePressEvent((QMouseEvent*)event); break;

case QEvent::MouseButtonRelease:

mouseReleaseEvent((QMouseEvent*)event); break; 

} }

3、由事件传递过程可见,Qt可以使用以下5种方式来处理事件

①、重新实现各部件内部默认的事件处理函数,比如重新实现paintEvent()、mousePressEvent()等事件处理函数,这是最常用、最简单的方式

②、重新实现QObject::event()函数(需继承QObject类),使用该方式,可以在事件到达默认的事件处理函数之前捕获到该事件。该方式常被用来处理Tab键的默认意义。在重新实现event()函数时,必须对未明确处理的事件调用基类的event()函数,比如,若子类化QWidget,并重写了event()函数,但未调用父类的event()函数,则程序可能会不能正常显示界面。

③、在QObject对象上安装(或称为注册)事件过滤器(见后文)。对象一旦使用installEventFilter()注册,传递给目标对象的所有事件都会先传递给这个监视对象的eventFilter()函数。或同一对象上安装了多个事件处理器,则按照安装的逆序依次激活这些事件处理器。④、在QApplication上安装(或称为注册)事件过滤器。该方式与重新实现notify函数一样强大。一旦在QApplication对象(程序中只有一个这种类型的对象)上注册了事件过滤器,则程序中每个对象的每个事件都会在发送到其他事件过滤器之前,发送到这个eventFilter()函数。该方式对于调试非常有用。

⑤、子类化QApplication,并重新实现QCoreApplication::notify()函数,由事件传递过程可知,该函数提供了对事件的完全控制,因此功能非常强大,所以重写该函数会很复杂,也因此此方法很少被使用。这是唯一能在事件过滤器之前捕获到事件的方式。

⑥、注意:exec()、notify()和event()函数都不会处理事件,他们只是根据事件的类型进行事件的传递(或分发)。

示例:事件处理的方式

#include <QApplication>

#include<QWidget>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QWidget{public:

bool event(QEvent* e); //事件处理方式1:重写虚函数QObject::event()

void mousePressEvent(QMouseEvent* e); };//事件处理方式2:重写QWidget类中的此虚函数

bool A::event(QEvent* e){ static int i=0; //i用于计数

cout<<"E"<<i++<<endl; //验证该函数能在事件到达目标对象之前被捕获

if(e->type()==QEvent::KeyPress) //判断是否是键盘按下事件。

cout<<"keyDwon"<<endl;

return QWidget::event(e); /*此处应调用父类的event函数以对未处理的事件进行处理,若此处不调用父类的event,则本例mousePressEvent处理函数中的内容将不会被执行。读者可使此处返回1或者0进行验证。*/

}

void A::mousePressEvent(QMouseEvent* e){//处理鼠标按下事件,这是Qt中最简单的事件处理方式。

cout<<"mouseDwon"<<endl;

}

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

QApplication a(argc,argv); //在Qt中QApplication类型的对象只能有一个A ma; //创建一个部件

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

ma.show(); //显示创建的部件

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

return 0; }

程序运行结果及说明(见右图) 只要窗口ma接收到事件就会输出Exx,可见event函数捕获事件的

范围及时机,当按下键盘上的任一键时会输出"keyDown",当按下

任一鼠标键时,会输出mouseDown

示例:重写notify函数

#include <QApplication>

#include<QWidget>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QWidget{public: bool event(QEvent* e); }; //重写虚函数QObject::event()

class AA:public QApplication{public:

AA(int i,char *p[]):QApplication(i,p){}

bool notify(QObject *o,QEvent *e);}; //重写虚函数notify

bool A::event(QEvent* e){ cout<<"E"<<endl; //用于验证该函数是否被执行

return QWidget::event(e); } //应调用父类的event函数处理未处理的事件

bool AA::notify(QObject *o,QEvent *e){

static int i=0;

cout<<"N"<<i++<<endl;

if(o->objectName()=="ma"&&e->type()==QEvent::KeyPress) //若对象为ma且事件为键盘按下事件

cout<<"keyDwon"<<endl;

return QApplication::notify(o,e); } /*应调用父类的notify函数,否则本示例的event函数不会被执行,同时无法关闭窗口*/

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

AA aa(argc,argv);//在Qt中QApplication或其子类型的对象只能有一个

A ma; //创建一个部件

ma.setObjectName("ma"); //设置对象名,方便在notify函数中调用

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

ma.show(); //显示创建的部件

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

return 0; }

程序运行结果及说明(见右图) 本示例可以看到notify()函数和event()函数的执行顺序,及notify函数和event

函数捕获的事件的范围和时机,当用户按下键盘上的键时会输出keyDown

示例:事件传递

#include <QApplication>

#include<QWidget>

#include<QPushButton>

#include<QObject>

#include <iostream>

using namespace std;

class A:public QWidget{public: bool event(QEvent* e); };//子类化QWidget

class C:public QPushButton{public: bool event(QEvent* e);}; //子类化标准按钮QPushButton

bool A::event(QEvent* e){

if(e->type()==QEvent::KeyPress) //判断是否是键盘按下事件。

{cout<<objectName().toStdString()<<"=keyDwon"<<endl; }

if(e->type()==QEvent::MouseButtonPress) //判断是否是鼠标按下事件。

{cout<<objectName().toStdString()<<"=mouseDwon"<<endl; }

if(e->type()==QEvent::MouseButtonRelease) //判断是否是鼠标释放事件。

{cout<<objectName().toStdString()<<"=mouseRelease"<<endl; }

return QWidget::event(e); }

bool C::event(QEvent* e){

if(e->type()==QEvent::KeyPress) //判断是否是键盘按下事件。

{cout<<objectName().toStdString()<<"=keyDwon"<<endl;

return 0; }//将事件传递给父对象处理

if(e->type()==QEvent::MouseButtonPress) //判断是否是鼠标按下事件。

{cout<<objectName().toStdString()<<"=mouseDwon"<<endl;

return 1; } //事件不传递

return QWidget::event(e);}

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

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

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

mc->setParent(&ma); /*设置父对象为ma,若mc未处理的对象会传递给此处设置的父对象ma处理,注意事件传递是在对象之间的父子关系,而不是类之间的父子关系。*/

mc->setText("AAA");

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

ma.setObjectName("ma"); //设置对象名

mc->setObjectName("mc");

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

ma.show();

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

return 0; }

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

1、当在按钮上按下鼠标键时,调用C::event()

函数输出mc=mouseDown,

2、此时该函数返回1,表示事件不再传递

3、但是该函数并未处理鼠标释放事件,因此

调用QWidget::event()对该事件作默认处理,

因默认未处理该事件,因此调用mc的父

对象ma处理该事件,此时调用A::event()

函数输出ma=mouseRelease,至此鼠标事件处理结束。

4、当按钮获得焦点,按下键盘上的按键时的处理方式与鼠标事件类似,只是C::event()函数直接把键按下事件传递给了父对象ma处理。

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

推荐阅读更多精彩内容

  • 参考:Events and signals in PyQt5 所有的应用都是事件驱动的。事件大部分都是由用户的行为...
    水之心阅读 1,838评论 1 1
  • 事件 事件(event)是由系统或者 Qt 本身在不同的时刻发出的。 一些事件在对用户操作做出响应时发出,如键盘事...
    人不知QAQ阅读 274评论 0 0
  • Qt 事件和信号的关系 Qt的事件是windows的底层消息封装而成的。这个消息和MFC里的消息是同一概念,都是指...
    行走的代码阅读 1,513评论 0 0
  • 笔者用Qt算是用了挺长时间了,当初入C++的坑就是因为需要用Qt设计上位机软件。现在打算总结一下一些当初觉得有点深...
    饮茶先啦靓仔阅读 50,638评论 2 26
  • QObject三大核心功能:信号与槽,内存管理,事件处理 总览 1、谁来产生事件: 最容易想到的是我们的输入设备,...
    上官宏竹阅读 2,737评论 2 4