Qt事件处理的几种方式

2023-05-16

Qt提供了5种事件处理和事件过滤的方法:

1、重写事件处理器函数

  这是大部分情况最常用的一种,如重写 paintEvent()、mousePressEvent()、keyPressEvent() 等事件处理器虚函数。

2、重写 QObject::event() 函数

  通过重写 event() 函数可以在事件到达特定的事件处理器之前处理它们。这种方式常用于拦截 Tab 键的处理(Tab 键默认的意义是焦点切换,但是对于某些控件这个可能要用来完成文本缩进之类的工作),以及没有事件处理器的不常见事件如 QEvent::HoverEnter 。当重写 event() 时,对于没有处理的事件必须调用基类的 event() 函数。

3、安装事件过滤器

  需要被监视的目标对象调用 installEventFilter() 注册监视对象后,被监视目标的所有事件都会先被监视对象的过滤器 eventFilter() 拦截,可以在这个函数里面对监视目标的某个事件做特殊处理。一个对象可以安装多个事件过滤器,事件过滤器的执行顺序和安装顺序相反。会从最近安装的那个 eventFilter() 开始执行。

4、在 QApplication 对象中安装事件过滤器·

  一旦在 qApp(全局唯一的运用程序对象) 中注册了事件过滤器,则运用程序中所有对象的每一个事件在发送到其他事件过滤器之前,都会被注册的那个监视器拦截。这对于调试非常有用。(就是定义一个 QObject 类的子类,重写 eventFilter() 函数,实例化一个过滤器对象 filter,然后 qApp->installEventFilter(filter) )

5、继承 QApplication 重写 notify() 函数

  Qt 是通过调用 QApplication::notify() 函数来发送事件的,重写这个函数即可在事件过滤器之前拦截所有事件。

/********************************

1.简述

个人认为,事件机制是Qt最难以理解且最为精妙的一部分。事件主要分为两种:

  1. 在与用户交互时发生。比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等。
  2. 系统自动发生,比如计时器事件(timerEvent)等。

在发生事件时(比如说上面说的按下鼠标),就会产生一个QEvent对象(这里是QMouseEvent,为QEvent的子类),这个QEvent对象会传给当前组件的event函数。如果当前组件没有安装事件过滤器(这个下文会提到),则会被event函数发放到相应的xxxEvent函数中(这里是mousePressEvent函数)。

Qt中所有的事件类都继承于QEvent类

 

这个QEvent对象会有各种各样的属性,这是由用户与界面交互时产生的。xxxEvent函数可以对其进行不同的处理(比如说是鼠标左键按下还是右键?)。查看帮助文档,可以看到QMouseEvent类有以下枚举。

在QtCreator中查看帮助文档

那么就可以在mousePressEvent中根据这个QEvent对象的这些枚举值来进行不同的处理,比如

class myLabel : public QLabel
{
protected:
    void mousePressEvent(QMouseEvent *event);
};

void myLabel::mousePressEvent(QMouseEvent *event)
{
    if(event->Buttons == LeftButton)
    {
        //do sth
    }
    else if(event->Buttons == RightButton)
    {
        //do sth
    }
}

可以看到,我们首先需要先创建一个自己的QLabel类,并继承于Qt的QLabel类,然后并重写相应的xxxEvent函数(这些事件处理函数都是虚函数)。

Qt程序的main函数中需要创建一个QApplication对象,然后调用exec函数。这将令程序进入一个死循环,并不断监听应用程序的事件,发生事件时就生成一个QEvent对象。这又称为事件循环。

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.show();

    return app.exec();
}

2.事件的分发:event函数

上面提到的xxxEvent函数,称为事件处理器(event handler)。而event函数的作用就在于事件的分发。如果想在事件的分发之前就进行一些操作,比如监听某个按键的按下。

bool myWidget::event(QEvent *e)
{
    if (e->type() == QEvent::KeyPress) 
    {
        //将QEvent对象转换为真正的QKeyEvent对象
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
        if (keyEvent->key() == Qt::Key_Tab) 
        {
            qDebug() << "You press tab.";
            return true;
        }
    }
    //按照原来的流程来进行事件的分发
    return QWidget::event(e);
}

在上面的程序中,myWidget是QWidget的子类。同样的,它的event函数是一个虚函数,带有一个QEvent类型的参数。当系统产生QEvent对象时,就会传入这个函数并调用。函数的返回值是bool类型,返回值不同有不同的意义。

如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。

Qt系统在处理事件时,有一种机制叫事件传播机制。也就是说,在子组件(比如说一个QButton)中发生的事件,调用了子组件的event函数之后,还会调用父组件(比如说QWidget)的event函数。event函数的返回值就用于控制这样的一个过程。

https://blog.csdn.net/tqs_1220/article/details/82563070

需要注意的是,重写event函数之后最好返回父类的event函数来处理其他的事件分发,不然就只能处理自己定义的事件。

bool myTextEdit::event(QEvent *e)
{
    if (e->type() == QEvent::KeyPress) 
    {
        //将QEvent对象转换为真正的QKeyEvent对象
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
        if (keyEvent->key() == Qt::Key_Tab) 
        {
            qDebug() << "You press tab.";
            return true;
        }
    }
    //直接返回false
    return false;
}

在这个例子中,因为没有调用父类QTextEditevent函数,所以只能处理Tab的情况,你再按其他按键就啥反应都没有了。同样,事件也不能进行传播。

事件过滤器(Even Filter)

某些应用场景下,需要拦截某个组件发生的事件,让这个事件不再向其他组件进行传播,这时候可以为这个组件或其父组件安装一个事件过滤器(evenFilter)。

QObject有一个虚函数,原型如下

virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

可以看到,函数有两个参数,一个为具体发生事件的组件,一个为发生的事件(产生的QEvent对象)。当事件是我们感兴趣的类型,可以就地进行处理,并令其不再转发给其他组件。函数的返回值也是bool类型,作用跟even函数类似,返回true为不再转发,false则让其继续被处理。

实际使用中,我们需要对QObject组件调用installEvenFilter函数,即为组件安装过滤器,才能使用事件过滤器这个机制。这样,该组件及其子组件的事件就会被监听。这个机制的好处在于不用像重写QEvent和xxxEvent函数一样需要继承Qt的内置类。

void QObject::installEventFilter ( QObject * filterObj );

下面举一个例子。MainWindow中有一个QTextEdit控件,我们拦截它的键盘按下的事件。这样处理之后,会在输出窗口打印出按下的键位,但不会在控件上显示。这表明事件已被拦截,不会去调用even函数。

class MainWindow : public QMainWindow
{
public:
    MainWindow();
protected:
    bool eventFilter(QObject *obj, QEvent *event);
private:
    QTextEdit *textEdit;
};
 
MainWindow::MainWindow()
{
    textEdit = new QTextEdit;
    setCentralWidget(textEdit);
    
    textEdit->installEventFilter(this);
}
 
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == textEdit) 
    {
        if (event->type() == QEvent::KeyPress) 
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            qDebug() << "you press" << keyEvent->key();
            //事件不再进行传播,拦截
            return true;
        } 
        else
        {
            return false;//继续传播
        }
    } 
    else 
    {
        //当不确定是否继续传播时,按照父类的方法来处理
        //即调用父类的evenFilter函数
        return QMainWindow::eventFilter(obj, event);
    }
}

同样的,even函数能干的事情,evenFilter也能干。比如说上面的处理键盘按下Tab键。

bool myObject::eventFilter(QObject *object, QEvent *event)
{
    if (object == target && event->type() == QEvent::KeyPress) 
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Tab) 
        {
            qDebug() << "You press tab.";
            //拦截
            return true;
        } 
        else 
        {
            //不进行拦截
            return false;
        }
    }
    //不进行拦截
    return false;
}

我们可以对QApplication或者QCoreApplication对象添加事件过滤器。这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。这种行为会严重降低整个应用程序的事件分发效率,要看具体情况使用。

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

总结

Qt中使用事件机制,每一种事件对应一个事件处理器,比如:

  • mouseEvent()
  • keyPressEvent()
  • etc....

发生事件时会生成一个QEvent对象,则需要even函数进行分发,来调用相应的事件处理器

switch (event->type()) 
{
    case QEvent::MouseMove:
        mouseMoveEvent((QMouseEvent*)event);
        break;
    // ...
}

事件过滤器(evenFilter)可以令事件进行拦截,阻止其传播,从而实现某些功能。

另外,有一种一般很少使用的方法,即去重写这么一个函数

virtual bool QCoreApplication::notify ( QObject * receiver, QEvent * event );

该函数原实现相当于让组件调用even函数,即receiver->event(event)。这相当于全局的事件过滤器,且不会受到多线程的限制。

那么,在使用Qt的事件机制时,应该按照以下思路进行

  • 重写paintEventmousePressEvent等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
  • 重写event函数。event函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
  • 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
  • QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,但会有多线程问题。
  • 重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt事件处理的几种方式 的相关文章

  • tightvncserver中文版,3步实现tightvncserver中文版下载

    tightvnc是一个免费的软件控制软件开发包 xff0c 您可以通过鼠标 键盘操作远程机器 tightvnc对个人和商业应用都免费 xff0c 开放所有源代码 Ttightvnc可以运行在Windows Unix Mac系统上 xff0c
  • c语言-指针的本质和使用

    指针的本质和使用 一 地址概念 我们首先来看看地址 是个什么概念 每一个变量都有一个内存位置 xff0c 每一个内存位置都定义了可使用 amp 运算符访问的地址 xff0c 它表示了在内存中的一个地址 画图简单说明 xff1a 二 什么是指
  • C++类与对象概念详解

    一 简介 1 类用于指定对象的形式 xff0c 它包含了数据表示法和用于处理数据的方法 类中的数据和方法称为类的成员 函数在一个类中被称为类的成员 2 类提供了对象的蓝图 xff0c 所以基本上 xff0c 对象是根据类来创建的 声明类的对
  • C++类对象的创建与释放过程详解

    类的定义与实例化 类对象的创建方法 类对象的创建过程 类对象的释放过程 析构函数 缺省析构函数 类的定义与实例化 类对象的创建方法 1 在栈上创建 类名 对象 无参创建方法 类名 对象 实参 有参创建方法 2 在堆上创建 类名 对象指针 6
  • c++中类创建对象的两种方式

    class A xff1b 1 A a xff1b 2 A a 61 new A 第一种方式对象数据存在栈中 xff0c 是局部变量 xff1b 第二种方式方式申请了动态内存 xff0c 即对象数据在堆区
  • C#里面SQLite读取数据的操作

    挂载表格时候用 public static DataSet Query string SQLString using SQLiteConnection connection 61 new SQLiteConnection connectio
  • C#-Hook钩子实例

    一 写在最前 本文的内容只想以最通俗的语言说明钩子的使用方法 xff0c 具体到钩子的详细介绍可以参照下面的网址 xff1a http www microsoft com china community program originalar
  • C#-invoke与sendmessage,findWindow的阻塞实验

    虽然模态对话框阻塞了顶层窗体 xff0c 但是仍然可以通过findwindow查找顶层窗体句柄 xff0c 并使用sendmessage向顶层窗体发送消息并执行消息内的逻辑 xff08 验证实例如下 xff09 public partial
  • c#中使用Marshal手动在堆中创建非托管内存并使用

    使用 Marshal 做出可以快速释放内存的大数组 需要不断申请一段大内存数组 xff0c 然后就释放他 xff0c 但是 C 对于大内存不是立刻释放 xff0c 所以就存在一定的性能问题 在博客园看到了一位大神使用 Marshal 做出快
  • C#-利用Marshal类实现序列化

    主要是使用Marshal类里的两个方法 xff1a 第一个是StructureToPtr xff0c 将数据从托管对象封送到非托管内存块 第二个是PtrToStructure xff0c 将数据从非托管内存块封送到新分配的指定类型的托管对象
  • c#中事件注册多个委托并获取委托列表

    using System namespace 委托和事件 internal class Program private static void Main string args Action action 61 One action 43
  • vnc怎么注册,教你3步解决vnc怎么注册

    VNC Virtual Network Computing 是一款优秀的远程控制工具软件 xff0c 由著名的AT amp T的欧洲研究实验室开发的 VNC是在基于UNIX和Linux操作系统的免费的开放源码软件 xff0c 远程控制能力强
  • C#中4种深拷贝方法实例

    1 xff1a 利用反射实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static T DeepCopy lt T gt T obj 如果是字符串或值类型则直接返回 if obj is string
  • c#子线程和主线程创建窗体时顶层显示的区别

    主线程 1 设置TopLevel xff0c Topmost bringfront属性 子线程 2 在可在Load事件里注册循环设置TopLevel xff0c Topmost bringTofront属性的方法 xff0c 如下 frm
  • 模态对话框阻塞主线程的话不影响其他线程操作主线程控件(不阻塞)

    Task Factory StartNew 61 gt Thread Sleep 5000 this Invoke new Action 61 gt this button7 Text 61 34 aaaaaaaaaaaaaa 34 Mes
  • Ubuntu18.04.3虚拟机安装步骤图文教程

    虚拟机的安装步骤就不教学了 xff01 自行百度 xff01 接下来 教学ubuntu18 04 3desktop amd64版本的安装和配置教程 软件下载地址详细介绍 xff1a ubuntu 18 04 3 下载速度很快 xff1a 2
  • Linux安装QTCreator问题解决汇总

    关于Ubuntu18版本下新安装Qtcreator编译报错 cannot run compiler clang 43 43 output的解决办法 前两次本以为记住了 xff0c 结果第三次再次在Linux下安装qtcreator依然捣鼓搞
  • Qt路径中常用字符“./”、“../”、“/”、“*”的含义

    在Qt中进行编程时 xff0c 当我们需要调用某些路径时候 xff0c 特别是调用QDir类进行访问时 xff0c 会定义一系列的字符串 xff0c 并将这些字符串赋值给路径 xff0c 这时候就出现了一个问题 xff0c 这些字符串所代表
  • Qt工程pro文件的配置和头文件和库的添加

    Qt开发中 xff0c pro文件是对正工程所有源码 编译 资源 目录等的全方位配置的唯一方式 xff0c pro文件的编写非常重要 xff0c 以下对几个重要项进行说明 xff08 win和linux xff0c mac平台通用配置 xf

随机推荐

  • Qt -线程基础(QThread、QtConcurrent、信号槽等)

    昨晚看Qt的Manual xff0c 突然发现下一个版本的Qt中 Qt4 7 4 Qt4 8等 增加了一个特赞的介绍多线程的文章 xff1a Thread Basics 注意 xff1a 该链接以后会失效 xff0c 但是 到时候你直接看Q
  • Qt中线程同步的几种方法详解

    1 QMutex类 QMutex类就像一把锁 xff0c 在互斥量以前上锁 xff08 QMutex lock xff09 xff0c 而后在使用完互斥量以后解锁 xff08 QMutex unlock xff09 好比下面的代码 xff1
  • Qt中多线程同步总结示例

    1 QMutex QMutex mutex void func mutex lock mutex unlock 2 QMutex联手QMutexLocker 在复杂函数或者异常处理中 xff0c 对mutex进行lock 和unlock 操
  • vnc远程连接ubuntu,如何实现vnc远程连接ubuntu教程图解

    Ubuntu Linux是由南非人马克 沙特尔沃思 Mark Shuttleworth 创办的基于Debian Linux的操作系统 Ubuntu几乎包含了所有常用的应用软件 xff1a 文字处理 电子邮件 软件开发工具和Web服务等 用户
  • 什么是元学习?

    作者 Aleksandr Movchan 编译 VK 来源 Medium 人工智能的一个基本问题是它无法像人类一样高效地学习 许多深度学习分类器显示了超人的表现 xff0c 但需要数百万个训练样本 知识不共享 xff0c 并且每个任务都独立
  • Qt-多线程-QThread分析总结

    QThread是Qt提供的线程类 xff0c 每一个QThread均可管理一个线程 其具有两种使用方式 xff1a 1 继承为QThread的子类 xff1b 2 继承为QObject的子类 xff0c 并使用QObject moveToT
  • C++中 二重指针详解

    前言 上篇博文讲到数组和指针的一些关系 xff0c 本篇博文会在这基础之上讲解一些更为复杂的指针 xff0c 这些指针在笔试题中经常出现 xff0c 对刚毕业找工作的大学生有很大的作用 指向指针的指针 在指针篇 xff08 1 xff09
  • C++中数组初始化方法

    定义 xff1a int pia 61 new int 10 array of 10 uninitialized ints 此 new 表达式分配了一个含有 10 个 int 型元素的数组 xff0c 并返回指向该数组第一个元素的指针 xf
  • 类对象的内存和堆空间的内存及初始化详解

    对象的内存 对象的内存可以存在于 3 种地方 xff1a 全局区 xff08 数据段 xff09 xff1a 全局变量栈空间 xff1a 函数里面的局部变量堆空间 xff1a 动态申请内存 xff08 malloc new等 xff09 文
  • C++内存空间管理

    1 C 43 43 内存机制 1 栈 Stack xff0c 函数中的局部变量 xff0c 由编译器负责分配释放 xff0c 函数结束 xff0c 变量释放 2 堆 Heap xff0c 通过new 申请的内存 xff0c 由delete或
  • Qt中跨线程下信号和槽的使用方法

    connect用于连接qt的信号和槽 xff0c 在qt编程过程中不可或缺 它其实有第五个参数 xff0c 只是一般使用默认值 xff0c 在满足某些特殊需求的时候可能需要手动设置 Qt AutoConnection xff1a 默认值 x
  • Qt 中开启线程的多种方式小结

    简介1 继承 QThread 重写 run 函数2 继承 QObject 调用 moveToThread3 继承 QRunnable 重新 run 函数 xff0c 结合 QThreadPool 实现线程池4 使用 C 43 43 11 中
  • Qt中exec函数的作用

    Qt中的exec 方法到处可见 xff0c 例如 xff1a QCoreApplicaton exec QApplication exec QDialog exec QThread exec QMenu exec 那么 xff0c 这些ex
  • 在Qt中使用全局变量的两种方式,及出现无法解析的命令的问题

    很多小伙伴在Qt编程时 xff0c 不会使用全局变量 xff0c 其实基本有两种方式来定义使用全局变量 xff0c 第一种是根据c c 43 43 的extern来进行多文件的使用 xff0c 第二种方法就是把全局变量放在类中 xff0c
  • Qt中信号槽形参值传递,引用传递,指针传递的实例及总结

    在同一个线程中 当信号和槽都在同一个线程中时 xff0c 值传递参数和引用传递参数有区别 xff1a 值传递会复制对象 xff1b xff08 测试时 xff0c 打印传递前后的地址不同 xff09 引用传递不会复制对象 xff1b xff
  • vnc远程连接,5个步骤教你如何轻松实现vnc远程连接

    当我们在进行vnc远程连接时 xff0c 往往找不到有用的工具来支撑我们的工作 那当我们在进行vnc远程连接时 xff0c 有什么工具能让我们快速实现vnc远程连接呢 xff1f iis7服务器管理工具 如图 xff1a IIS7服务器管理
  • 关于qt信号槽传指针与释放指针

    今天 xff0c 意外发现qt中信号槽传指针的方式进行时 xff0c 会发生释放不掉指针所分配的空间的情况 定义一个信号 void emit showTime Data 槽 void slot showTime Data 在发出信号之前动态
  • Qt-信号和槽函数传递自定义参数前需注册自定义数据类型

    Qt 信号和槽函数参数只能是基于 Qt 的基础类型的 xff0c 比如 QString int bool 等 xff0c 如果想传递自定义类型默认情况下是行不通的 要想在 Qt 的信号和槽函数之间传递自定义类型 xff0c 可以先将自己的自
  • Qt 设置窗口背景图片的几种方法实例

    1 在paintEvent事件中绘制图片 void Widget paintEvent QPaintEvent ev QPainter painter this painter drawPixmap rect QPixmap 34 bg j
  • Qt事件处理的几种方式

    Qt提供了5种事件处理和事件过滤的方法 xff1a 1 重写事件处理器函数 这是大部分情况最常用的一种 xff0c 如重写 paintEvent mousePressEvent keyPressEvent 等事件处理器虚函数 2 重写 QO