Qt事件机制

Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.。

Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期。

Qt事件的类型很多, 常见的qt的事件如下:

键盘事件: 按键按下和松开.

鼠标事件: 鼠标移动,鼠标按键的按下和松开.

拖放事件: 用鼠标进行拖放.

滚轮事件: 鼠标滚轮滚动.

绘屏事件: 重绘屏幕的某些部分.

定时事件: 定时器到时.

焦点事件: 键盘焦点移动.

进入和离开事件: 鼠标移入widget之内,或是移出.

移动事件: widget的位置改变.

大小改变事件: widget的大小改变.

显示和隐藏事件:widget显示和隐藏.

窗口事件: 窗口是否为当前窗口.

还有一些非常见的qt事件,比如socket事件,剪贴板事件,字体改变,布局改变等等.

交流会围绕以下三个问题展开:

一、什么是事件?

二、事件是怎样被处理的?

三、事件与信号的区别?

一、什么是事件?

事件:某个“动作”的完成后,需让某个对象知道而发送的消息。(个人观点)

解释:此时的“动作”并非通常意义所指的动作,而是广义的“动作”,是主动和被动的总和。

例:两个窗体A和B,当A为最小化状态时,我们使它最大化,这就会让A主动产生一个重绘事件;当A和B非最小化状态,且B位于A窗体之上时,我们让B最小化,那么刚才被B遮挡的A窗体就会被动地产生一个重绘事件。

Qt 的事件和Qt中的signal不一样. 后者通常用来"使用"widget,而前者用来"实现" widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的. 但是如果我们要重载一个按钮的时候,我们就要面对event了. 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.

我们按产生来源把事件分为两类:

(一) 系统产生的;通常是windowsystem把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中,Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理.

(二)是由Qt应用程序程序自身产生的.程序产生事件有两种方式, 一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理. 另一种方式是调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发和处理,QWidget::repaint()函数用的就是这种方式。

二、事件是怎样被处理的?

(一)两种调度方式,一种是同步的, 一种是异步.

Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:

while ( !app_exit_loop )

{

while( !postedEvents ) { processPostedEvents() }

while( !qwsEvnts ){ qwsProcessEvents();  }

while( !postedEvents ) { processPostedEvents() }

}

先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.

调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.

(二) 事件的派发和处理

首先说明Qt中事件过滤器的概念. 事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便. 通过它, 可以让一个对象侦听拦截另外一个对象的事件. 事件过滤器是这样实现的: 在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObjec (qobjA)给另一个QObject (qobjB)安装了事件过滤器之后, qobjB会把qobjA的指针保存在eventFilters中. 在qobjB处理事件之前,会先去检查eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数. 一个对象可以给多个对象安装过滤器. 同样, 一个对象能同时被安装多个过滤器, 在事件到达之后, 这些过滤器以安装次序的反序被调用. 事件过滤器函数( eventFilter() ) 返回值是bool型, 如果返回true, 则表示该事件已经被处理完毕,Qt将直接返回, 进行下一事件的处理; 如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理.

Qt中,事件的派发是从QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用这些事件过滤器. 接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并). 之后,事件被送到reciver::event() 处理.

同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之. 接下来,根据QEvent的类型, 调用相应的特定事件处理函数. 一些常见的事件都有特定事件处理函数, 比如:mousePressEvent(),focusOutEvent(),  resizeEvent(),paintEvent(), resizeEvent()等等. 在实际应用中, 经常需要重载这些特定事件处理函数在处理事件. 但对于那些不常见的事件, 是没有相对应的特定事件处理函数的. 如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器.

事件派发和处理的流程图如下:

(三) 事件的转发

对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口. 如图所示, 事件最先发送给QCheckBox, 如果QCheckBox没有处理, 那么由QGroupBox接着处理, 如果QGroupBox没有处理, 再送到QDialog, 因为QDialog已经是最顶层widget, 所以如果QDialog不处理, QEvent将停止转发.       如何判断一个事件是否被处理了呢? Qt中和事件相关的函数通过两种方式相互通信. QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递. 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识. 这种方式只用于event() 函数和特定事件处理函数之间的沟通. 而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件.

(四)实际运用

根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下:

(1)重载特定事件处理函数.

最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下:

void imageView::keyPressEvent(QKeyEvent *event)

{

switch (event->key()) {

case Key_Plus:

zoomIn();

break;

case Key_Minus:

zoomOut();

break;

case Key_Left:

// …

default:

QWidget::keyPressEvent(event);

}

}

(2)重载event()函数.

通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件.

下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )

bool CodeEditor::event(QEvent * event)

{

if (event->type() == QEvent::KeyPress)

{

QKeyEvent *keyEvent = (QKeyEvent *) event;

if (keyEvent->key() == Key_Tab)

{

insertAtCurrentPosition('\t');

return true;

}

}

return QWidget::event(event);

}

(3) 在Qt对象上安装事件过滤器.

安装事件过滤器有两个步骤:(假设要用A来监视过滤B的事件)

首先调用B的installEventFilter( const QOject*obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.

然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.

用这种方法改写上面的例子:(假设我们将CodeEditor放在MainWidget中)

MainWidget::MainWidget()

{

CodeEditor * ce = new CodeEditor( this, “code editor”);

ce->installEventFilter( this );

}

bool MainWidget::eventFilter( QOject *target , QEvent * event )

{

if( target == ce )

{

if( event->type() == QEvent::KeyPress )

{

QKeyEvent *ke = (QKeyEvent *) event;

if( ke->key() == Key_Tab )

{

ce->insertAtCurrentPosition('\t');

return true;

}

}

}

return false;

}

(4) 给QAppliction对象安装事件过滤器.

一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个eventFilter(). 在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉. ( 在QApplication::notify()中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)

(5) 继承QApplication类,并重载notify()函数.

Qt 是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事

三、事件与信号的区别?

Qt 的事件和Qt中的signal不一样. 后者通常用来"使用"widget,而前者用来"实现" widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的。但是如果我们要重载一个按钮的时候,我们就要面对event了。 比如我们可以改变它的行为,在鼠标按键按下的时候(mousepress event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.。

信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。

http://blog.csdn.net/u013394556/article/details/38597529

Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)的更多相关文章

  1. Qt事件机制浅析(定义,产生,异步事件循环,转发,与信号的区别。感觉QT事件与Delphi的事件一致,而信号则与Windows消息一致)

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  2. Qt事件机制---信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。

    转:http://www.cnblogs.com/findumars/p/8001484.html Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和.先处理自己队列中的消息,然后再处 ...

  3. Qt事件机制浅析

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  4. 详解 QT 源码之 Qt 事件机制原理

    QT 源码之 Qt 事件机制原理是本文要介绍的内容,在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使 Qt 程序进入消息循环.下面我们就到ex ...

  5. Qt 事件机制

    [1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...

  6. qt事件机制(转)

    学习了一段时间的Qt之后,发现Qt的事件机制和其他语言的机制有些不同.Qt除了能够通过信号和槽机制来实现一些Action动作之外,还可以用对象所带的事件,或者用户自定义的事件来实现对象的一些行为处理. ...

  7. [Qt] 事件机制(一)

    事件主要分为两种: 在与用户交互时发生.比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等 系统自动发生,比如计时器事件(timerEvent)等 每种事件对应一 ...

  8. [Qt] 事件机制(二)

    在samp4_1中加一个小功能,点击右上角关闭按钮时,弹出"确认是否关闭"的消息框.如果点"yes"则关闭,如果点"No"则不关闭 在wid ...

  9. qt事件机制---事件范例

    在笔记qt课程04笔记中

随机推荐

  1. 调用获取学生信息的接口,保存到excel里面的小程序

    # 2.http: // doc.nnzhp.cn / index.php?s = / 6 & page_id = 14# 调用获取学生信息的接口,保存到excel里面 import requ ...

  2. iOS时间间隔判断

    如何计算两个NSDate之间的时间间隔呢? timeIntervalSinceDate: Returns the interval between the receiver and another g ...

  3. windows 下编译64位ffmpeg

    window下如何编译ffmpeg 前文已有介绍,前面介绍的都是如何编译32位的库,本文主要介绍window 下如何编译64位ffmpeg库. 一.搭建编译环境 1.下载 windows下编译64位F ...

  4. 无需Root实现Android手机屏幕流畅投影到电脑进行演示(附软件下载)

    近期要在公司的会议上演示App,须要在投影仪上显示出来给大家演示.因为投影仪不是智能投影仪,仅仅能将App先投影到自己的笔记本上.然后再将笔记本上的内容投影到投影仪上.该App是个游戏,实时交互性比較 ...

  5. java.String中的方法

    (String) str.trim() 该方法返回一个复制该字符串的开头和结尾的白色空格去掉,或字符串,如果它没有头或尾空白. (Boolean) str.contains(str1) 判断 str ...

  6. Atitit. Atiposter 发帖机版本历史 编年史

    Atitit. Atiposter 发帖机版本历史 编年史 V1  初步实现sina csdn cnblogs V2  实现qzone sohu 的发帖功能  顺便重构接口实现分离 V3多文件循环发帖 ...

  7. smarty模版使用php标签,如何获取模版变量

    smarty模版使用php标签,如何获取模版变量 in: 后端程序 已经assign一个模版变量$assign,由于要做特殊的循环输出,使用for循环,因此使用到了php标签,但是php语句和模版语句 ...

  8. WAF绕过方法

    1.大小写绕过 这个大家都很熟悉,对于一些太垃圾的WAF效果显著,比如拦截了union,那就使用Union UnIoN等等绕过. 2.简单编码绕过 比如WAF检测关键字,那么我们让他检测不到就可以了. ...

  9. excel批量取消隐藏工作表

    按下"Alt+F11"键,在打开的"Microsoft Bisual Basic"窗口中,选择"插入——模块".,复制下面的代码,按F5键运 ...

  10. SQLSERVER---- 通过位运算更改标志位

    当给多个中心传输数据时,怎么标记哪些单位推送了,哪些单位没有更新,如果单独设置一个字段,一来说,扩展不足,另外会造成数据库冗余,这里可以采用SQLSERVER的位运算. 比如说,更新标志位为0,长度为 ...