Qt事件处理的5个层次
参考教材:C++ GUI Programming with Qt4, Second Edition
1、重新实现特殊的事件处理器
重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单也是最常用的形式。例如自定义EventLabel类,继承于QLabel:
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())));
}
2、重新实现QObject::event(),event()函数是所有对象的事件入口, 特殊事件处理函数在event()函数中通过switch...case调用,完成事件的分发,例如自定义CustomWidget类,继承于QWidget,重写event()函数的一般格式是,通过QEvent::type()函数判断传入事件类型,对感兴趣的事件进行自定义处理,不感兴趣的类型传入父对象:
bool CustomWidget::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return QWidget::event(e);
}
3、在特定的QObject对象中安装事件过滤器,这可以解决event()函数的两点不足:
首先,eventFilter()函数不是protected的,我们可以向任何QObject子类安装事件过滤器,假如我们想要拦截所有窗口部件的某一事件,我们不用分别子类化所有窗口部件,并重写所有窗口部件的event()函数,只需在所有窗口部件对象安装事件过滤器,在重写主窗口的eventFilter()函数即可;
其次,事件过滤器可以在目标对象接收事件之前进行处理,如果我们将时间过滤掉,目标对象根本不会见到这个事件,被过滤掉的事件根本传不到目标对象的event()函数中。
自定义MainWindow类如下,为QTextEdit对象安装事件过滤器,调用installEventFilter(QObject* )函数,重载eventFilter()函数的格式一般为:首先判断对象是否为想要拦截事件的目标对象,如果是,则判断发生事件是否为感兴趣的事件,如果是则进行处理,然后返回true(返回true会导致目标对象的event()函数不会被调用),如果对发生事件不感兴趣,则返回false,去调用目标对象的event()函数,如果过滤器接收到的事件不属于要拦截的目标对象,那么,此事件以及其对应的对象肯定是父类安装的过滤器,传给父类即可。
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *event);
private:
QTextEdit *textEdit;
};
MainWindow::MainWindow()
{
textEdit = new QTextEdit;
setCentralWidget(textEdit);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
4、在QCoreApplication::instance()或qApp()(唯一的QApplication对象)上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
QCoreApplication对象都是QObject的子类,因此,我们可以向QApplication或者QCoreApplication添加事件过滤器。这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用,并且应用程序的每个对象的事件都要经过它。尽管很强大,但这种行为会严重降低整个应用程序的事件分发效率。因此,除非是不得不使用的情况,否则的话我们不应该这么做。
注意,
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
5、重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。
最后两种层次基本不会使用到。