Qt学习(十一)—— 事件

什么是事件

事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

例如,在执行主窗口的exec()函数之后,程序将进入事件循环监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),相当于用一个switch判断事件类型,根据不同的事件类型给出不同的处理函数。

P.S:
可以把事件看作是一个个软件中断。
事件处理函数都是虚函数,需要我们自己去实现。
事件处理函数可以在帮助文档中QWidget-protected function中查找。

下面我们来用事件实现点击按钮弹出窗口的效果:

//MyButton.h
#ifndef MYBUTTON_H
#define MYBUTTON_H
#pragma execution_character_set("utf-8")
#include <QObject>
#include <QPushButton>
class MyButton : public QPushButton
{
    Q_OBJECT
public:
    explicit MyButton(QPushButton *parent = nullptr);

signals:

public slots:

protected:
        void MyButton::mousePressEvent(QMouseEvent *ev);
};
#endif // MYBUTTON_H

说明:先在项目中新建一个叫MyButton的C++类,继承自QPushButton类(在创建C++文件时如果没有这个选项,可以先随便选一个,然后在代码中修改成我们需要的基类)
在这里插入图片描述在这里插入图片描述

新建一个MyButton对象,作为主窗口的私有成员数据。

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include "MyButton.h"
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected:

private:
    MyButton m_button;
};
#endif // WIDGET_H

在MyButton.cpp文件中,实现在头文件中声明的事件虚函数。

#include "MyButton.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QMouseEvent>
MyButton::MyButton(QPushButton *parent) : QPushButton(parent)
{

}
void MyButton::mousePressEvent(QMouseEvent *ev){
    qDebug()<<ev->button();
    QDialog *p_dialog=new QDialog(this);
    p_dialog->setWindowTitle("我是生成的窗口");
    p_dialog->resize(350,350);
    p_dialog->exec();
}

P.S:函数中返回的参数ev可以获得当前事件对象的一些信息,比如可以获取当前鼠标事件点击的是左键还是右键,可以获取当前键盘事件按下的是哪个键等等。
在Widget.cpp文件中,将MyButton对象作为子部件添加进来。

#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
}
Widget::~Widget()
{

}

P.S:即使MyButton对象作为主窗口的私有成员数据,还是需要为其指定父元素,否则按钮不会在主窗口中显示出来。

实现效果:
在这里插入图片描述
同时,也打印出鼠标点击事件的信息:按下的是左键在这里插入图片描述
P.S:要先引入相应的事件头文件才能调用事件对象的方法。

计时器

计时器在软件开发中很常用,它会以一定的时间间隔自动触发。
下面我们在刚才的界面中实现一个计时器:

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include "MyButton.h"
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void Widget::timerEvent(QTimerEvent *event);	//声明要实现的计时器函数
private:
    MyButton m_button;
};
#endif // WIDGET_H
//Widget.cpp
#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QLabel>
#include <QString>
#include <QTimerEvent>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
    startTimer(Qt::1000);	//启动一个计时器,参数是每隔多少毫秒触发一次定时器
}
Widget::~Widget()
{

}
void Widget::timerEvent(QTimerEvent *event){
    static int count=0;
    count++;
	qDebug()<<QString("%1 秒钟过去了").arg(count);
}

P.S:
Qt中格式化字符串函数:QString("xxx %1 %2").arg(a).arg(b); 结果是:%1位置会被变量a的值替换,%2位置会被变量b的值替换
启动一个计时器需要调用方法startTimer()
实现效果:
在这里插入图片描述
P.S:startTimer()函数返回一个int型的值,这个值就是计时器的ID,当我们想要关闭某个计时器时,只需要获取它的ID,然后调用killTimer(),将计时器ID作为参数传入即可。

#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QLabel>
#include <QString>
#include <QTimerEvent>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
    timerId=startTimer(1000);	//用timerId接收计时器ID
    qDebug()<<this;
}
Widget::~Widget()
{

}
void Widget::timerEvent(QTimerEvent *event){
    static int total=0;
    total++;
    qDebug()<<QString("%1 秒钟过去了").arg(total);
    if(total==3) killTimer(timerId);	//关闭计时器
}

这样,经过3秒钟就将计时器关闭了。

注意:这里的timerId是类的私有成员数据,如果作为类的构造函数中的局部变量,在timerEvent()中是访问不到的。

事件的接收和忽略

事件的接收是有一定顺序的,如果在事件传播的过程中,事件被某个对象接收了,就不会继续往下传递了,只有当优先级高的对象忽略了这个事件,事件才会继续往后传递,优先级低的对象才有机会接收事件。
以下是若非特殊事件,则转发给基类去处理的情况:

void MyButton::event(QEvent *event)
{	//如果左键被按下,打印文字
    if (event->button() == Qt::LeftButton) {
    	qDebug()<<"左键被按下";
    }
    //否则,转发给基类处理
    else{
    	return QWidget::event(event);
    }
}

以下是既要处理特殊事件,同时也要转发给基类去处理的情况:

void MyButton::event(QEvent *event)
{	//如果左键被按下,打印文字
    if (event->button() == Qt::LeftButton) {
    	qDebug()<<"左键被按下";
    }
    //无论是否为特殊事件,都要转发给基类去处理
    return QWidget::event(event);
}

下面是一个实验:

//MyButton.cpp
#include "MyButton.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QMouseEvent>
MyButton::MyButton(QPushButton *parent) : QPushButton(parent)
{

}
void MyButton::mousePressEvent(QMouseEvent *ev){
    if(ev->button()==Qt::LeftButton){
        qDebug()<<"Mybutton鼠标左键被按下";
    }else{
        return QWidget::mousePressEvent(ev);
    }
}
#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QLabel>
#include <QString>
#include <QTimerEvent>
#include <QEvent>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
    timerId=startTimer(1000);
    qDebug()<<this;
}
Widget::~Widget()
{

}
void Widget::mousePressEvent(QMouseEvent *ev){
    qDebug()<<ev->button();
    qDebug()<<"Widget鼠标右键被按下";
}

P.S:注意到MyButton类和Widget类都实现了mousePressedEvent(),若点击鼠标左键,会打印“MyButton鼠标左键被按下”,若点击鼠标右键,则将事件分发给基类,此时基类捕获到这个事件,会调用Widget类中的mousePressEvent()函数,打印“Widget鼠标被按下”

实现效果:
在这里插入图片描述

event() 函数

event()函数主要用于根据事件对象类型进行消息分发。利用这个特性,我们可以拦截某些特定的事件。

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include "MyButton.h"
#include <QLabel>
#include <QEvent>
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void Widget::timerEvent(QTimerEvent *event);
    bool Widget::event(QEvent *event);	//声明重载函数
private:
    MyButton m_button;
    int timerId;
};
#endif // WIDGET_H

//Widget.cpp
#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QLabel>
#include <QString>
#include <QTimerEvent>
#include <QEvent>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
    timerId=startTimer(1000);
    qDebug()<<this;
}
Widget::~Widget()
{

}
void Widget::timerEvent(QTimerEvent *event){
    static int total=0;
    total++;
    qDebug()<<QString("%1 秒钟过去了").arg(total);
    if(total==3) killTimer(timerId);
}
bool Widget::event(QEvent *event){
    switch(event->type()){
    case QEvent::KeyPress:
        qDebug()<<"键盘被按下";
        break;
    case QEvent::MouseButtonPress:
        qDebug()<<"鼠标被按下";
        break;
    case QEvent::Resize:
        qDebug()<<"窗口大小被改变";
        break;
    default:
        break;
    }
    return true;
}

实现效果:
在这里插入图片描述
P.S:重写的event函数返回类型是bool型,当返回值为true,Qt会认为事件已经处理完毕,不再继续分发给其它对象,而是处理事件队列中的下一个事件。当返回值为false,事件将会由系统处理。
event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理委托给具体的事件处理器。而这些事件处理器是一些虚函数,需要我们自己去实现。

事件过滤器

有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理,这时候就需要使用到事件过滤器。

所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。
事件过滤器eventFilter()解决了当组件很多时需要重写很多个event()函数的问题。

eventFilter()的参数有两个,分别是watched和event,其中watched指的是触发事件的目标控件,event指的是触发的事件。

eventFilter()相当于创建过滤器,而installEventFilter()则是安装过滤器。对象只有调用installEventFilter()安装过滤器后,对应的eventFilter()才有效。

  • installEventFilter()函数的声明为:
    void QObject::installEventFilter(QObject *filterObj);
    其中,filterObj参数表示实现事件过滤器的部件。
  • 请注意,如果我们在一个部件安装了事件过滤器,一般在其父控件上实现事件过滤器函数。
  • 事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
  • 【转自:cicero-Qt evenFilter()与installEvenFilter()-博客园
#include "Widget.h"
#include <QPushButton>
#include <QDialog>
#include <QDebug>
#include <QLabel>
#include <QString>
#include <QTimerEvent>
#include <QEvent>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_button.move(100,100);
    m_button.setText("点击生成窗口");
    m_button.setParent(this);
    m_button.installEventFilter(this);
    this->installEventFilter(this);
}
Widget::~Widget()
{

}
bool Widget::eventFilter(QObject *obj,QEvent *ev){
   // MyButton *p_button=static_cast<MyButton*>(obj);
    if( obj == &m_button){
        if(ev->type()==QEvent::MouseButtonPress){
            qDebug()<<"m_button需要被过滤";
            return true;
        }
    }
    if(obj==this){
        if(ev->type()==QEvent::MouseButtonPress){
            qDebug()<<"Widget需要被过滤";
            return true;
        }
    }
    return QWidget::eventFilter(obj,ev);
}

在这里插入图片描述
可以看到,点击m_button后,m_button的鼠标点击事件被它的事件过滤器捕获了,点击旁边的空白区域,widget的鼠标点击事件被它的事件过滤器捕获了。
P.S:如有错误,欢迎指正~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值