Qt 事件 < 一 > 基础总结

Qt 事件 < 一 > 基础总结

在学习事件时,经常会有这样的疑问,在学习本文前,先了解这个内容,有助于今后深入理解事件、信号机制。
事件与信号的区别:

事件(Event):
	事件是 Qt 中处理用户输入、状态变化等的一种机制。
	事件主要用于处理异步的、系统产生的事件,比如键盘输入、鼠标点击等。
	事件的处理通过重写事件处理函数,比如keyPressEvent、mousePressEvent等。
	事件处理是基于事件分发和继承的机制,对象通过重写相应的事件处理函数来响应事件。
信号与槽(Signal and Slot):
	信号与槽是 Qt 中处理对象之间通信的一种机制。
	信号与槽主要用于处理同步的、对象之间的通信,一个对象发出信号,另一个对象通过槽响应这个信号。
	信号与槽的机制通过 connect 函数建立,当信号发射时,与之相连的槽函数被调用。
	信号与槽机制是基于元对象系统的,对象不需要了解彼此,通过信号与槽进行连接。
总体区别:
	事件是一种更低层次、更基础的机制,处理系统和用户输入。
	信号与槽是一种更高层次、更灵活的机制,处理对象之间的通信和协作。

1. 事件简介:

  • 事件(Event): 在 Qt 中,事件是程序运行时发生的事情,如按键、鼠标点击、定时器超时等。每个事件都是一个 C++ 对象,派生自 QEvent 类。在 Qt 中,事件处理是一个基本的概念,它涉及到多个对象之间的协作和通信。Qt 的事件模型是 Qt 应用程序中的核心机制之一,它允许对象能够响应来自用户、系统和其他对象的事件。

2. Qt 事件处理流程与时序:

在 Qt 中,事件处理是一个基本的概念,它涉及到多个对象之间的协作和通信。事件首先传递给具有焦点的对象,然后沿着父对象链向上传递,最后传递给顶层窗口。

1. 事件传递流程:

事件传递的流程主要涉及到两个概念:事件的发送和事件的接收。

  • 事件的发送:
    1. 来源对象产生事件: 事件首先由某个对象产生,这个对象可以是窗口、部件、定时器等。
    2. 事件封装: 事件被封装成 QEvent 类型的对象。不同的事件类型对应不同的 QEvent 子类,比如键盘事件对应 QKeyEvent(后面类型会有补充)。
  • 事件的接收:
    1. 焦点对象接收: 事件首先传递给具有焦点的对象。可以通过 setFocus() 函数设置对象的焦点。焦点有其系统默认。
    2. 事件传递: 事件沿着对象的父子关系链向上传递。从接收焦点的对象开始,逐级向上传递到顶层窗口。
    3. 事件过滤器: 在事件到达目标对象之前,可以通过事件过滤器进行拦截和处理。

2. 事件处理时序:

Qt 确保事件按照正确的时序传递,这是为了确保用户在交互时看到的效果是符合预期的。以下是一些常见事件的时序:

  • 键盘事件时序:

    1. QEvent::KeyPress 发生。
    2. keyPressEvent 处理函数被调用。
  • 鼠标事件时序:

    1. QEvent::MouseButtonPress 发生。
    2. mousePressEvent 处理函数被调用。
    3. QEvent::MouseMove 发生。
    4. mouseMoveEvent 处理函数被调用。
    5. QEvent::MouseButtonRelease 发生。
    6. mouseReleaseEvent 处理函数被调用。
  • 定时器事件时序:

    1. QEvent::Timer 发生。
    2. 与定时器关联的 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 的派生类,例如 QMouseEventQKeyEventQTimerEvent 等。

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. 具体事件类

具体事件类(如 QMouseEventQKeyEvent 等): 这些类继承自 QEvent,用于表示具体的事件类型。每个具体事件类都包含了与该事件类型相关的信息和方法。例如,QMouseEvent 包含了鼠标事件的信息,如鼠标位置、按键状态等。

// QMouseEvent 的使用示例
void MyWidget::mousePressEvent(QMouseEvent *event) {
    qDebug() << "Mouse Pressed:" << event->pos();
    if (event->button() == Qt::LeftButton) {
        // 处理鼠标左键按下事件
    }
}

这些类之间的关系构成了 Qt 的事件处理体系,通过继承和多态机制,Qt 实现了灵活且强大的事件处理框架。

4. 事件类型:

  • 常见事件类型:
    • QEvent::KeyPressQEvent::KeyRelease:键盘按下和释放事件。
    • QEvent::MouseButtonPressQEvent::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::KeyPressQEvent::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::MouseButtonPressQEvent::MouseButtonReleaseQEvent::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::InputMethodQEvent::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::DragEnterQEvent::DragMoveQEvent::DragLeaveQEvent::Drop 拖拽相关事件。对应的子类是 QDragEnterEventQDragMoveEventQDragLeaveEventQDropEvent

    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. 重写事件过滤器:

在安装事件过滤器的对象中,需要重写 QObjecteventFilter() 函数。

// 在类的声明中
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::sendEventQCoreApplication::postEvent 来发送和接收它们。

这个总结提供了 Qt 事件模型基础的关键概念。通过深入学习这些概念,将能够更好地理解 Qt 应用程序中事件的处理和传递。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值