Qt之事件机制1

https://yq.aliyun.com/articles/308755#

未命名文件 (1).jpg

一、概览

  • 1、重载特定事件函数, 比如: mousePressEvent()keyPressEvent()paintEvent()
  • 2、重新实现QObject::event()。这一般用在Qt没有提供该事件的处理函数时。也就是,我们增加新的事件时。
  • 3、安装事件过滤器。比如用 objA 过滤 objB 的事件,即事件到达 objB 之前,先交由 objA 处理。只需两个步骤:
    • 调用objB->installEventFilter(objA)
    • 重载objA::eventFilter()
  • 4、在QApplication上安装事件过滤器。
  • 5、重新实现QApplicationnotify()方法。 Qt使用notify()来分发事件。要想在任何事件处理器捕获事件之前捕获事件,唯一的方法就是重新实现QApplicationnotify()方法。

    二、Qt事件机制

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

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

三、Qt事件的类型

  • 键盘事件:按键按下和松开
  • 鼠标事件:鼠标移动,鼠标按键的按下和松开
  • 拖放事件:用鼠标进行拖放
  • 绘屏事件:重绘屏幕的某些部分
  • 定时事件:定时器到时
  • 鼠标进入和离开事件:鼠标移入或移出widget
  • 窗口大小改变事件:widget大小的改变
  • 窗口显示和隐藏事件:widget显示和隐藏
  • 窗口事件:窗口是否为当前窗口

事件的定义

  • 事件:某个“动作”的完成后,需让某个对象知道而发送的消息。(个人观点)
  • 解释:此时的“动作”并非通常意义所指的动作,而是广义的“动作”,是主动和被动的总和。
  • 例:两个窗体A和B,当A为最小化状态时,我们使它最大化,这就会让A主动产生一个重绘事件;当A和B非最小化状态,且B位于A窗体之上时,我们让B最小化,那么刚才被B遮挡的A窗体就会被动地产生一个重绘事件。

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

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

  • 系统产生的。
    • 通常是window system把从系统得到的消息,比如鼠标按键、键盘按键等,放入系统的消息队列中,Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理。
  • Qt程序自身产生的
    • 调用QApplication::postEvent()。例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理。
    • 调用sendEvent()函数。这时候事件不会放入队列,而是直接被派发和处理,QWidget::repaint()函数用的就是这种方式。

四、事件的处理过程

1.异步:

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

  1. while (!app_exit_loop) {
  2. while (!postedEvents) {
  3. processPostedEvents()
  4. }
  5. while (!qwsEvnts) {
  6. qwsProcessEvents();
  7. }
  8. while (!postedEvents) {
  9. processPostedEvents()
  10. }
  11. }

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

2.同步

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

五、事件的派发和处理

https://doc.qt.io/qt-5/eventsandfilters.html

5.1 Qt中事件过滤器的概念

官方说明:

  • 1.事件过滤器可以对需要的组件接收到的事件进行过滤,以及监控
  • 2.任意的QObject对象都可以作为事件过滤器使用事件过滤器的实现,需要重写eventFilter()函数
  • 3.组件要想被监控,则需要通过installEventFilter()安装事件过滤器

事件过滤器是Qt中一个独特的事件处理机制,功能强大而且使用起来灵活方便。通过它,可以让一个对象侦听拦截另外一个对象的事件。

事件过滤器是这样实现的:

  • 在所有Qt对象的基类:QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObject(qobjA)给另一个QObject(qobjB)安装了事件过滤器之后,qobjB会把qobjA的指针保存在eventFilters中。在qobjB处理事件之前,会先去检查eventFilters列表,如果非空,就先调用列表中对象的eventFilter()函数。
  • 一个对象可以给多个对象安装过滤器;同样,一个对象能同时被安装多个过滤器。
  • 在事件到达之后,这些过滤器以安装次序的反序被调用。事件过滤器函数(eventFilter()) 返回值是bool型:
    • 如果返回true,则表示该事件已经被处理完毕,Qt将直接返回,进行下一事件的处理;
    • 如果返回false,事件将接着被送往剩下的事件过滤器或是目标对象进行处理。

5.1 Qt中事件的派发

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

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

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

六、事件的转发

对于某些类别的事件,如果在整个事件的派发过程结束后还没有被处理,那么这个事件将会向上转发给它的父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()这样的特定事件处理函数。以按键事件为例,一个典型的处理函数如下:

  1. void imageView::keyPressEvent(QKeyEvent *event)
  2. {
  3. switch (event->key()) {
  4. case Key_Plus:
  5. zoomIn();
  6. break;
  7. case Key_Minus:
  8. zoomOut();
  9. break;
  10. case Key_Left:
  11. // …
  12. default:
  13. QWidget::keyPressEvent(event);
  14. }
  15. }

2.重载event()函数

通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(像keyPressEvent())处理它。比如,当我们想改变tab键的默认动作时,一般要重载这个函数。在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数。当我们重载event()函数时,需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件。
下面这个例子演示了如何重载event()函数,改变Tab键的默认动作:(默认的是键盘焦点移动到下一个控件上)

  1. bool CodeEditor::event(QEvent * event)
  2. {
  3. if (event->type() == QEvent::KeyPress){
  4. QKeyEvent *keyEvent = (QKeyEvent *) event;
  5. if (keyEvent->key() == Key_Tab) {
  6. insertAtCurrentPosition('\t');
  7. return true;
  8. }
  9. }
  10. return QWidget::event(event);
  11. }

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

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

  • 首先调用B的installEventFilter(const QOject*obj),以A的指针作为参数。这样所有发往B的事件都将先由A的eventFilter()处理。
  • 然后,A要重载QObject::eventFilter()函数,在eventFilter()中书写对事件进行处理的代码。

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

  1. MainWidget::MainWidget() {
  2. CodeEditor * ce = new CodeEditor( this, code editor”);
  3. ce->installEventFilter( this );
  4. }
  5. bool MainWidget::eventFilter( QOject *target , QEvent * event ) {
  6. if( target == ce ) {
  7. if( event->type() == QEvent::KeyPress ) {
  8. QKeyEvent *ke = (QKeyEvent *) event;
  9. if( ke->key() == Key_Tab ) {
  10. ce->insertAtCurrentPosition('\t');
  11. return true;
  12. }
  13. }
  14. }
  15. return false;
  16. }

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了。比如我们可以改变它的行为,在鼠标按键按下的时候(mousePressEvent)就触发clicked()的signal而不是通常在释放的(mouse ReleaseEvent)时候。

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

若有收获,就点个赞吧

0 人点赞

  • 书签
  • 添加书签 移除书签
  • KDAB
    • 1.Converting Enums to and from String
  • 练习CPP设计模式 2017
    • 第一章 定义案例研究
    • 第二章 分解
    • 第三章 Stack模块
    • 第四章 命令调度器
    • 第五章 命令行界面
  • practical-cpp-design-2017
  • Qt之安装
    • Qt VS2019开发环境安装
  • Qt之位置
    • Qt之位置
  • Qt之窗口
  • Qt之核心特点
    • Qt之属性系统
    • Qt之元对象系统
    • Qt之信号与槽
  • Qt之QtCreator使用
    • QtCreator 添加自定义注释
    • QtCreator QSS语法高亮
    • QtCreator 代码风格 - windows
    • QtCreator 代码风格 - Linux
    • QtCreator 插件Todo
    • QtCreator 快捷键
    • QtCreator 添加源码调试
    • QtCreator 使用Heob检测内存泄漏
  • Qt之一些项目文件
    • Qt资源系统 *.prc文件
    • Qt项目管理文件 *.pro文件
    • Qt Windows资源文件 *.rc文件
  • Qt之工具类
    • QStandardPaths - 系统标准路径类
    • QRandomGenerator - 生成随机数
  • Qt之工具-完整代码
    • Qt窗体缩放移动 跨平台完整版本
  • Qt之文件系统
    • Qt5删除隐藏目录文件
    • 监视文件目录
    • 文件、目录操作
    • 获得系统信息
  • Qt之命令行
    • 解析命令行
  • Qt之布局
    • Qt之QSplitter
      • QSplitter QSS样式
  • Qt之菜单
    • Qt之QAction
      • QAction类
      • QActionGroup类详解
      • QWidgetAction类详解
  • Qt之QTextStream
    • 1.QTextStream文本流
    • 2.QTextStream输出流
    • 3.QTextStream操纵算子
  • Qt之QWidget
    • Qt之QWidget
  • Qt之QLabel
    • QLabel
    • QLabel QSS美化
    • QLabel 增加点击事件
  • Qt之QLineEdit
    • QLineEdit
    • QLineEdit QSS样式
  • Qt之QPushButton
    • QPushButton QSS样式
  • Qt之QGroupBox
    • 可伸缩的QGroupBox控件
  • Qt之QComboBox
    • QComboBox添加复选框
    • QComboBox QSS样式
  • Qt之QPainter
    • QPainter paintEvent
  • Qt之QListWidget
    • QListWidget实现缩略图展示
    • QListWidget
    • QListWidget QSS样式
  • Qt之QTreeWidget
    • QTreeWidget QSS样式
    • QTreeWidget 将json的某字段转成树
    • QTreeWidget 查找节点
    • QTreeWidget 迭代节点
  • Qt之QTableWidget
    • QTableWidget
    • QTableWidget QSS样式
    • QTableWidget 显示提示消息气泡
  • Qt之QTimer
    • Qt之 QTimer
    • Qt计时器 QElapsedTimer
  • Qt之QToolTip
    • QToolTip
    • QToolTip QSS样式
  • Qt之QSetting
    • QSetting
    • QSetting读写ini配置文件
  • Qt之QProgressBar
    • QProgressBar
  • Qt之QMessageBox
    • QMessageBox按钮改中文
  • Qt之QSystemTrayIcon(系统托盘)
    • QSystemTrayIcon
  • Qt之Json
    • Json的查找
    • JSON解析的简单封装
    • JSON生成与解析
  • Qt之XML
    • 解析MacOS下 .plist配置文件
  • Qt之事件机制
    • Qt之事件机制1
    • Qt之事件机制
  • Qt线程
    • Qt之线程
    • Qt之线程池QThreadPool
  • Qt之进程间通信
    • Qt之QSharedMemory
  • Qt之网络
    • QTcpSocket及TCP粘包分析
  • Qt之国际化
    • Qt之多语言
  • Qt之QSS
    • QSS padding
    • QSS 加载QSS文件
    • QSS 编辑预览工具
    • QSS 样式表
  • Qt之QGraphics
    • Qt之图形视图框架
    • 常见的QGraphicsItem
    • 在QGraphicsScene中嵌入QWidget
    • 部件和布局 - QGraphicsWidget
    • QGraphicsScene 管理 QGraphicsItem(单击/选择/移动/缩放/删除)
    • QGraphicsItem 分组
    • 让 QGraphicsItemGroup 中的 item 处理自己的事件
    • QGraphicsItem的类型检测与转换
    • QGraphicsItem 如何使用信号/槽
  • Qt之QtCharts
    • QtCharts 官方示例
  • Qt之Animation(动画)
    • Qt之动画框架
    • Qt之QPropertyAnimation
    • Qt之QParallelAnimationGroup
    • Qt之QSequentialAnimationGroup
    • Qt之窗口动画(下坠、抖动、透明度)
  • Qt之打包
    • Qt之打包(Windows Linux)
  • Qt之数据库QtSQL模块
    • Qt之数据库简介
  • Qt之串口
    • Qt之串口一、通过串口同步接收数据
    • Qt之串口一、获取有关系统中串行设备的信息
    • 2.跨平台串口通信类库
    • 1.串口
  • Qt之QML
    • 初识QML
    • 一、QML快速入门
      • 1. QML语法
      • 2.基本元素
      • 4.简单的转换(Simple Transformations)
      • 5. 定位元素(Positioning Element)
      • 6. 布局元素(Layout Items)
      • 7. 输入元素(Input Element)
  • Qt Example(官方示例)
    • Qt模型视图
      • QDataWidgetMapper
    • Qt SQL
      • Book
    • star delegate
    • Spin Box Delegate示例
  • Qt 捕获崩溃信息方法
  • Qt面试
    • Qt面试题
暂无相关搜索结果!
    展开/收起文章目录

    PHP网站源码大运网站搜索优化横岗网站关键词优化广州如何制作网站同乐企业网站建设深圳网站建设大芬网站seo优化观澜优化罗湖网页设计光明网站优化推广布吉英文网站建设西乡网站改版石岩网站设计布吉网站推广工具吉祥建设网站塘坑关键词按天扣费永湖网站制作设计坪山优秀网站设计龙岗至尊标王坂田百姓网标王石岩关键词按天收费吉祥关键词排名包年推广松岗模板推广平湖seo网站优化罗湖建网站大芬seo排名吉祥关键词按天计费西乡网站关键词优化坪山关键词按天计费大芬网站优化推广坪地seo网站推广歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

    PHP网站源码 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化