Qt 事件 < 一 > 基础总结
在学习事件时,经常会有这样的疑问,在学习本文前,先了解这个内容,有助于今后深入理解事件、信号机制。
事件与信号的区别:
事件(Event):
事件是 Qt 中处理用户输入、状态变化等的一种机制。
事件主要用于处理异步的、系统产生的事件,比如键盘输入、鼠标点击等。
事件的处理通过重写事件处理函数,比如keyPressEvent、mousePressEvent等。
事件处理是基于事件分发和继承的机制,对象通过重写相应的事件处理函数来响应事件。
信号与槽(Signal and Slot):
信号与槽是 Qt 中处理对象之间通信的一种机制。
信号与槽主要用于处理同步的、对象之间的通信,一个对象发出信号,另一个对象通过槽响应这个信号。
信号与槽的机制通过 connect 函数建立,当信号发射时,与之相连的槽函数被调用。
信号与槽机制是基于元对象系统的,对象不需要了解彼此,通过信号与槽进行连接。
总体区别:
事件是一种更低层次、更基础的机制,处理系统和用户输入。
信号与槽是一种更高层次、更灵活的机制,处理对象之间的通信和协作。
文章目录
1. 事件简介:
- 事件(Event): 在 Qt 中,事件是程序运行时发生的事情,如按键、鼠标点击、定时器超时等。每个事件都是一个 C++ 对象,派生自
QEvent
类。在 Qt 中,事件处理是一个基本的概念,它涉及到多个对象之间的协作和通信。Qt 的事件模型是 Qt 应用程序中的核心机制之一,它允许对象能够响应来自用户、系统和其他对象的事件。
2. Qt 事件处理流程与时序:
在 Qt 中,事件处理是一个基本的概念,它涉及到多个对象之间的协作和通信。事件首先传递给具有焦点的对象,然后沿着父对象链向上传递,最后传递给顶层窗口。
1. 事件传递流程:
事件传递的流程主要涉及到两个概念:事件的发送和事件的接收。
- 事件的发送:
- 来源对象产生事件: 事件首先由某个对象产生,这个对象可以是窗口、部件、定时器等。
- 事件封装: 事件被封装成
QEvent
类型的对象。不同的事件类型对应不同的QEvent
子类,比如键盘事件对应QKeyEvent
(后面类型会有补充)。
- 事件的接收:
- 焦点对象接收: 事件首先传递给具有焦点的对象。可以通过
setFocus()
函数设置对象的焦点。焦点有其系统默认。 - 事件传递: 事件沿着对象的父子关系链向上传递。从接收焦点的对象开始,逐级向上传递到顶层窗口。
- 事件过滤器: 在事件到达目标对象之前,可以通过事件过滤器进行拦截和处理。
- 焦点对象接收: 事件首先传递给具有焦点的对象。可以通过
2. 事件处理时序:
Qt 确保事件按照正确的时序传递,这是为了确保用户在交互时看到的效果是符合预期的。以下是一些常见事件的时序:
-
键盘事件时序:
QEvent::KeyPress
发生。keyPressEvent
处理函数被调用。
-
鼠标事件时序:
QEvent::MouseButtonPress
发生。mousePressEvent
处理函数被调用。QEvent::MouseMove
发生。mouseMoveEvent
处理函数被调用。QEvent::MouseButtonRelease
发生。mouseReleaseEvent
处理函数被调用。
-
定时器事件时序:
QEvent::Timer
发生。- 与定时器关联的
timerEvent
处理函数被调用。
3. 事件的中断与接收:
-
中断事件传递:
- 通过
QCoreApplication::sendEvent
中断事件传递,将事件直接发送给指定对象。 - 通过
QCoreApplication::postEvent
在事件队列中插入事件,等待下一次事件循环处理。
- 通过
-
事件接收优先级:
- 当一个对象接收到多个事件时,事件的处理顺序与它们的接收顺序一致。
- 如果一个事件被某个对象接收并处理,它将不再传递给该对象的父对象。
-
补充:
-
当一个事件发生时,首先会将该事件发送给对象本身。如果对象没有处理该事件,事件将被传递给对象的父对象,然后一直传递到最顶层的父对象。
-
对于某个对象,如果它已经被安装了事件过滤器,事件将先传递给事件过滤器,然后再传递给对象本身的事件处理函数。如果对象的事件过滤器处理了事件并返回
true
,那么该事件将不再传递给对象本身的事件处理函数。 -
如果某个对象的事件处理函数返回
true
,表示该对象已经成功处理了事件,那么该事件将不再传递给它的父对象。这种机制确保了事件的停止传递,即事件只会被一个对象处理。 -
例:对于
QWidget
及其子类,它们之间的事件传递遵循父子关系。当一个事件发生在某个QWidget
上时,首先会传递给该QWidget
,然后再传递给它的父QWidget
,一直传递到顶层的QWidget
。// 事件传递到父对象的示例 void ChildWidget::mousePressEvent(QMouseEvent *event) { qDebug() << "ChildWidget mousePressEvent"; // 处理完事件后调用父类的事件处理函数 QWidget::mousePressEvent(event); }
-
4. 事件的停止和忽略:
-
停止事件传递:
- 通过
event->accept()
可以停止事件的传递,确保事件不再传递给其他对象。
- 通过
-
忽略事件:
- 通过
event->ignore()
表示对象忽略该事件,让它继续传递给父对象。
- 通过
理解 Qt 事件处理的流程和时序对于开发交互性强的应用程序至关重要。清晰的时序和传递流程可以帮助更好地处理和调试事件处理代码。
3. 重要事件类关系:
在 Qt 中,事件的处理涉及多个重要类,它们之间的关系如下:
1. QEvent
QEvent
: 是所有事件类的基类。它本身是一个抽象类,定义了事件的基本属性和方法。各种具体的事件类型都是 QEvent
的派生类,例如 QMouseEvent
、QKeyEvent
、QTimerEvent
等。
2. QObject
QObject
: 是 Qt 中所有对象的基类,它提供了事件处理的机制。QObject
类中有一些关于事件处理的重要函数,比如 event()
用于处理事件,installEventFilter()
用于安装事件过滤器。
// 事件处理的示例
bool MyObject::event(QEvent *event) {
if (event->type() == QEvent::MouseButtonPress) {
// 处理鼠标按下事件
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << "Mouse Pressed:" << mouseEvent->pos();
return true;
}
// 其他事件交给基类处理
return QObject::event(event);
}
3. QWidget
QWidget
: 是用户界面元素的基类,包括窗口和部件。QWidget
类继承自 QObject
,因此也具备了事件处理的能力。在 QWidget
中,有一些与事件相关的函数,比如 event()
用于处理事件,setMouseTracking()
用于设置是否跟踪鼠标等。
// QWidget 中事件处理的示例
void MyWidget::mousePressEvent(QMouseEvent *event) {
qDebug() << "Mouse Pressed:" << event->pos();
}
4. 具体事件类
具体事件类(如 QMouseEvent
、QKeyEvent
等): 这些类继承自 QEvent
,用于表示具体的事件类型。每个具体事件类都包含了与该事件类型相关的信息和方法。例如,QMouseEvent
包含了鼠标事件的信息,如鼠标位置、按键状态等。
// QMouseEvent 的使用示例
void MyWidget::mousePressEvent(QMouseEvent *event) {
qDebug() << "Mouse Pressed:" << event->pos();
if (event->button() == Qt::LeftButton) {
// 处理鼠标左键按下事件
}
}
这些类之间的关系构成了 Qt 的事件处理体系,通过继承和多态机制,Qt 实现了灵活且强大的事件处理框架。
4. 事件类型:
- 常见事件类型:
QEvent::KeyPress
和QEvent::KeyRelease
:键盘按下和释放事件。QEvent::MouseButtonPress
和QEvent::MouseButtonRelease
:鼠标按钮按下和释放事件。QEvent::MouseMove
:鼠标移动事件。QEvent::Wheel
:鼠标滚轮事件。QEvent::Resize
:窗口大小调整事件。QEvent::Timer
:定时器事件。
在 Qt 中,不同的事件类型对应着不同的 QEvent
子类。以下是一些常见事件类型及其对应的子类:
1. 基础事件类型:
-
QEvent::Timer
: 定时器事件。对应的子类是QTimerEvent
。class QTimerEvent : public QEvent { public: QTimerEvent(int timerId); int timerId() const; };
2. 键盘事件类型:详情见 <qt 事件 二>;
-
QEvent::KeyPress
和QEvent::KeyRelease
: 键盘按下和释放事件。对应的子类是QKeyEvent
。class QKeyEvent : public QEvent { public: QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text = QString(), bool autorep = false, ushort count = 1); int key() const; Qt::KeyboardModifiers modifiers() const; QString text() const; bool isAutoRepeat() const; ushort count() const; };
3. 鼠标事件类型:详情见 <qt 事件 二>
-
QEvent::MouseButtonPress
、QEvent::MouseButtonRelease
和QEvent::MouseMove
: 鼠标按键按下、释放和移动事件。对应的子类是QMouseEvent
。class QMouseEvent : public QEvent { public: QMouseEvent(Type type, const QPointF &localPos, const QPointF &windowPos, const QPointF &screenPos, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); QPointF localPos() const; QPointF windowPos() const; QPointF screenPos() const; Qt::MouseButton button() const; Qt::MouseButtons buttons() const; Qt::KeyboardModifiers modifiers() const; };
4. 绘图事件类型:详情见 < qt 绘图 >;
-
QEvent::Paint
: 绘图事件。对应的子类是QPaintEvent
。class QPaintEvent : public QEvent { public: QPaintEvent(const QRect &rect); QRect rect() const; };
5. 窗口事件类型:
-
QEvent::Resize
: 窗口大小变化事件。对应的子类是QResizeEvent
。class QResizeEvent : public QEvent { public: QResizeEvent(const QSize &size, const QSize &oldSize); QSize size() const; QSize oldSize() const; };
6. 输入法事件类型:
-
QEvent::InputMethod
和QEvent::InputMethodQuery
: 输入法相关事件。对应的子类是QInputMethodEvent
。class QInputMethodEvent : public QEvent { public: QInputMethodEvent(); QString commitString() const; QList<QInputMethodEvent::Attribute> attributes() const; static QList<QInputMethodEvent::Attribute> attributesFromString(const QString &string); };
7. 拖拽事件类型:
-
QEvent::DragEnter
、QEvent::DragMove
、QEvent::DragLeave
和QEvent::Drop
: 拖拽相关事件。对应的子类是QDragEnterEvent
、QDragMoveEvent
、QDragLeaveEvent
和QDropEvent
。class QDragEnterEvent : public QDragMoveEvent { public: QDragEnterEvent(const QPoint &pos, Qt::DropActions actions, const QMimeData *data, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); };
这只是一小部分事件类型和对应的 QEvent
子类,Qt 提供了丰富的事件类型和相应的类来满足各种应用场景的需求。不同的事件类型对应不同的处理函数,开发者可以根据需要重写相应的处理函数来处理特定类型的事件。
5. 事件过滤器:
在 Qt 中,事件过滤器是一种机制,允许一个对象监视另一个对象的事件。通过事件过滤器,可以在目标对象接收到事件之前截获并处理该事件,也可以决定是否阻止该事件的传播。
1. 安装事件过滤器:
事件过滤器通过调用 installEventFilter()
函数来安装。该函数的参数是一个继承自 QObject
的对象,通常是当前对象。
// 在构造函数或初始化阶段安装事件过滤器
someObject->installEventFilter(this);
2. 重写事件过滤器:
在安装事件过滤器的对象中,需要重写 QObject
的 eventFilter()
函数。
// 在类的声明中
class MyObject : public QObject {
Q_OBJECT
protected:
// 重写事件过滤器
bool eventFilter(QObject *obj, QEvent *event) override;
};
3. 事件过滤器的实现:
事件过滤器函数接收两个参数:
obj
:是安装了事件过滤器的对象,即目标对象。event
:是接收到的事件。
事件过滤器函数的返回值是 bool
类型。如果返回 true
,表示过滤器已经处理了该事件,该事件不再传递给目标对象;如果返回 false
,表示该事件会继续传递给目标对象。
bool MyObject::eventFilter(QObject *obj, QEvent *event) {
if (obj == watchedObject) {
if (event->type() == QEvent::KeyPress) {
// 处理按键事件
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "Key Pressed:" << keyEvent->key();
// 返回 true 表示事件已经处理,不再传递给 watchedObject
return true;
}
}
// 返回 false 表示该事件会继续传递给 watchedObject
return false;
}
4. 移除事件过滤器:
当不再需要事件过滤器时,可以通过 removeEventFilter()
函数来移除。
someObject->removeEventFilter(this);
5. 注意事项:
- 事件过滤器可以被多个对象安装,但每个对象只能安装一个事件过滤器。
- 事件过滤器对所有类型的事件都起作用,因此需要在事件过滤器中判断事件的类型。
- 当事件过滤器返回
true
时,表示事件已经被过滤,不再传递给目标对象,这可能会影响目标对象的正常行为,需要慎重使用。
通过事件过滤器,可以在不修改目标对象代码的情况下,实现对其事件的监视和干预。这为 Qt 提供了一种灵活的事件处理机制。
6. 完整示例:
#include <QtWidgets>
class KeyFilter : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event) override
{
if (obj == watchedWidget) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "Key Pressed:" << keyEvent->key();
return true; // 事件已处理,不再传递给 watchedWidget
}
}
return false; // 事件继续传递给 watchedWidget
}
public:
KeyFilter(QWidget *watched) : watchedWidget(watched)
{
watched->installEventFilter(this);
}
private:
QWidget *watchedWidget;
};
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget()
{
// 创建一个按钮
QPushButton *button = new QPushButton("Click Me", this);
// 创建事件过滤器,监控当前窗口的按键事件
keyFilter = new KeyFilter(this);
// 连接按钮的点击信号到自定义槽函数
connect(button, &QPushButton::clicked, this, &MyWidget::onButtonClick);
}
private slots:
void onButtonClick()
{
qDebug() << "Button Clicked!";
}
private:
KeyFilter *keyFilter;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget widget;
widget.setGeometry(100, 100, 300, 200);
widget.show();
return app.exec();
}
这个例子中,KeyFilter
是一个自定义的事件过滤器类,它继承自 QObject
,实现了 eventFilter
函数。在 MyWidget
类中,我们创建了一个按钮和一个 KeyFilter
的实例。KeyFilter
实例被安装在 MyWidget
上,用于监控它的按键事件。在按钮点击时,会触发相应的槽函数。
6. 自定义事件:
- 自定义事件: 通过派生自
QEvent
类来创建自定义事件。可以通过QCoreApplication::sendEvent
和QCoreApplication::postEvent
来发送和接收它们。
这个总结提供了 Qt 事件模型基础的关键概念。通过深入学习这些概念,将能够更好地理解 Qt 应用程序中事件的处理和传递。