Qt事件机制浅析

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)时候.

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

(一) 系统产生的;通常是window
system把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中,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了。 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse
press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.。

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

时间: 2024-08-10 17:01:54

Qt事件机制浅析的相关文章

QT开发(六十三)——QT事件机制分析

QT开发(六十三)--QT事件机制分析 一.事件机制 事件是由系统或者QT平台本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应时发出,如键盘事件等:另一些事件则是由系统自动发出,如计时器事件. 事件的出现,使得程序代码不会按照原始的线性顺序执行.线性顺序的程序设计风格不适合处理复杂的用户交互,如用户交互过程中,用户点击"打开文件"将开始执行打开文件的操作,用户点击"保存文件"将开始执

Qt事件机制概览

Qt事件机制概览 Qt事件机制概览 消息循环 Qt事件循环 简介 QEventLoop 跨线程的信号和槽与事件循环 模态窗口 Native widget or Alien widget 创建Native widget 创建QApplication的message-only窗口 派发事件的公共基础方法 source code QApplication的创建过程 QWidget native QWidget 的创建过程 普通native widget回调过程 QApplication的message

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

Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事件: 按键按下和松开. 鼠标事件: 鼠标移动,鼠标按键的按下和松开. 拖放事件: 用鼠标进行拖放. 滚轮事件: 鼠标滚轮滚动. 绘屏事件: 重绘屏幕的某些部分. 定时事件: 定时器到时. 焦点事件: 键盘焦点移动. 进入和离开事件: 鼠标移入widget之内,或是移出. 移动事件: widget的位

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

Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事件: 按键按下和松开. 鼠标事件: 鼠标移动,鼠标按键的按下和松开. 拖放事件: 用鼠标进行拖放. 滚轮事件: 鼠标滚轮滚动. 绘屏事件: 重绘屏幕的某些部分. 定时事件: 定时器到时. 焦点事件: 键盘焦点移动. 进入和离开事件: 鼠标移入widget之内,或是移出. 移动事件: widget的位

Qt——事件机制(二)

在samp4_1中加一个小功能,点击右上角关闭按钮时,弹出“确认是否关闭”的消息框.如果点“yes”则关闭,如果点“No”则不关闭 在widget.h中添加代码: #include <QCloseEvent> protected: void closeEvent(QCloseEvent *ev); 在widget.cpp中添加代码: 1 #include <QMessageBox> 2 void Widget::closeEvent(QCloseEvent *ev){ 3 int

【转】QT事件传递与事件过滤器

1.事件类型 Qt程序是事件驱动的,程序的每个动作都是由幕后某个事件所触发. Qt事件的类型很多,常见的qt的事件如下:            键盘事件: 按键按下和松开.        鼠标事件: 鼠标移动,鼠标按键的按下和松开.        拖放事件: 用鼠标进行拖放.        滚轮事件: 鼠标滚轮滚动.        绘屏事件: 重绘屏幕的某些部分.        定时事件: 定时器到时.        焦点事件: 键盘焦点移动.        进入和离开事件: 鼠标移入widge

Qt:事件和事件循环

最近想要了解一下Qt线程,但在对相关资料师都是从线程和事件循环开始将.对于事件循环是个相对很抽象的概念,研究了很久也很难在脑子里建立起一个具体的模型.今天在这里对这几天研究内容做总结,为以后做参考. 一.事件 网上很多资料都将的很清楚.在这里重点是明确一下一个事件发出后对于该事件的一个调用顺序.在这里我们在QApplication安装一个过滤器,在一个一个Qwidget安装一个过滤器 代码如下: class Label : public QWidget { public: Label() { i

Qt消息机制和事件(二)

三,事件过滤器 有时候,对象需要查看.甚至要拦截发送到另外对象的事件.例如,对话框可能想要拦截按键事件,不让别的组件接收到:或者要修改回车键的默认处理. 通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发.显然,我们可以在event()函数中实现拦截的操作.由于event()函数是 protected 的,因此,需要继承已有类.如果组件很多,就需要重写很多个event()函数.这当然相当麻烦,更不用说重写event()函数还

剖析Qt的事件机制原理

版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 QT源码解析(二)深入剖析QT元对象系统和信号槽机制 QT源码解析(三)深入剖析QT元对象系统和信号槽机制(续) QT源码解析(四)剖析Qt的事件机制原理 QT源码解析(五)QLibrary跨平台调用动态库的实现 QT源码解析(六)Qt信号槽机制与事件机制的联系 QT源码解析(七)Qt创建窗