QT中事件及事件处理
什么是事件,事件与Qt中信号的区别?
个人所见:
事件是应用程序对内部或者外部的动作的统称。
信号是事件的后续响应通知
例如你点击了一个按钮,物理上的鼠标点击动作就是事件,而程序收到事件时,就会发出按钮被按下的信号,通知按钮被按下了。
常见的事件有:鼠标事件,定时事件,键盘事件,关闭事件,上下文菜单事件,拖放事件。
QT事件封装
在Qt中,事件作为一个对象,继承自QEvent类,常见的有键盘事件QKeyEvent、鼠标事件QMouseEvent和定时器事件QTimerEvent等,它们与QEvent类的继承关系如图所示:
事件的处理方式
方法一:重新实现部件的paintEvent(),mousePressEvent()等事件处理函数。这是最常用也的一种方法,不过它只能用来处理特定部件的特定事件。例如前一章实现拖放操作,就是用的这种方法。
方法二:重新实现notify()函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。
方法三:向QApplication对象上安装事件过滤器。因为一个程序只有一个QApplication对象,所以这样实现的功能与使用notify()函数是相同的,优点是可以同时处理多个事件。
方法四:重新实现event()函数。QObject类的event()函数可以在事件到达默认的事件处理函数之前获得该事件。
方法五:在对象上安装事件过滤器。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。
实际编程中,最常用的是方重新实现event函数,其次是事件过滤器。
具体事件处理方式
重新实现事件处理函数
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLineEdit>
#include <QDebug>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void keyPressEvent(QKeyEvent *event) override;
};
class MyLineEdit:public QLineEdit
{
Q_OBJECT
public:
MyLineEdit(QWidget *parent):QLineEdit(parent){};
~MyLineEdit();
protected:
void keyPressEvent(QKeyEvent *event) override;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
MyLineEdit * line = new MyLineEdit(parent);
setCentralWidget(line);
}
MainWindow::~MainWindow()
{
}
void MainWindow::keyPressEvent(QKeyEvent * event)
{
qDebug() << "MainWindow key PressEvent";
}
void MyLineEdit::keyPressEvent(QKeyEvent * event)
{
qDebug() << "MyLineEdit key PressEvent";
QLineEdit::keyPressEvent(event);
}
MyLineEdit::~MyLineEdit()
{
}
执行结果:
MyLineEdit中添加ignore;
void MyLineEdit::keyPressEvent(QKeyEvent * event)
{
qDebug() << "MyLineEdit key PressEvent";
QLineEdit::keyPressEvent(event);
event->ignore(); //忽略该事件
}
执行结果:
结论:
从例子中可以看到,事件是先传递给指定窗口部件的。
这里确切的说应该是先传递给获得焦点的窗口部件的。
但是如果该部件忽略掉该事件(ignore),那么这个事件就会传递给这个部件的父部件。
在重新实现事件处理函数时,一般要调用父类的相应的事件处理函数来实现默认的操作。
安装事件过滤器
1在MyLineEdit中添加函数声明:
bool event(QEvent *event);
该函数定义:
bool MyLineEdit::event(QEvent * event)
{
if(event->type() == QKeyEvent::KeyPress)
{
qDebug() << "MyLineEdit event";
}
return QLineEdit::event(event);//执行QLineEdit类event()函数的默认操作
}
在MyLineEdit的event()函数中使用了QEvent的type()函数来获取事件的类型,如果是键盘按下事件QEvent::KeyPress,则输出信息。因为event()函数具有bool型的返回值,所以在该函数的最后要使用return语句,这里一般是返回父类的event()函数的操作结果。
2 事件过滤器函数的声明:
bool eventFilter(QObject *obj, QEvent *event);
构造函数的最后添上一行代码:
MyLineEdit::MyLineEdit(QWidget *parent):QLineEdit(parent)
{
installEventFilter(this);
}
事件过滤器函数的定义:
bool MyLineEdit::eventFilter(QObject *obj, QEvent *event) // 事件过滤器
{
if(obj == this) { // 如果本部件上的事件
if(event->type() == QEvent::KeyPress)
qDebug() << tr("事件过滤器");
}
return QWidget::eventFilter(obj, event);
}
在事件过滤器中,先判断该事件的对象是不是本身,如果是,再判断事件类型。最后返回了QWidget默认的事件过滤器的执行结果。
执行结果:
完整代码:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLineEdit>
#include <QDebug>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void keyPressEvent(QKeyEvent *event) override;
};
class MyLineEdit:public QLineEdit
{
Q_OBJECT
public:
MyLineEdit(QWidget *parent);
~MyLineEdit();
bool eventFilter(QObject *obj, QEvent *event) override;
protected:
void keyPressEvent(QKeyEvent *event) override;
bool event(QEvent *event) override; //重新实现event函数
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
MyLineEdit * line = new MyLineEdit(parent);
setCentralWidget(line);
}
MainWindow::~MainWindow()
{
}
void MainWindow::keyPressEvent(QKeyEvent * event)
{
qDebug() << "MainWindow key PressEvent";
}
MyLineEdit::MyLineEdit(QWidget *parent):QLineEdit(parent)
{
installEventFilter(this); //安装事件过滤器
}
void MyLineEdit::keyPressEvent(QKeyEvent * event)
{
qDebug() << "MyLineEdit key PressEvent";
QLineEdit::keyPressEvent(event);
event->ignore();
}
bool MyLineEdit::event(QEvent * event)
{
if(event->type() == QKeyEvent::KeyPress)
{
qDebug() << "MyLineEdit event";
}
return QLineEdit::event(event);
}
bool MyLineEdit::eventFilter(QObject *obj, QEvent *event) // 事件过滤器
{
if(obj == this) { // 如果本部件上的事件
if(event->type() == QEvent::KeyPress)
qDebug() << tr("事件过滤器");
}
return QLineEdit::eventFilter(obj, event);
}
MyLineEdit::~MyLineEdit()
{
}
事件的传递
在每个程序的main()函数的最后,都会调用QApplication类的exec()函数,它会使Qt应用程序进入事件循环,这样就可以使应用程序在运行时接收发生的各种事件。一旦有事件发生,Qt便会构建一个相应的QEvent子类的对象来表示它,然后将它传递给相应的QObject对象或其子对象。
从前面的例子可以看到,事件的传递顺序是这样的:先是事件过滤器,然后是该部件的event()函数,最后是该部件的事件处理函数。这里还要注意,event()函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的(但我在子类中定义也可以)。
传递图:
事件传递过程是:就近,焦点原则(谁在交互,就是谁),子部件响应后的事件可以传递父部件。