一 事件来源
- 来源有两类:底层窗口系统;使用 QCoreApplication::sendEvent() 和 QCoreApplication::postEvent() 手动发送。
In general, events come from the underlying window system (spontaneous() returns true), but it is also possible to manually send events using QCoreApplication::sendEvent() and QCoreApplication::postEvent() (spontaneous() returns false).
- 来源可以根据 QEvent::spontaneous() 判断。
二 事件基类
- QEvent
- QEvent 是Qt 所有事件类的基类。
Qt’s main event loop (QCoreApplication::exec()) fetches native window system events from the event queue, translates them into QEvents, and sends the translated events to QObjects.
三 事件分类
- QEvent::Type 定义了Qt中所有的有效事件类型。例如:
- QEvent::Close
- QEvent::KeyPress
- QEvent::Enter 等
- 事件对象中包含了事件的相关参数。例如 QKeyEvent:
- count() 事件按键数量
- key() 事件按键码(Qt::Key)等
四 事件一般流程
-
首先是QCoreApplication (可能是其子类),相关函数:
[virtual] bool QCoreApplication::notify(QObject *receiver, QEvent *event)
-
然后是QObject (receiver,可能是其子类),相关函数:
[virtual] bool QObject::event(QEvent *e)
-
具体事件函数:
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event)
五 事件过滤器
-
在一般流程的基础上,要改变事件处理的流程,给对象安装事件过滤器是常见的方法之一。
-
事件过滤器处理顺序在对象 event() 之前。
-
相关函数:
// 安装事件过滤器 void QObject::installEventFilter(QObject *filterObj) // 事件“过滤” bool QObject::eventFilter(QObject *watched, QEvent *event)
-
官网例子:
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
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; // 不希望该事件继续被处理,返回true;
} else {
return false; // 希望该事件继续被处理,返回false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event); // 否则调用基类方法
}
}
六 事件处理的5种方式
There are five different ways that events can be processed; reimplementing this virtual function is just one of them. All five approaches are listed below:
- Reimplementing paintEvent(), mousePressEvent() and so on. This is the most common, easiest, and least powerful way.
- Reimplementing this function(QCoreApplication::notify). This is very powerful, providing complete control; but only one subclass can be active at a time.
- Installing an event filter on QCoreApplication::instance(). Such an event filter is able to process all events for all widgets, so it’s just as powerful as reimplementing notify(); furthermore, it’s possible to have more than one application-global event filter. Global event filters even see mouse events for disabled widgets. Note that application event filters are only called for objects that live in the main thread.
- Reimplementing QObject::event() (as QWidget does). If you do this you get Tab key presses, and you get to see the events before any widget-specific event filters.
- Installing an event filter on the object. Such an event filter gets all the events, including Tab and Shift+Tab key press events, as long as they do not change the focus widget.
- 简述如下:
- 通过派生重写具体事件处理函数,例如 paintEvent(), mousePressEvent()等。
- 派生QApplication,重写 notify() 方法。
- 给进程对象QCoreApplication::instance()安装过滤器。
- 通过派生重写 event() 方法。
- 给对象安装过滤器。
- 重写event()例子:
class MyClass : public QWidget
{
Q_OBJECT
public:
MyClass(QWidget *parent = 0);
~MyClass();
bool event(QEvent* ev) // 重写
{
if (ev->type() == QEvent::PolishRequest) {
// overwrite handling of PolishRequest if any
doThings();
return true;
} else if (ev->type() == QEvent::Show) {
// complement handling of Show if any
doThings2();
QWidget::event(ev);
return true;
}
// Make sure the rest of events are handled
return QWidget::event(ev);
}
};
七 注意点
- 注意 eventfilter() 、event() 等返回值的含义。true代表事件处理到此为止。
stop it being handled further, return true; otherwise return false.
- 同一个对象安装了多个过滤器,最后安装的过滤器最先被触发。
If multiple event filters are installed on a single object, the filter that was installed last is activated first.
- 除了要处理的事件类型,其它情况请确保调用基类相应事件函数。
Make sure you call the parent event class implementation for all the events you did not handle.
- 若事件接收者对事件不感兴趣(不处理或者返回false),事件将沿着父类传播。
For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver’s parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).
八 参考
- Qt Assistant