Qt扫盲-Qt事件系统概述

这篇具有很好参考价值的文章主要介绍了Qt扫盲-Qt事件系统概述。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、概述

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

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

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

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

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

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

Qt扫盲-Qt事件系统概述

二、事件类型 - 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消息编号一样文章来源地址https://www.toymoban.com/news/detail-477568.html

到了这里,关于Qt扫盲-Qt事件系统概述的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 水库大坝安全监测系统是由什么组成的?

    水库大坝是防洪抗灾的重要设施,它们的安全性直接关系到人民群众的生命财产安全。因此,水库大坝的安全监测必不可少。水库大坝安全监测系统是一种集成了数据采集、传输、处理和分析的技术平台,能够实时、准确地监测大坝的状态,及时发现异常情况,提供科学的依

    2024年02月16日
    浏览(43)
  • qt源码---事件系统之QCoreApplication

    上一节分析了qt和windows系统之间的消息的传递,本节着重看一下,qt内部的事件是如何传递的? 1.sendEvent函数 在使用的自定义事件时,有时需要手动抛出一个事件,常用的方式有2种,其一时阻塞式的sendEvent函数;其二是postEvent函数;sendEvent函数定义如下: 其主要是将spont设置

    2024年02月14日
    浏览(29)
  • 【Qt 底层之事件驱动系统】深入理解 Qt 事件机制:主事件循环与工作线程的交互探究,包括 QML 的视角

    在探讨 Qt 的世界时,我们不仅是在讨论一种编程框架,更是在探索一种将复杂技术细节隐藏于幕后、让开发者专注于创造性工作的艺术形式。正如著名的计算机科学家 Edsger Dijkstra 所言:“简洁是复杂性的先决条件。” 在这一章节中,我们将探讨 Qt 事件机制的基础概念,这

    2024年02月22日
    浏览(43)
  • Qt扫盲-Model/View入门

    每个UI开发人员都应该了解ModelView编程, 表格格控件、列表格控件和树控件是gui中经常使用的组件。这些控件有两种不同的方式访问它们的数据。 1.传统方法 传统的方法就是让控件本身去储存数据,在控件内部有数据容器,这种方法非常直观,但是,在许多重要的应用程序中

    2024年02月13日
    浏览(40)
  • Qt扫盲-Reentrant和线程安全

    在整个文档中, Reentrant 和 thread-safe 术语用于标记类和函数,指明如何在多线程应用程序中使用它们: 一个 thread-safe的函数可以从多个线程同时调用,即使调用使用了共享数据,因为对共享数据的所有引用都是序列化的。 一个 Reentrant 函数也可以从多个线程同时调用Reentrant函

    2023年04月23日
    浏览(27)
  • Java基础:简单工厂模式、工厂方法模式和抽象工厂模式综合概述

    简单工厂模式、工厂方法模式和抽象工厂模式是面向对象设计中用来实现对象创建灵活性的三种不同形式的工厂模式。它们各自有其特点、适用场景及优缺点。以下是它们之间的区别以及对应的适用场景,以及示例说明: 简单工厂模式 定义 : 简单工厂模式提供一个静态方法

    2024年04月25日
    浏览(23)
  • 跟着cherno手搓游戏引擎【4】窗口抽象、GLFW配置、窗口事件

    在vendor里创建GLFW文件夹: 在github上下载,把包下载到GLFW包下。 GitHub - TheCherno/glfw: A multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input修改SRC/premake5.lua的配置:12、13、15、36、37、38、39、40行的代码是新加上去的: GLFW中的premake5.lua:  如出现此BUG:请找GLFW中的premake5文件,

    2024年01月21日
    浏览(25)
  • C#:单例,闭包,委托与事件,线程,Parallel,Params,扩展方法,接口与抽象类

    在对泛型的约束中,最常使用的有where 和 new。 其中where是约束所使用的泛型,该泛型必须是where后面的类,或者继承自该类。 new()说明所使用的泛型,必须具有无参构造函数,这是为了能够正确的初始化对象 1.泛型约束class Singleton where T : class,new() 2.静态对象没创

    2024年01月16日
    浏览(79)
  • 【Qt6】列表模型——抽象基类

    列表模型(Item Model),老周没有翻译为“项目模型”,因为 Project 和 Item 都可以翻译为“项目”,容易出现歧义。干脆叫列表模型。这个模型也确实是为数据列表准备的,它以 MVC 的概念为基础,在原始数据和用户界面视图之间搭建桥梁,使两者可以传递数据(提取、修改)

    2024年02月09日
    浏览(29)
  • 鸿蒙系统扫盲(三):鸿蒙开发用什么语言?

    我们常说鸿蒙开发,但是其实鸿蒙开发分为两个方向: 一个是系统级别的开发,比如驱动,内核和框架层的开发,这种开发以C/C++为主 还有一个是应用级别的开发,在API7以及以下,还是支持Java的,从API8开始,只能用Arkts,js或着C++开发了,我们这篇文章重点讲下应用级别的

    2024年02月04日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包