一、简介

在Qt中,事件作为一个对象,继承自 QEvent 类,常见的有键盘事件 QKeyEvent、鼠标事件 QMouseEvent 和定时器事件 QTimerEvent 等,与 QEvent 类的继承关系图如下所示。本章会详细讲解这3个常见的事件,还会涉及事件过滤器、自定义事件和随机数的知识。关于本章的相关内容,可以在Qt帮助中通过The Event System 关键字查看。

二、Qt中的事件

事件是对各种应用程序需要知道的由应用程序内部或者外部产生的事情或者动作的通称。Qt中使用一个对象来表示一个事件,继承自QEvent类。需要说明的是,事件与信号并不相同,比如单击一下界面上的按钮,那么就会产生鼠标事件 QMou­seEvent (不是按钮产生的 ),而因为按钮被按下了 ,所以它会发出 clicked() 单击信号(是按钮产生的)。这里一般只关心按钮的单击信号,而不用考虑鼠标事件,但是如果要设计一个按钮,或者当单击按钮时让它产生别的效果,那么就要关心鼠标事件了。可以看到,事件与信号是两个不同层面的东西,发出者不同,作用也不同。在Qt中,任何 QObject子类实例都可以接收和处理事件。

2.1 事件的处理

一个事件由一个特定的 QEvent 子类来表示,但是有时一个事件又包含多个事件类型,比如鼠标事件又可以分为鼠标按下、双击和移动等多种操作。这些事件类型都由 QEvent 类的枚举型 QEvent::Type 来表示,其中包含了 一百多种事件类型,可以在 QEvent 类的帮助文档中查看。虽然 QEvent 的子类可以表示一个事件,但是却不能用来处理事件,那么应该怎样来处理一个事件呢?在 QCoreApplication 类的notify()函数的帮助文档处给出了5种处理事件的方法:

方法一:重新实现部件的 paintEvent()、mousePressEvent() 等事件处理函数。这是最常用的一种方法,不过它只能用来处理特定部件的特定事件。

方法二:重新实现 notify() 函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。

方法三:向 QApplication 对象上安装事件过滤器。因为一个程序只有一个 QApplication 对象,所以这样实现的功能与使用 notify() 函数是相同的,优点是可以同时处理多个事件。

方法四:重新实现 event() 函数。QObject 类的 event() 函数可以在事件到达默认的事件处理函数之前获得该事件。

方法五:在对象上安装事件过滤器。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。

在实际编程中,最常用的是方法一,其次是方法五。因为方法二需要继承自 QApplication 类;而方法三要使用一个全局的事件过滤器,这将减缓事件的传递,所以,虽然这两种方法功能很强大,但是却很少被用到。

2.2 事件的传递

在每个程序的 main() 函数的最后都会调用 QApplication 类的 exec() 函数,它会使Qt应用程序进人事件循环,这样就可以使应用程序在运行时接收发生的各种事件。一旦有事件发生,Qt 便会构建一个相应的 QEvent 子类的对象来表示,然后将它传递给相应的 QObject 对象或其子对象。下面通过例子来看一下Qt中的事件传递过程

新建Qt Gui应用,项目名称为myEvent,基类选择QWidget,然后类名保持 Widget 不变。建立完成后向项目中添加新文件,模板选择C+ +类,类名为MyLineEdit,基类手动填写为QLineEdit,自定义了一个MyLineEdit 类。

mylineEdit. h 文件:

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H #include <QLineEdit> class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit MyLineEdit(QWidget *parent = nullptr); //event()函数获取事件的类型
bool event(QEvent *event); protected:
//MyLineEdit类的键盘按下事件
void keyPressEvent(QKeyEvent *event);
}; #endif // MYLINEEDIT_H

这里添加了keyPressEvent()函数和event()函数的声明。

mylineEdit. cpp 文件:

#include "mylineedit.h"
#include <QKeyEvent>
#include <QDebug> MyLineEdit::MyLineEdit(QWidget *parent) :
QLineEdit(parent)
{ } // MyLineEdit类的键盘按下事件
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
qDebug() << tr("MyLineEdit键盘按下事件");
//让MyLineEdit输入栏能输入字符
QLineEdit::keyPressEvent(event); // 执行QLineEdit类的默认事件处理
event->ignore(); // 忽略该事件
} //event()函数获取事件的类型
bool MyLineEdit::event(QEvent *event)
{
//判断触发事件类型是否为键盘按下事件
if(event->type() == QEvent::KeyPress)
qDebug() << tr("MyLineEdit的event()函数");
return QLineEdit::event(event); // 执行QLineEdit类event()函数的默认操作
}

这里自定义了一个MyLineEdit类,它继承自QWidget,并且实现了MyLineEdit类的keyPressEvent()函数和event()函数。event()函数中使用了 event->type() 来获取事件的类型。如果是键盘按下事件 QEvent::KeyPress,则输出信息,另外返回父类的event()函数的操作结果。

widget.h 文件:

#ifndef WIDGET_H
#define WIDGET_H #include <QWidget>
class MyLineEdit;
namespace Ui {
class Widget;
} class Widget : public QWidget
{
Q_OBJECT public:
explicit Widget(QWidget *parent = 0);
~Widget(); // Widget类的事件过滤器
bool eventFilter(QObject *obj, QEvent *event); private:
Ui::Widget *ui;
MyLineEdit *lineEdit; protected:
// Widget类的键盘按下事件
void keyPressEvent(QKeyEvent *event);
}; #endif // WIDGET_H

这里也添加了keyPressEvent()函数的声明。

widget.cpp 文件:

#include "widget.h"
#include "ui_widget.h"
#include "mylineedit.h"
#include <QKeyEvent>
#include <QDebug> Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this); lineEdit = new MyLineEdit(this);
lineEdit->move(100, 100);
} Widget::~Widget()
{
delete ui;
} // Widget类的键盘按下事件
void Widget::keyPressEvent(QKeyEvent *event)
{
qDebug() << tr("Widget键盘按下事件");
} // Widget类的事件过滤器
bool Widget::eventFilter(QObject *obj, QEvent *event) // 事件过滤器
{
// 如果是lineEdit部件上的事件
if(obj == lineEdit)
{
if(event->type() == QEvent::KeyPress)
qDebug() << tr("Widget的事件过滤器");
}
return QWidget::eventFilter(obj, event);
}

这里也实现了Widget类的keyPressEvent()函数,并且会调用MyLineEdit类的keyPressEvent()函数。在事件过滤器中,先判断该事件的对象是不是lineEdit,如果是,再判断事件类型,最后返回QWidget类默认的事件过滤器的执行结果。

运行程序,然后按下键盘的任意键,比如这里按下a键,执行结果如下图所示。

可以看到,事件的传递顺序是这样的:先是事件过滤器,然后是焦点部件的event()函数,最后是焦点部件的事件处理函数,例如这里的键盘按下事件函数;如果焦点部件忽略了该事件,那么会执行父部件的事件处理函数,该部件的函数,最后是该部件的事件处理函数,如上图所示。注意,event()函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的。

Qt事件系统之一:Qt中的事件处理与传递的更多相关文章

  1. 第39课 Qt中的事件处理(下)

    1. 事件的传递过程 (1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序 (2)Qt应用程序收到系统消息后,将其转化为一个对应的QEvent事件对象,并调用QObject: ...

  2. 第38课 Qt中的事件处理(上)

    1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个 ...

  3. Qt 中的事件处理(二)

    1. 回顾事件传递的过程 ①源头:操作系统   操作系统检测到用户的动作时,就会产生一个系统消息,系统消息就会被发送到正在运行的Qt应用程序中, ②应用程序收到系统消息后, 他会将系统消息翻译成对应的 ...

  4. Qt 中的事件处理(一)

    1.图形界面应用程序的消息处理模型 特点: 基于操作系统才能运行 GUI应用程序提供的功能必须由用户触发 用户操作界面时操作系统是第一个感知的 系统内核的消息通过事件处理转变成QT的信号 2. Qt中 ...

  5. Qt事件系统基本概念

    (转自:http://www.cnblogs.com/andy1987/p/3322059.html) 1. QT事件系统 Qt应用程序的消息处理是基于事件驱动的,程序的每个动作都是由某个事件所触发的 ...

  6. QT事件过滤器(QT事件处理的5个层次:自己覆盖或过滤,父窗口过滤,Application过滤与通知)

    Qt事件模型一个真正强大的特色是一个QObject 的实例能够管理另一个QObject 实例的事件. 让我们试着设想已经有了一个CustomerInfoDialog的小部件.CustomerInfoD ...

  7. Qt 事件系统浅析 (用 Windows API 描述,分析了QCoreApplication::exec()和QEventLoop::exec的源码)(比起新号槽,事件机制是更高级的抽象,拥有更多特性,比如 accept/ignore,filter,还是实现状态机等高级 API 的基础)

    事件系统在 Qt 中扮演了十分重要的角色,不仅 GUI 的方方面面需要使用到事件系统,Signals/Slots 技术也离不开事件系统(多线程间).我们本文中暂且不描述 GUI 中的一些特殊情况,来说 ...

  8. Qt事件系统之二:鼠标事件和滚轮事件

    在Qt中,事件作为一个对象,继承自 QEvent 类,常见的有键盘事件 QKeyEvent.鼠标事件 QMouseEvent 和定时器事件 QTimerEvent 等,与 QEvent 类的继承关系图 ...

  9. Qt线程(2) QThread中使用WorkObject

    一般继承QThread的WorkThread都会在重载的run()中创建临时的WorkObject,这样能确定这个WorkObject在该thread中使用 那如果这个WorkObject是个Sing ...

随机推荐

  1. Java的发送邮件

    以下内容引用自http://wiki.jikexueyuan.com/project/java/sending-email.html: 用Java应用程序来发送一封电子邮件是足够简单的,但是开始时应该 ...

  2. CPU 天梯图

  3. C# 读自己的资源文件

    Assembly assm = this.GetType().Assembly;//Assembly.LoadFrom(程序集路径); foreach (string resName in assm. ...

  4. Objective-c写冒泡排序

    做面试题遇到用obj-c写冒泡排序,随便写了个 - (NSMutableArray *)sorted:(NSMutableArray *)array { int len = [array count] ...

  5. [转]JAVA异常

    异常 异常就是导致程序中断执行的一段指令流. 在java中, 对于异常在API中也有明确的定义,叫做异常类. Error : JVM的错误, 程序中不进行处理, 交给虚拟机. Exception : ...

  6. 通过grub硬盘安装centos7

    centos7与centos6.x有了很大的不同,从硬盘安装的方法也有了很大的不同,故出此文章我机器环境如下:    有俩系统 Win7 和 RHEL6.4 ,是通过grub(非grub2)引导的,g ...

  7. C#中泛型方法与泛型接口 C#泛型接口 List<IAll> arssr = new List<IAll>(); interface IPerson<T> c# List<接口>小技巧 泛型接口协变逆变的几个问题

    http://blog.csdn.net/aladdinty/article/details/3486532 using System; using System.Collections.Generi ...

  8. eclipse下对中文乱码问题的一些思考

    一.浏览器问题 当你的html页面或jsp页面没有显式声明页面编码的时候,也就是没有下面其中之一的代码 <meta http-equiv="content-type" con ...

  9. c语言学习-指针探究

    1:指针定义格式:格式:变量类型 *变量名用途:指针变量用于储存地址(only),也就是根据地址值,访问对应的存储空间. 注意.int *p 只能指向int类型的数据: 例: int a = 20; ...

  10. IDEA debug调式快捷键

    F9 resume programe 恢复程序 Alt+F10 show execution point 显示执行断点 F8 Step Over 相当于eclipse的f6 跳到下一步 F7 Step ...