Qt扫盲-Qt事件系统概述

news2024/11/27 23:48:38

Qt事件系统概述

  • 一、概述
  • 二、事件类型 - Event Types
  • 三、事件处理程序 - Event Handlers
  • 四、事件过滤器 - Event Filters
  • 五、发送事件 - Sending Events
    • 1. sendEvent()
    • 2. postEvent()

一、概述

在Qt中,事件是由抽象的QEvent类派生而来的对象,表示发生在应用程序内部或应用程序需要知道的外部活动的结果。

事件可以由QObject子类的任何实例接收和处理,一般这个事件在窗口程序里面使用非常频繁。

那事件是如何传递并处理的呢?

当事件发生时,Qt会通过构造适当的QEvent子类的实例来创建一个event对象来表示事件,并通过调用其 event() 函数将事件传递给QObject的特定实例(或其子类之一)。

这个 event() 函数不处理事件本身;根据交付的事件类型,它调用该特定类型事件的事件处理程序,并根据事件是被接受还是被忽略发送响应。

有些事件,如QMouseEvent和QKeyEvent,来自window系统;有些事件(如QTimerEvent)来自其他来源;有些来自应用程序本身。

在这里插入图片描述

二、事件类型 - Event Types

大多数事件类型都有特殊的类,特别是QResizeEvent、QPaintEvent、QMouseEvent、QKeyEvent和QCloseEvent。每个类都是QEvent的子类,并添加了特定于事件的函数。例如,QResizeEvent添加了size()和oldSize(),让部件能够发现它们的尺寸是如何变化的。

有些类支持多个实际事件类型。就像 QMouseEvent支持鼠标按键、双击、移动及其他相关操作。

每个事件都有一个关联的事件类型,定义在QEvent:: type中,这可以用作运行时类型信息的方便来源,以快速确定给定事件对象是从哪个子类构建的。

由于程序需要以各种复杂的方式作出反应,因此Qt的事件传递机制是灵活的。QCoreApplication::notify() 的文档简明地讲述了整个过程;Qt的季刊文章《另一看事件》(Another Look at Events)则不那么简洁。在这里,我们将解释95%的应用程序。

三、事件处理程序 - Event Handlers

传递事件的正常方式是调用虚函数。例如,QPaintEvent通过调用QWidget::paintEvent()来交付。这个虚函数负责适当地作出反应,通常是通过重绘窗口组件。如果在实现虚函数时没有执行所有必要的工作,可能需要调用基类的实现

也就是要自己去重写这个虚函数来实现功能。

例如,下面的代码处理自定义复选框部件上的鼠标左键单击,同时将所有其他按钮单击传递给基本的QCheckBox类
这个 MyCheckBox 就是继承下来QCheckBox并重写 mousePressEvent 函数的类。
:

  void MyCheckBox::mousePressEvent(QMouseEvent *event)
  {
      if (event->button() == Qt::LeftButton) {
          // handle left mouse button here
      } else {
          // pass on other buttons to base class
          QCheckBox::mousePressEvent(event);
      }
  }

如果你想替换基类的函数,你必须自己实现所有东西。然而,如果您只想扩展基类的功能,那么您可以实现您想要的,并调用基类来获得任何您不想处理的情况的默认行为。

有时候,没有特定于事件的函数,或者仅使用特定于事件的函数还不够。最常见的例子是按Tab键。通常,QWidget会拦截这些键来移动键盘焦点,但是有一些widget本身需要Tab键。

这些对象可以重新实现通用事件处理程序 QObject::event(),并在通常的处理之前或之后进行事件处理,或者完全取代函数。(如果是自己已经处理过的才去返回 true, 如果不去处理的话,后面基类的函数也会被调用的,有可能会覆盖自己写的功能)

下面就是实现的 一个按下 tab 能够循环切换tab的功能 的event()函数:

bool EventUse::event(QEvent *event)
{
    if (event->type() == QEvent::KeyPress)
    {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab)
        {
            qDebug()<<"当前页面:"<<ui->tabWidget->currentIndex();
            ui->tabWidget->setCurrentIndex((ui->tabWidget->currentIndex() + 1) % ui->tabWidget->count());
            return true;
        }
    }

    return QWidget::event(event);
}

请注意,对于所有未处理的情况,仍然调用QWidget::event(),并且返回值表明是否处理了事件:true值阻止将事件发送到其他对象

四、事件过滤器 - Event Filters

有时,一个对象需要查看并拦截传递给另一个对象的事件。例如,对话框通常希望过滤某些部件的按键;;例如,修改返回键处理。

QObject::installEventFilter()函数通过设置事件过滤器来实现这一点,使指定的过滤器对象在其QObject::eventFilter()函数中接收目标对象的事件。

事件过滤器在目标对象之前处理事件,允许它根据需要检查和丢弃事件。可以使用QObject::removeEventFilter()函数删除现有的事件过滤器。

在调用filter对象的eventFilter()实现时,可以接受或拒绝事件,并允许或拒绝事件的进一步处理。如果所有的事件过滤器都允许进一步处理事件(每次都返回false),则事件会被发送到目标对象本身。如果其中一个停止处理(通过返回true),那么目标和以后的任何事件过滤器都不会看到该事件。相当于一个短路性。这就起到了过滤的功能嘛!

就行我在一个控件的父窗口安装了 一个事件处理器,处理了然后返回false,也在该控件上安装了事件处理器,针对同一个事件,父窗口返回false 就会把事件传递给 这个 控件,如果是true,那这个控件就拿不到这个事件了。

  bool FilterObject::eventFilter(QObject *object, QEvent *event)
  {
      if (object == target && event->type() == QEvent::KeyPress) {
          QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
          if (keyEvent->key() == Qt::Key_Tab) {
              // Special tab handling
              return true;
          } else
              return false;
      }
      return false;
  }

上面的代码展示了另一种拦截发送到特定目标widget的制表键按下事件的方法。在这种情况下,过滤器会处理相关的事件,并返回true以停止后续处理。所有其他事件都被忽略,过滤器返回false,允许它们通过安装在目标部件上的其他事件过滤器发送到目标部件。

通过在QApplication或QCoreApplication对象上安装事件过滤器,也可以过滤整个应用程序的所有事件。这样的全局事件过滤器在特定于对象的过滤器之前调用。这是非常强大的,但它也减慢了整个应用程序中每个事件的交付速度;这个通常应该使用讨论的其他技术。

QObject::installEventFilter(QObject *filterObj) 的用例如下:

在这个对象上安装一个事件过滤器filterObj。例如:

monitoredObj - > installEventFilter (filterObj);

事件过滤器是一个对象,它接收发送到该对象的所有事件。过滤器可以停止事件,也可以将事件转发给这个对象。事件过滤器filterObj通过eventFilter()函数接收事件。如果事件需要被过滤(即停止),eventFilter()函数必须返回true;否则必须返回false。

如果在单个对象上安装了多个事件过滤器,则最后安装的过滤器会首先被激活。类似压栈的机制
下面是KeyPressEater类,它会吃掉被监控对象的按键行为,因为他拦截了所有的按键行为:

  class KeyPressEater : public QObject
  {
      Q_OBJECT
      ...

  protected:
      bool eventFilter(QObject *obj, QEvent *event) override;
  };

  bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
  {
      if (event->type() == QEvent::KeyPress) {
          QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
          qDebug("Ate key press %d", keyEvent->key());
          return true;
      } else {
          // standard event processing
          return QObject::eventFilter(obj, event);
      }
  }

下面是如何在两个部件上安装它:

  KeyPressEater *keyPressEater = new KeyPressEater(this);
  QPushButton *pushButton = new QPushButton(this);
  QListView *listView = new QListView(this);

  pushButton->installEventFilter(keyPressEater);
  listView->installEventFilter(keyPressEater);

例如,QShortcut类使用这种技术来拦截快捷键的按下。

警告:如果你删除了eventFilter()函数中的receiver对象,请确保返回true。如果返回false, Qt将事件发送给被删除的对象,程序将崩溃。

注意,筛选对象必须与该对象在同一个线程中。如果filterObj在不同的线程中,这个函数什么都不做。如果filterObj或该对象在调用该函数后被移动到不同的线程,则事件过滤器将不会被调用,直到两个对象再次具有相同的线程亲和性(未被删除)。

五、发送事件 - Sending Events

许多应用程序希望创建和发送自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()和QCoreApplication::postEvent()来发送事件,你可以以与Qt自己的事件循环完全相同的方式发送事件。

1. sendEvent()

sendEvent()立即处理事件当它返回时,事件过滤器和/或对象本身已经处理了事件。对于许多事件类来说,都有一个名为isAccepted()的函数,它可以告诉你事件被上一个被调用的处理程序接受还是拒绝。

2. postEvent()

postEvent()将事件发送到队列中,以便稍后分发下一次Qt的主事件循环运行时,它将分发所有发布的事件,并进行了一些优化。例如,如果有多个resize事件,它们会被压缩为一个。这同样适用于绘制事件:QWidget::update()调用postEvent(),通过避免多次重绘来消除闪烁并提高速度。

postEvent()也用于对象初始化,因为提交的事件通常会在对象初始化完成后很快分发。在实现窗口时,重要的是要意识到事件可以在其生命周期的早期交付,因此,在其构造函数中,确保在它可能接收到事件之前尽早初始化成员变量。

要创建自定义类型的事件,您需要定义一个事件编号,它必须大于QEvent::User,并且您可能需要子类化QEvent以便传递有关自定义事件的特定信息。这个就和那个 win32消息编号一样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/628861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

凌恩全新育种分析流程!助力种质资源高分文章发表!

种质资源又称遗传资源。种质是指生物体亲代传递给子代的遗传物质&#xff0c;它往往存在于特定品种之中。如古老的地方品种、新培育的推广品种、重要的遗传材料&#xff0c;野生近缘植物以及利用上述繁殖材料创造的各种遗传材料&#xff0c;都属于种质资源的范围&#xff0c;是…

为什么要使用微软的 Application Framework?

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来看一下我们为什么要使用微软的 Application Framework&#xff1f; 虽然Application Framework 并不是新观念&#xff0c;它们却在最近数年才成为 PC 平台上软件开发的主流工具。面向对象语言是具体实…

【Spring Boot 初识丨三】starter

Soring Boot 初识 【Spring Boot 初识丨一】入门实战 【Spring Boot 初识丨二】maven 本篇来讲一讲 starter 依赖项 Starter 一、定义二、启动器2.1 应用启动器2.2 生产启动器2.3 技术启动器 一、定义 启动器是一组方便的依赖关系描述符&#xff0c;它包含了一系列可以集成到应…

并行事务会引发的三个问题

并行事务是指同时运行多个事务&#xff0c;每个事务独立地执行&#xff0c;并且不会相互影响。在数据库管理系统中&#xff0c;当多个用户同时对同一个数据集进行读取或者写入的时候&#xff0c;使用并行事务可以提高系统的吞吐量和响应时间。同时&#xff0c;由于并行事务可以…

c++学习之继承

目录 一&#xff0c;为什么需要继承 二&#xff0c;继承的基本概念 三&#xff0c;派生类的定义 四&#xff0c;继承中的析构预构造 1&#xff0c;子类中的构造与析构的顺序 2&#xff0c;子类调用成员对象&#xff0c;父类的有参构造 五&#xff0c;子类与父类的同名处理…

防雪崩利器之Hystrix

Hystrix作为一个容错组件&#xff0c;本文从它的作用、熔断设计、工作流程和应用方面一一道来&#xff0c;帮助大家了解如何使用。 1、什么是灾难性雪崩效应 要讲Hystrix&#xff0c;我们就要讲一种场景&#xff0c;在微服务架构中&#xff0c;如果底层服务出现故障&#xff0…

Ubuntu搭建APM固件编译环境

文章目录 前言一、下载源码二、配置编译环境三、编译固件 前言 Ubuntu20.04 APM 4.2.3 参考链接&#xff1a; https://ardupilot.org/dev/docs/building-setup-linux.html 一、下载源码 git clone https://github.com/ArduPilot/ardupilot.git下载完之后 cd ardupilotgit s…

Lecture 18 Information Extraction

目录 Named Entity RecognitionRelation ExtractionOther IE TasksConclusion information extraction Given this: “Brasilia, the Brazilian capital, was founded in 1960.”Obtain this: capital(Brazil, Brasilia)founded(Brasilia, 1960) Main goal: turn text into str…

Linux基本指令详细介绍 【Linux】

文章目录 ls 指令( list directory contents)ls -als -alls -dlls -l (ll)ls -alF文件的类型 &#xff1a; pwd命令(Print Working Directory)cd 命令 &#xff08;change directory&#xff09;cd ..cd ~cd - touch指令mkdir指令mkdir -p ( parents) treermdir指令&#xff08…

【数据结构】哈希应用

目录 一、位图 1、位图概念 2、位图实现 2.1、位图结构 2.2、比特位置1 2.3、比特位置0 2.4、检测位图中比特位 3、位图例题 3.1、找到只出现一次的整数 3.2、找到两个文件交集 3.3、找到出现次数不超过2次的所有整数 二、布隆过滤器 1、布隆过滤器提出 2、布隆过…

javaScript蓝桥杯----商城管理系统

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 在商城管理系统中&#xff0c;超级管理员和普通管理员因为权限不同&#xff0c;登录进入后看到的菜单也会是不同的。 本题需要你完成商城管理系统中权限数据的处理。 二、准备 开始答题前&#xff0c;需要先打开本…

2023年,千万不要裸辞....

作为IT行业的大热岗位——软件测试&#xff0c;只要你付出了&#xff0c;就会有回报。说它作为IT热门岗位之一是完全不虚的。可能很多人回说软件测试是吃青春饭的&#xff0c;但放眼望去&#xff0c;哪个工作不是这样的呢&#xff1f;会有哪家公司愿意养一些闲人呢&#xff1f;…

硬件设计电源系列文章-LDO基础知识

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 例如&#xff1a; 本文主要开始讲述电源的发展 整体架构流程 提示&#xff1a;这里可以添加技术整体架构 AC/DC转换基础。为什么需要AC需要DC 技术名词解释 提示&#x…

车载测试很难吗?我靠着这套面试资料拿下了16k车载测试offer!

目录 如何写简历 项目经验 如何准备面试 车载项目的实施 常见面试题 总结&#xff1a; 车载测试通常包含以下三个方面&#xff1a; 系统测试&#xff1a;对整车系统进行测试&#xff0c;如车载电子系统、底盘系统、动力系统等。系统测试主要是评估整车各项性能指标是否达到…

STM32单片机(四)第一节:OLED调试工具

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

深入聊一下机械硬盘的相关内容

本文是《数据存储通识课》合集的一部分,本合集希望通过一系列文章科普数据存储相关技术内容。同时,本系列文章不仅仅是科普,还会进行有深度解析,理论结合实现,从代码实现层面进行剖析​ 介绍存储技术当然要从存储技术最基本的组件磁盘开始介绍了。目前市面上我们见得最多的…

shell脚本基础4——function函数、expect

文章目录 一、function函数1.1 函数的定义使用1.2 函数参数1.2.1 脚本内传参1.2.2 脚本外传参 1.3 引用局部变量1.3.1 区分局部变量1.3.2 全局变量在函数外1.3.3 全局变量在函数体1.3.4 函数体直接输出具体值 二、expect命令2.1 常用命令2.2 安装使用2.3 例一2.4 例二2.5 例三 …

Cmake工具的简单使用

引言 本篇文章讲述如何简单的使用cmake工具构建一个项目&#xff0c;帮助入门的c新手学会如何使用cmake. 我们在Clion新创建一个项目时&#xff0c;会发现&#xff0c;除了main.cpp文件之外&#xff0c;还存在一个build-debug目录和一个CMakelists.txt文件&#xff0c;如图: …

Spring Boot 集成 Redisson 实现分布式锁

Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库&#xff0c;可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面&#xff0c;例如分布式锁、分布式集合、分布式事件发布和订…

Java学习记录

引入包 把jar包复制到lib下面&#xff0c;然后右键add as library 常用方法 读取文件&#xff1a;FileInputStream获得当前路径&#xff1a;System.getProperty(“user.dir”) 快捷键 https://blog.csdn.net/W_317/article/details/114300373 常用的 生成循环&#xff1…