参考:
Qt 事件(event)_w3cschool
https://www.w3cschool.cn/learnroadqt/xvme1j4c.html
本地环境:
win10专业版,64位
将事件抽象为一个对象,当用户发起一个行为,就把对应的事件加入事件队列,对于系统来说,每次只要处理事件队列里未处理的事件就可以了;如果没用事件,程序就阻塞,不执行任何代码。
必要时,Qt的事件也可以不进入事件队列,直接处理。
与信号的区别:
如果要使用事件,只要让类继承QWidget类及其子类(里面定义了很多protected virtual函数),然后再重写事件回调函数即可。
初始状态:
在label内移动:
鼠标按压:
鼠标释放:
代码:
// eventlabel.h
#ifndef EVENTLABEL_H
#define EVENTLABEL_H
#include
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
#include
#include "eventlabel.h"
void EventLabel::mouseMoveEvent(QMouseEvent *event) {
this->setText(QString("Move: (%1, %2)
").arg(
QString::number(event->x()),
QString::number(event->y())));
}
void EventLabel::mousePressEvent(QMouseEvent *event) {
this->setText(QString("Press: (%1, %2)
").arg(
QString::number(event->x()),
QString::number(event->y())));
}
void EventLabel::mouseReleaseEvent(QMouseEvent *event) {
// 支持用c格式化字符串的方式写QString
QString msg;
msg.sprintf("Release: (%d, %d)
",
event->x(), event->y());
this->setText(msg);
}
// main.cpp
#include
#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
...
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()是QObject的,它不是直接处理事件的,而是分发给不同的事件处理器(event handler)。如果希望在事件分发之前做一些操作,比如区分一些事件,某些事件在处理之前需要做一些处理。下面的例子是,当在窗口中,按下tab键时,做一些处理,那么应该继承QWidget,然后重写它的event()函数。同时,如果需要自定义事件,同样也要重写event()。
event()函数的返回值是bool型,返回true表示被传入的事件已经被识别并且得到了处理,此时QApplication会认为这个事件已经处理了,然后继续处理事件队列的下一个事件;返回false表示未被处理,QApplication会尝试寻找这个事件的下一个处理函数。
需要注意的是,前面提到的accept()和ignore()是不同事件处理器之间的沟通,event()的返回值是通知QApplication的notify()函数是否处理下一个事件。
// 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处理键盘事件,那么需要:
注意:
自定义事件需要继承QEvent,同时需要提供一个QEvent::TYPE类型(enum)的参数,作为自定义事件的类型值。Qt保留了0-999的值,所以TYPE要大于999,同时需要在QEvent::User和QEvent::MaxUser之间(1000-65535)。由于很难记住这个黍子,所以使用一个函数:
static int QEvent::registerEventType( int hint = -1 );
这个函数接受一个int,如果合法,直接返回,如果不合法,返回一个系统分配的合法值。这个函数是线程安全的,不必添加另外的同步操作。
发送事件有两种方式:(自定义的事件也可以)
QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
QApplication::sendEvent(mainWindow, &event);
QApplication::postEvent(object, new MyEvent(QEvent::registerEvenType(2048)));
这个对象不用手动delete,是Qt自动的。postEvent()还有一个带优先级参数的版本。通过调用sendPostedEvent()可以让已提交的事件立即得到处理。处理自定义事件也有两种方式:
bool CustomWidget::event(QEvent *event) {
if (event->type() == CustomEventType) {
CustomeEvent *myEvent = static_cast<CustomeEvent *>(event);
// processing
return true;
}
return QWidget::event(event);
}
这一块等用到了再补实例。暂时只作理解。