qt5入门-事件

参考:
Qt 事件(event)_w3cschool
https://www.w3cschool.cn/learnroadqt/xvme1j4c.html

本地环境:
win10专业版,64位


事件的概念

将事件抽象为一个对象,当用户发起一个行为,就把对应的事件加入事件队列,对于系统来说,每次只要处理事件队列里未处理的事件就可以了;如果没用事件,程序就阻塞,不执行任何代码。

必要时,Qt的事件也可以不进入事件队列,直接处理。

与信号的区别

  • 信号一旦发出,对应的槽函数一定会被执行,但是事件可以使用事件过滤器进行过滤。
  • 如果使用组件,那么要关心信号槽;如果自定义组件,那么要关心事件。(比如如果自定义一个QPushButton,那么需要重写它的鼠标点击事件和键盘处理时间,并在恰当的时候发出clicked()信号)

如果要使用事件,只要让类继承QWidget类及其子类(里面定义了很多protected virtual函数),然后再重写事件回调函数即可。

简单实例:自定义一个Label,重写鼠标移动、按压和释放事件

初始状态:
在这里插入图片描述
在label内移动:
在这里插入图片描述
鼠标按压:
在这里插入图片描述
鼠标释放:
在这里插入图片描述
代码:

// eventlabel.h
#ifndef EVENTLABEL_H
#define EVENTLABEL_H

#include <QLabel>

class QLabel;

class EventLabel : public QLabel {
protected:
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
};

#endif // EVENTLABEL_H

// eventlabel.cpp
#include <QWidget>
#include <QMouseEvent>

#include "eventlabel.h"

void EventLabel::mouseMoveEvent(QMouseEvent *event) {
    this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>").arg(
                      QString::number(event->x()),
                      QString::number(event->y())));
}

void EventLabel::mousePressEvent(QMouseEvent *event) {
    this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>").arg(
                      QString::number(event->x()),
                      QString::number(event->y())));
}

void EventLabel::mouseReleaseEvent(QMouseEvent *event) {
	// 支持用c格式化字符串的方式写QString
    QString msg;
    msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
                event->x(), event->y());
    this->setText(msg);
}

// main.cpp
#include <QApplication>
#include "eventlabel.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    EventLabel *label = new EventLabel;
    label->setWindowTitle("DIY Label");
    label->resize(400, 200);// width, height
    label->show();

    return a.exec();
}

事件的接收与忽略

Qt的事件对象都有一个accept()和一个ignore()。对于一个事件,如果子类没有处理这个事件的函数或者忽略了,那么会向上传递事件给父类;否则会接收事件,不再传递。在事件处理函数中可以用isAccepted()查询接收状态。

不过accept()和ignore()是很少用的,如果希望忽略一个事件,只要调用父类的响应函数即可。(如果不传给父类,直接忽略,可能应该执行的操作没有执行,会有危险)。用到它们的情况,例如在窗口关闭时需要询问是否要关闭:
在这里插入图片描述
点击Yes之后才能正常关闭。

// eventlabel.h
protected:
	void closeEvent(QCloseEvent *event);
private:
    bool continueToClose();

// eventlabel.cpp
#include <QMessageBox>
...
void EventLabel::closeEvent(QCloseEvent *event){
    if(continueToClose()) {
        event->accept();
    }else {
        event->ignore();
    }
}

bool EventLabel::continueToClose() {
    if(QMessageBox::question(this, "Quit", "Are you sure?",
                             QMessageBox::Yes | QMessageBox::No,
                             QMessageBox::No) == QMessageBox::Yes){
        return true;
    }
    else {
        return false;
    }
}

event()函数

event()是QObject的,它不是直接处理事件的,而是分发给不同的事件处理器(event handler)。如果希望在事件分发之前做一些操作,比如区分一些事件,某些事件在处理之前需要做一些处理。下面的例子是,当在窗口中,按下tab键时,做一些处理,那么应该继承QWidget,然后重写它的event()函数。同时,如果需要自定义事件,同样也要重写event()。

event()函数的返回值是bool型,返回true表示被传入的事件已经被识别并且得到了处理,此时QApplication会认为这个事件已经处理了,然后继续处理事件队列的下一个事件;返回false表示未被处理,QApplication会尝试寻找这个事件的下一个处理函数。

需要注意的是,前面提到的accept()和ignore()是不同事件处理器之间的沟通event()的返回值是通知QApplication的notify()函数是否处理下一个事件。

效果:当按下tab键后,会弹出about提示框。
在这里插入图片描述

// eventlabel.h
protected:
    bool event(QEvent *event);

// eventlabel.cpp
bool EventLabel::event(QEvent *event) {
    if(event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Tab) {
            // 处理
            QMessageBox::about(this, "", "press Tab");
            return true;
        }
    }
    return QWidget::event(event);
}

事件过滤器

事件过滤器的作用是筛选哪些事件需要被响应,就是判断哪些事件需要调用event(),进行识别和分发。此时需要重写eventFilter()函数,在有事件过滤器的情况下,这个函数会被优先调用,然后才处理事件。

这个函数的返回值也是bool,返回true表示停止响应。

比如为EventLabel安装一个eventFilter,希望它在面对键盘事件的时候,先发出一条提示信息。

在这里插入图片描述

// eventlabel.h
public:
    EventLabel();
protected:
	bool eventFilter(QObject *obj, QEvent *event);

// eventlabel.cpp
EventLabel::EventLabel() {
	// 安装事件过滤器
    this->installEventFilter(this);
}

bool EventLabel::eventFilter(QObject *obj, QEvent *event) {
    if(obj == this) {
        if(event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            //qDebug() << "you press: " << keyEvent->key();
            QMessageBox::about(this, "", QString("%1").arg(keyEvent->key()));
            return true;
        } else {
            return false;
        }
    } else {
    	// pass to parent
        return QLabel::eventFilter(obj, event);
    }
}

这是安装在Label上的。如果假设有个主窗口MainWindow,MainWindow有个组件textEdit(一个QObject对象),假设不希望让textEdit处理键盘事件,那么需要:

  • 在MainWindow上实现eventFilter(),需要判断obj == textEdit,如果是的话,再判断是不是KeyPress,如果是的话返回true,这样不会被父级的evenFilter处理
  • 执行textEdit.installEventFilter(MainWindow),表示如果有事件发送到textEdit,那么先调用MainWindow->eventFilter(),然后才调用textEdit.event()

注意:

  • 事件过滤器也可以安装在QApplication上,这样可以过滤所有事件,但是也会降低事件分发的效率。
  • 如果一个组件安装了多个过滤器,那么最后一个安装的会先调用,类似stack。
  • 如果在事件过滤器中delete了某个接收组件,一定要将返回值设为true,不然Qt仍然会分发给这个组件,但实际是找不到的,那么程序会崩溃
  • 事件过滤器和被安装的组件必须在同一线程,否则过滤器不起作用;如果安装之后,两个组件到了不同的线程,那么只有当二者重回到同一线程才气笑
  • 事件的调用最终都会调用QCoreApplication.notify()。因此Qt的事件处理,控制权由低到高依次是:重定义事件处理函数,重定义event()函数,为单个组件安装事件过滤器,为QApplication安装事件过滤器,重定义QCoreApplication.notify()

自定义事件

自定义事件需要继承QEvent,同时需要提供一个QEvent::TYPE类型(enum)的参数,作为自定义事件的类型值。Qt保留了0-999的值,所以TYPE要大于999,同时需要在QEvent::User和QEvent::MaxUser之间(1000-65535)。由于很难记住这个黍子,所以使用一个函数:

static int QEvent::registerEventType( int hint = -1 );

这个函数接受一个int,如果合法,直接返回,如果不合法,返回一个系统分配的合法值。这个函数是线程安全的,不必添加另外的同步操作。

发送事件有两种方式:(自定义的事件也可以)

  1. send
    事件被QCoreApplication的notify()函数直接发给receiver对象,返回值是事件处理函数的返回值,使用这个函数必须在栈上创建对象
    QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
    QApplication::sendEvent(mainWindow, &event);
    
  2. post
    事件被追加到事件列表的最后,等待处理。线程安全,要在堆上创建对象。
    QApplication::postEvent(object, new MyEvent(QEvent::registerEvenType(2048)));
    
    这个对象不用手动delete,是Qt自动的。postEvent()还有一个带优先级参数的版本。通过调用sendPostedEvent()可以让已提交的事件立即得到处理。

处理自定义事件也有两种方式:

  1. 重写void customEvent(QEvent *event),类似重写event()
  2. 直接重写event
    bool CustomWidget::event(QEvent *event) {
    	if (event->type() == CustomEventType) {
    		CustomeEvent *myEvent = static_cast<CustomeEvent *>(event);
    		// processing
    		return true;
    	}
    	return QWidget::event(event);
    }
    

这一块等用到了再补实例。暂时只作理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值