Qt·事件处理机制

news/2024/11/17 23:34:06/

思维导读

一、事件简介

  QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发。QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期。

  常见的QT事件类型如下:

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

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

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

 滚轮事件: 鼠标滚轮滚动

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

 定时事件: 定时器到时

 焦点事件: 键盘焦点移动

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

 移动事件: widget的位置改变

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

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

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

  QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent,用于描述程序内部或外部发生的动作,任意的QObject对象都具备处理QT事件的能力。

文章最后为大家准备了Qt资料
↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡↡

二、QT事件产生

(1)操作系统事件

  操作系统将获取的事件,比如鼠标按键,键盘按键等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系统的消息队列中,Qt事件循环的时候读取消息队列中的消息,转化为QEvent并被分发到相应的QWidget对象,相应的QWidget中的event(QEvent *)进行事件处理会对事件进行处理,event(QEvent *)会根据事件类型调用不同的事件处理函数,在事件处理函数中发送QT预定义的信号,最终调用信号关联的槽函数。

  GUI应用程序的事件处理:

  A、QT事件产生后会被立即发送到相应的QWidget对象

  B、相应的QWidget中的event(QEvent *)进行事件处理

  C、event(QEvent *)根据事件类型调用不同的事件处理函数

  D、在事件处理函数中发送QT预定义的信号

  E、调用信号关联的槽函数

(2)Qt应用程序自己产生

  程序产生事件有两种方式, 一种是调用QApplication::postEvent(), 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理;

  另一种方式是调用sendEvent()函数,事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是阻塞型的。

 sendEvent()中事件对象的生命期由Qt程序管理,支持分配在栈上和堆上的事件对象;postEvent()中事件对象的生命期由Qt平台管理,只支持分配在堆上的事件对象,事件被处理后由Qt平台销毁。

三、Qt事件处理

(1)事件调度

  事件有两种调度方式,同步和异步。

  Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环,先处理Qt事件队列中的事件, 直至为空,再处理系统消息队列中的消息, 直至为空, 处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理。

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

(2)事件通知、派发

  事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便。通过事件过滤器, 可以让一个对象侦听拦截另外一个对象的事件。事件过滤器实现如下: 在所有Qt对象的基类QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObject(A)给另一个QObject(B)安装了事件过滤器后, B会把A的指针保存在eventFilters中。在B处理事件前,会先去检查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() 函数, 或是安装事件过滤器。

(3)事件的转发

  对于某些类别的事件,如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口。Qt中和事件相关的函数通过两种方式相互通信,一种是QApplication::notify(), QObject::eventFilter(), QObject::event()通过返回bool值来表示是否已处理;另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,只用于event()函数和特定事件处理函数之间的沟通,而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件。

(4)事件的处理和过滤

  QT提供了五种不同级别的事件处理和过滤:

 A、重写特定事件处理函数.

 最常见的事件处理办法就是重写mousePressEvent(), keyPressEvent(), paintEvent() 等特定事件处理函数。

  B、重写event()函数.

 重写event()函数时, 需要调用父类的event()函数来处理不需要处理或是不清楚如何处理的事件。

 return QWidget::event(event);

 C、在Qt对象上安装事件过滤器

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

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

 D、给QAppliction对象安装事件过滤器

如果给QApplication对象装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前eventFilter()。在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃。

 E、继承QApplication类,并重载notify()函数

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

四、自定义事件和eventFilter示例

class DefineEvent : public QEvent
{
public:const static QEvent::Type DefineType = static_cast<QEvent::Type>(QEvent::User + 0xFF);explicit DefineEvent(QString data) : QEvent(DefineType){m_data = data;}QString data() {return m_data;}private:QString m_data;
};
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QLineEdit>
#include "StringEvent.h"
#include <QMouseEvent>
#include <QDebug>
#include <QApplication>class Widget : public QWidget
{Q_OBJECTQLineEdit m_edit;
public:Widget(QWidget *parent = 0): QWidget(parent), m_edit(this){m_edit.installEventFilter(this);}bool event(QEvent* evt){if( evt->type() == QMouseEvent::MouseButtonDblClick ){qDebug() << "event: Before sentEvent";StringEvent e("D.T.Software");QApplication::sendEvent(&m_edit, &e);qDebug() << "event: After sentEvent";}return QWidget::event(evt);}bool eventFilter(QObject* obj, QEvent* evt){if( (obj == &m_edit) && (evt->type() == StringEvent::TYPE) ){StringEvent* se = dynamic_cast<StringEvent*>(evt);qDebug() << "Receive: " << se->data();m_edit.insert(se->data());return true;}return QWidget::eventFilter(obj, evt);}~Widget(){}
};#endif // WIDGET_H自定义事件类型可以使用registerEventTypeQEvent::Type ImageLoadedEvent::evType(){if(s_evType == QEvent::None){s_evType = (QEvent::Type)registerEventType();}return s_evType;
}

http://www.ppmy.cn/news/76434.html

相关文章

STM32入门100步(第1步~第5步)

第一章 基础知识与平台建立 第1~2步 是时候学ARM了 1.1 为啥学? 什么是ARM? ARM处理器是英国Acorn有限公司设计的低功耗成本的第一款RISC(精简指令集)微处理器。全称为Advanced RISC Machine。ARM是一种性能出众的32位处理器的内核架构。1991年,一家叫ARM的公司在英国…

不需要策略模式也能避免满屏if/else

满屏if/else java 复制代码 public static void main(String[] args) { int a 1; if(a 1){ System.out.println("执行a1的逻辑"); }else if (a 2){ System.out.println("执行a2的逻辑"); }else if (a 3){ System.out.println("执行a3的逻辑&quo…

R语言实践——使用 rWCVP 生成自定义清单

使用 rWCVP 生成自定义清单 介绍1. 特有物种清单2. 近特有物种清单2.1 在塞拉利昂和另一地区出现的物种2.2 在塞拉利昂和相邻地区出现的物种 3. 生成自定义报告 介绍 除了允许用户从世界维管植物名录&#xff08;WCVP&#xff09;创建清单外&#xff0c;rWCVP还提供了修改清单…

python中的类型转换

文章目录 类型转换简介int()float()str()bool() 类型转换简介 所谓的类型转换&#xff0c;将一个类型的对象转换为其他对象。 类型转换不是改变对象本身的类型&#xff0c;而是将对象的值转换为新的对象。 类型转换四个函数 int() 、 float() 、 str() 、 bool() int() int()…

Autosar-以太网(UDP网络管理)

文章目录 前言一、网络协同机制二、运行模式1.状态转换流程Network Mode(正常工作模式)Repeat Message State(重复消息状态)Ready Sleep State(就绪睡眠状态)Normal Operation State(正常运行状态)Prepare Bus-Sleep Mode(准备睡眠模式)Bus-Sleep Mode(睡眠模式/低功…

C#入门笔记(需有C基础)

目录 一、1.Console.WriteLine()2.Length()3.Trim()、TrimStart()、TrimEnd()4.Replace()5.ToUpper()、ToLower()6.Contains()7.StartsWith()、EndsWith()8.decimal 二、1.数组的使用2.IndexOf()3.Sort()4.Count5.斐波那契数列 一、 1.Console.WriteLine() //1.输出:Hello Fr…

Word控件Spire.Doc 【列表】教程:在 Word 文档中插入列表

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

港科夜闻|叶玉如校长在大湾区科学论坛主论坛发表「为应对人口老龄化贡献大湾区力量」主旨演讲...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长在大湾区科学论坛主论坛发表「为应对人口老龄化贡献大湾区力量」主旨演讲。她指出&#xff0c;中国已成为世界上老年人口最多、老龄化速度最快的国家之一&#xff0c;预计到2025年&#xff0c;65岁及以上人口3…