问题1:Qt中常见的事件有哪些?

答:鼠标事件(QMouseEvent)、键盘事件(QKeyEvent)、绘制事件(QPaintEvent)、窗口尺寸改变(QResizeEvent)、滚动事件(QScrollEvent)、控件显示(QShowEvent)、控件隐藏(QHideEvent)、定时器事件(QTimerEvent)等等。。

问题2:Qt是事件驱动的,这句话该怎么理解呢?

Qt将系统产生的信号(软件中断)转换成Qt事件,并且将事件封装成类,所有的事件类都是由QEvent派生的,事件的产生和处理就是Qt程序的主轴,且伴随着整个程序的运行周期。因此我们说,Qt是事件驱动的。

问题3:Qt事件是由谁产生的?Qt是如何将信号转换成事件的?

答:Qt的官方手册说,事件有两个来源:程序外部和程序内部,多数情况下来自操作系统并且通过spontaneous()函数返回true来获知事件来自于程序外部,当spontaneous()返回false时说明事件来自于程序内部,就像例程1创建一个事件并把它分发出去。

问题4:Qt事件是由谁接收的?

答:QObject!它是所有Qt类的基类!是Qt对象模型的核心!QObject类的三大核心功能其中之一就是:事件处理。QObject通过event()函数调用获取事件。所有的需要处理事件的类都必须继承自Qobject,可以通过重定义event()函数实现自定义事件处理或者将事件交给父类。

问题5:事件处理的流程是什么样的?

答:事件有别于信号的重要一点:事件是一个类对象具有特定的类型,事件多数情况下是被分发到一个队列中(事件队列),当队列中有事件时就不停的将队列中的事件发送给QObject对象,当队列为空时就阻塞地等待事件,这个过程就是事件循环!

QCoreApplication::exec()开启了这种循环,一直到QCoreApplication::exit()被调用才终止,所以说事件循环是伴随着Qt程序的整个运行周期!

另外一种同步处理情形是通过sendEvent()将事件发送出去,直接进入事件的传送和处理流程。

事件处理流程如图所示:

例程1:sendEvent同步事件分发

/*!
 * \brief Widget::Widget 使用sendEvent同步分发事件
 * 使用QPushButton模拟键盘的回删和向前删除按键
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::on_button_back_clicked()
{
    int key = Qt::Key_Backspace; //
 
    QKeyEvent EventPress(QEvent::KeyPress, key, Qt::NoModifier);
    QApplication::sendEvent(ui->textEdit, &EventPress);
 
    QKeyEvent EventRelease(QEvent::KeyRelease, key, Qt::NoModifier);
    QApplication::sendEvent(ui->textEdit, &EventRelease);
}
 
void Widget::on_button_delete_clicked()
{
    int  key = Qt::Key_Delete; //
 
    QKeyEvent EventPress(QEvent::KeyPress, key, Qt::NoModifier);
    QApplication::sendEvent(ui->textEdit, &EventPress);
 
    QKeyEvent EventRelease(QEvent::KeyRelease, key, Qt::NoModifier);
    QApplication::sendEvent(ui->textEdit, &EventRelease);
}
postEvent和sendEvent的关系就像Qt::QueuedConnection和Qt::DirectConnection的关系,只不过前两者是分发事件后两者是发送消息罢了,机制上postEvent和QueuedConnected是异步通信,而另外两种是同步通信。

例程2:postEvent异步事件分发

int count = 0;
 
/*!
 * \brief Widget::Widget 使用postEvent异步分发事件
 * 连续分发10个事件,在事件处理函数中逐个处理
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    int i = 1;
    while(i <= 10)
    {
        //postEvent传递的事件必须是通过new创建的
        qDebug() << "分发第" << i << "个事件";
        QApplication::postEvent(this, new QEvent(NewType));
        i++;
    }
}
 
void Widget::customEvent(QEvent *event)
{
    //使用延时模拟处理过程
    if(event->type() == NewType)
    {
        qDebug() << "现在时间:" <<
                    QTime::currentTime().toString("hh::mm:ss.zzz");
        qDebug() << "第" << ++count << "次收到了事件!处理事件需要一点时间!";
        Delay(1000*2);
    }
 
    QWidget::customEvent(event);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::Delay(unsigned int msec)
{
    QTime start = QTime::currentTime();
 
    QTime end;
    do{
        end = QTime::currentTime();
    } while(start.msecsTo(end) <= msec);
}
问题6:事件过滤器机制?

事件的传送和处理流程的第一站是事件过滤器eventFilter(),某个对象A可以通过给另一个对象B安装事件处理器,实现对对象B事件的监听或者拦截功能。我们可以给A取名监听器,B取名接收器。一个对象可以监听多个对象,一个对象也可以被多个事件监听。事件过滤器返回true则表示事件已经处理完毕,否则传递给下一个监听器或者接收器本身。

例程3:事件过滤器

/*!
 * \brief Widget::Widget 事件过滤器
 * 不借助Tab键的情况下使用Space键实现控件跳转
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    ui->lineEdit_user->setText(QString("lee"));
    focusNextChild();
    ui->lineEdit_password->setText(QString("*******"));
 
    //监听控件
    ui->lineEdit_user->installEventFilter(this);
    ui->lineEdit_password->installEventFilter(this);
    ui->button_accept->installEventFilter(this);
    ui->button_cancel->installEventFilter(this);
}
 
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    //定义事件处理动作
    if (watched == ui->lineEdit_user || watched == ui->lineEdit_password
        || watched == ui->button_accept || watched == ui->button_cancel)
    {
        if (event->type() == QEvent::KeyPress)
        {
            QKeyEvent *e = static_cast<QKeyEvent *>(event);
            if(e->key() == Qt::Key_Space)
            {
                focusNextChild();
                return true;
            }
        }
    }
    return QWidget::eventFilter(watched, event);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::on_button_cancel_clicked()
{
    qApp->quit();
}
值得注意的一点是QCoreApplication虽然负责事件分发,但本身也是继承自QObject的,所以在分发事件之前,也要检查自身是否被别的对象安装了事件过滤器,事件过滤器可能会过滤掉一些事件不发布。

例程4:QCoreApplication安装事件过滤器

widget.cpp

/*!
 * \brief Filter::eventFilter 用于监听qApp的监听器
 * \return
 */
bool Filter::eventFilter(QObject *obj, QEvent *event)
{
    //阻止所有的鼠标点击事件
    if(event->type() == QEvent::MouseButtonPress)
    {
        qDebug() << "sorry everybody, I gonna filter all the mouse event!";
        return true;
    }
    return QObject::eventFilter(obj,event);
}
 
/*!
 * \brief Widget::Widget
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "mouse press!";
 
    QWidget::mousePressEvent(event);
}
main.c

#include "widget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    Filter filter;
    a.installEventFilter(&filter);
 
    Widget w;
    w.show();
 
    return a.exec();
}
也可以通过重新实现QCoreApplication的notify(),自定义对事件的处理动作。

例程5:QCoreApplication子类化并重写notify

newapplication.h

#ifndef NEWAPPLICATION_H
#define NEWAPPLICATION_H
 
#include <QApplication>
 
class NewApplication : public QApplication
{
public:
    NewApplication(int argc, char **argv) : QApplication(argc,argv) {}
 
    virtual bool notify(QObject *, QEvent *);
 
};
 
#endif // NEWAPPLICATION_H
newapplication.cpp

#include "newapplication.h"
#include <QMouseEvent>
#include <QDebug>
 
bool NewApplication::notify(QObject *receiver, QEvent *event)
{
    if(event->type() == QMouseEvent::MouseButtonPress)
    {
        qDebug() << "sorry everybody I gonna filter all the mouse press event";
        return true;
    }
 
    return QApplication::notify(receiver,event);
}
widget.h

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
#include <QMouseEvent>
 
namespace Ui {
class Widget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
 
protected:
    void mousePressEvent(QMouseEvent *event);
 
private slots:
    void on_pushButton_clicked();
 
private:
    Ui::Widget *ui;
};
 
#endif // WIDGET_H
widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "I am mainwindow Widget, I got a mouse event!";
 
    QWidget::mousePressEvent(event);
}
 
void Widget::on_pushButton_clicked()
{
    qDebug() << "I am push button , I got a mouse event!";
}
 main.cpp

#include "widget.h"
#include <QApplication>
#include "newapplication.h"
 
int main(int argc, char *argv[])
{
//    QApplication a(argc, argv);
    NewApplication a(argc, argv);
 
    Widget w;
    w.show();
 
    return a.exec();
}
运行结果:点击界面的任意位置,事件都被qApp过滤。

小结:事件处理的方式

1.重新实现对象的特定事件处理函数,例如mousePressEvent、keyPressEvent 、showEvent等,处理完毕后将事件交给父类;

2.重新实现event函数,处理完毕后将事件交给父类;

3.在对象上安装事件过滤器,让其他对象控制此对象的事件行为;

4.给主程序QCoreApplication安装事件过滤器,在调用notify进行事件分发之前,会根据过滤器判断对事件的处理(例如:丢弃);

5.子类化QCoreApplication,重新实现notify事件分发函数;

问题7.怎么使用自定义事件?
情景:自定义事件对于特定的操作是很有用的,定义一种连续点击10次鼠标的事件NewMouseEvent,连续点击10次屏幕唤醒屏幕校准程序。

自定义事件

newmouseevent.h

#ifndef MYEVENT_H
#define MYEVENT_H
 
#include <QEvent>
#include <QString>
 
class NewMouseEvent : public QEvent
{
public:
    explicit NewMouseEvent() :  QEvent(MouseTenClick) {}
    const static Type MouseTenClick = static_cast<Type>(QEvent::User+0x10);
};
 
#endif // MYEVENT_H
widget.h

#ifndef MYEVENT_H
#define MYEVENT_H
 
#include <QEvent>
#include <QString>
 
class NewMouseEvent : public QEvent
{
public:
    explicit NewMouseEvent() :  QEvent(MouseTenClick) {}
    const static Type MouseTenClick = static_cast<Type>(QEvent::User+0x10);
};
 
#endif // MYEVENT_H
widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "newmouseevent.h"
#include <QMouseEvent>
#include <QTimer>
 
/*!
 * \brief Widget::Widget
 * 创建并分发一种新的事件:鼠标连续点击10次
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    ui->label->installEventFilter(this);
 
    ui->label->setText(tr("请连续点击屏幕以唤醒屏幕校准功能!"));
    ui->label->adjustSize();
 
    m_timer = new QTimer;
    m_timer->setInterval(1000);
    m_timer->start();
    connect(m_timer, SIGNAL(timeout()), SLOT(clearCount()));
 
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::mousePressEvent(QMouseEvent *event)
{
    QWidget::mousePressEvent(event);
}
 
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() != Qt::LeftButton)
        return;
 
    if(m_timer->isActive())
        m_timer->stop(); //如果计时器在运行,则停止然后重新开始
    m_timer->start();
 
    count++;
 
    if(10 == count)
    {
        count = 0;
 
        NewMouseEvent event;
        qApp->sendEvent(ui->label, &event);
    }
 
    QWidget::mouseReleaseEvent(event);
}
 
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->label && event->type()== NewMouseEvent::MouseTenClick)
    {
        ui->label->setText(tr("你连续点击了10次屏幕,校准程序正在启动!"));
        ui->label->adjustSize();
        return true;
    }
 
    return QWidget::eventFilter(obj,event);
}
 
void Widget::clearCount()
{
    count = 0;
}
运行结果

连续点击10次鼠标算一次自定义事件

问题8:接受者对象中途被删除会发生什么?被监听者被删除会怎么样?
发送失败?程序崩溃?

widget.h

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
#include <QLabel>
 
namespace Ui {
class Widget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
 
protected:
 
private slots:
    void slotSendEvent();
    void deleteLabel();
 
private:
    Ui::Widget *ui;
 
    QLabel *m_label;
 
};
 
#endif // WIDGET_H
widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QResizeEvent>
#include <QDebug>
 
/*!
 * \brief Widget::Widget
 * 在事件循环分发事件给接收者之前,接收者被删除
 * \param parent
 */
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    //创建小窗口
    m_label = new QLabel(this);
    m_label->setStyleSheet(QString("border:1px solid red;"));
    m_label->setGeometry(QRect(0,0,200,100));
 
    //在qApp发送事件之前销毁小窗口
    QTimer::singleShot(1000, this, SLOT(deleteLabel()));
    //qApp发送事件给小窗口
    QTimer::singleShot(2000, this, SLOT(slotSendEvent()));
 
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::slotSendEvent()
{
    QResizeEvent re(QSize(300,200), QSize(200,100));
 
    qDebug() << "qApp发送了一个事件给小窗口!";
    qApp->sendEvent(m_label, &re);
}
 
void Widget::deleteLabel()
{
    qDebug() << "小窗口被销毁了!";
    delete m_label;
    m_label = NULL;
}
 运行结果

Qt中事件循环机制详解相关推荐

  1. node mysql 事件循环_NodeJs 的 Event loop 事件循环机制详解

    什么是事件轮询 事件循环是 Node.js 处理非阻塞 I/O 操作的机制--尽管 JavaScript 是单线程处理的--当有可能的时候,它们会把操作转移到系统内核中去. 下面的图表显示了事件循环的 ...

  2. 学习QT之信号槽机制详解

    学习QT之信号槽机制详解 一.Qt信号槽机制 概念:信号槽是Qt框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如:按钮检测到自己被点击了一下,它就会发出一个信号(sig ...

  3. Android 系统(199)---Android事件分发机制详解

    Android事件分发机制详解 前言 Android事件分发机制是Android开发者必须了解的基础 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全.思路不清晰.无源码分析. ...

  4. QT消息/事件循环机制与多线程的关系

    关于Qt子线程和消息循环 一.QT消息/事件循环机制 Qt作为一个可视化GUI界面操作系统,是基于事件驱动的,我们程序执行的顺序不再是线性,而是由一个个应用程序内部或外部的事件进行驱动,无事件时便阻塞 ...

  5. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

  6. 安卓自定义View进阶-事件分发机制详解

    原文地址:http://www.gcssloop.com/customview/dispatch-touchevent-source Android 事件分发机制详解,在上一篇文章 事件分发机制原理  ...

  7. OSPF中DR选举机制详解

    OSPF中DR选举机制详解--对于这方面不太清楚的可以看看附件 转载于:https://blog.51cto.com/gauyanm/226882

  8. smarty手册-smarty中foreach循环语句详解

    原文地址:smarty手册-smarty中foreach循环语句详解作者:谭博 {foreach}循环也有自身属性的变量,可以通过{$smarty.foreach.name.property}访问,其 ...

  9. epoll边缘触发_epoll事件通知机制详解,水平触发和边沿触发的区别

    看到网上有不少讨论epoll,但大多不够详细准确,以前面试有被问到这个问题.不去更深入的了解,只能停留在知其然而不知其所以然.于是,把epoll手册翻译一遍,更深入理解和掌握epoll事件处理相关知识 ...

最新文章

  1. PHP 显示文章发布日期 一小时前 一天前 一月前 一年前
  2. 全球及中国真空管太阳能集热器行业竞争状况及投资策略研究报告2021年版
  3. python字符串去头尾_悉尼大学某蒟蒻的Python学习笔记
  4. ubuntu20.04安装edge浏览器
  5. 用async 解放你的大脑
  6. AI 人工智能与半导体论坛:
  7. 密码学原理与实践_到底什么是防火墙入侵检测密码学身份认证?如何高效建立网络安全知识体系?...
  8. python语句可以采用交互式执行方式_怎么在Python交互式命令行中运行脚本?
  9. java8以及常见编程技术Api中文版手册
  10. envi自定义坐标系
  11. DM6437 720P调试问题记录
  12. 计算机时区找不到北京,emwin7时区/em 怎么没有北京时间了-win7时区,win7系统怎么添加北京时区...
  13. 构建具有丰富功能的交互式 Kiosk
  14. 【转】如何订机票最划算?
  15. 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、偏向所、轻量级锁、重量级锁、锁膨胀、对象锁和类锁
  16. Linux Python 导航目录
  17. 【JVM · GC】垃圾回收器
  18. JavaWeb技术之JSP
  19. 多线程+socket 实现群聊服务器
  20. 免费的网络学习工具【eNSP】

热门文章

  1. mysql数据库 on 命令_MySql 数据库基础命令
  2. nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器
  3. Linux软件基础实验,linux基本操作的实验
  4. java如何记录查看记录_Java如何检查消息是否可记录?
  5. html语言右对齐,在HTML中右对齐块元素
  6. spring boot 503_Spring实战读书笔记第4章 面向切面的Spring
  7. mpi tcp连接报错_关于WinCC与真实PLC之间的TCP/IP连接问题-工业支持中心-西门子中国...
  8. html for循环正方形,JavaScript for 循环
  9. n个点组成多少个三角形Java,农田开发 NOJ (已知N个点选取3个求最大三角形面积问题)...
  10. oracle 转换成csv文件,如何将csv转换为oracle中的表