一.来个Demo, 才更加具体 

功能实现 : 下面是一个扩展的示例代码,增加了几个按钮来演示 QDrag::exec、QMenu::exec 和 QThread::exec 的消息循环,同时展示事件逐层向上传递给父界面的责任链传递方式。

#include <QApplication>
#include <QPushButton>
#include <QMainWindow>
#include <QEvent>
#include <QDebug>
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QDrag>
#include <QMimeData>
#include <QMenu>
#include <QThread>
#include <QMouseEvent>

// 自定义事件类型
const QEvent::Type CustomEventType = static_cast<QEvent::Type>(QEvent::User + 1);

// 自定义事件过滤器类
class CustomEventFilter : public QObject
{
    Q_OBJECT
protected:
    bool eventFilter(QObject* obj, QEvent* event) override
    {
        if (event->type() == CustomEventType)
        {
            qDebug() << "Custom event caught in event filter!";
            return true; // 事件已处理
        }
        else if (event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            qDebug() << "Mouse button pressed in event filter at position:" << mouseEvent->pos();
            // 继续传递事件
        }
        return QObject::eventFilter(obj, event);
    }
};

// 全局事件过滤器类
class GlobalEventFilter : public QObject
{
    Q_OBJECT
protected:
    bool eventFilter(QObject* obj, QEvent* event) override
    {
        if (event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            qDebug() << "Mouse button pressed in global event filter at position:" << mouseEvent->pos();
            // 继续传递事件
        }
        return QObject::eventFilter(obj, event);
    }
};

// 模态对话框类
class ModalDialog : public QDialog
{
    Q_OBJECT
public:
    ModalDialog(QWidget* parent = nullptr) : QDialog(parent)
    {
        QVBoxLayout* layout = new QVBoxLayout(this);
        QLabel* label = new QLabel("This is a modal dialog", this);
        QLineEdit* lineEdit = new QLineEdit(this);
        QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);

        layout->addWidget(label);
        layout->addWidget(lineEdit);
        layout->addWidget(buttonBox);

        connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
        connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
    }

protected:
    void mousePressEvent(QMouseEvent* event) override
    {
        qDebug() << "Mouse button pressed in modal dialog at position:" << event->pos();
        QDialog::mousePressEvent(event); // 继续传递事件
    }
};

// 自定义线程类
class CustomThread : public QThread
{
    Q_OBJECT
protected:
    void run() override
    {
        qDebug() << "Thread started!";
        exec(); // 启动线程消息循环
        qDebug() << "Thread finished!";
    }
};

// 主窗口类
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow()
    {
        QPushButton* modalButton = new QPushButton("Show Modal Dialog", this);
        QPushButton* nonModalButton = new QPushButton("Send Custom Event", this);
        QPushButton* dragButton = new QPushButton("Start Drag", this);
        QPushButton* menuButton = new QPushButton("Show Menu", this);
        QPushButton* threadButton = new QPushButton("Start Thread", this);

        modalButton->setGeometry(50, 50, 200, 50);
        nonModalButton->setGeometry(50, 150, 200, 50);
        dragButton->setGeometry(50, 250, 200, 50);
        menuButton->setGeometry(50, 350, 200, 50);
        threadButton->setGeometry(50, 450, 200, 50);

        // 安装事件过滤器
        CustomEventFilter* filter = new CustomEventFilter();
        modalButton->installEventFilter(filter);
        nonModalButton->installEventFilter(filter);
        dragButton->installEventFilter(filter);
        menuButton->installEventFilter(filter);
        threadButton->installEventFilter(filter);

        // 连接按钮点击信号到槽函数
        connect(modalButton, &QPushButton::clicked, this, &MainWindow::onModalButtonClicked);
        connect(nonModalButton, &QPushButton::clicked, this, &MainWindow::onNonModalButtonClicked);
        connect(dragButton, &QPushButton::clicked, this, &MainWindow::onDragButtonClicked);
        connect(menuButton, &QPushButton::clicked, this, &MainWindow::onMenuButtonClicked);
        connect(threadButton, &QPushButton::clicked, this, &MainWindow::onThreadButtonClicked);
    }

protected:
    void customEvent(QEvent* event) override
    {
        if (event->type() == CustomEventType)
        {
            qDebug() << "Custom event caught in main window!";
        }
    }

    void mousePressEvent(QMouseEvent* event) override
    {
        qDebug() << "Mouse button pressed in main window at position:" << event->pos();
        QMainWindow::mousePressEvent(event); // 继续传递事件
    }

private slots:
    void onModalButtonClicked()
    {
        qDebug() << "Modal button clicked!";
        // 发送自定义事件
        QCoreApplication::postEvent(this, new QEvent(CustomEventType));

        // 显示模态对话框
        ModalDialog dialog(this);
        if (dialog.exec() == QDialog::Accepted)
        {
            qDebug() << "Dialog accepted!";
        }
        else
        {
            qDebug() << "Dialog rejected!";
        }
    }

    void onNonModalButtonClicked()
    {
        qDebug() << "Non-modal button clicked!";
        // 发送自定义事件
        QCoreApplication::postEvent(this, new QEvent(CustomEventType));
    }

    void onDragButtonClicked()
    {
        qDebug() << "Drag button clicked!";
        // 开始拖拽操作
        QDrag* drag = new QDrag(this);
        QMimeData* mimeData = new QMimeData;
        mimeData->setText("Dragged data");
        drag->setMimeData(mimeData);
        drag->exec(Qt::CopyAction | Qt::MoveAction);
    }

    void onMenuButtonClicked()
    {
        qDebug() << "Menu button clicked!";
        // 显示菜单
        QMenu menu(this);
        menu.addAction("Option 1");
        menu.addAction("Option 2");
        menu.addAction("Option 3");
        menu.exec(QCursor::pos());
    }

    void onThreadButtonClicked()
    {
        qDebug() << "Thread button clicked!";
        // 启动自定义线程
        CustomThread* thread = new CustomThread();
        thread->start();
    }
};

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    // 安装全局事件过滤器
    GlobalEventFilter* globalFilter = new GlobalEventFilter();
    app.installEventFilter(globalFilter);

    MainWindow window;
    window.resize(400, 600);
    window.show();

    return app.exec();
}

#include "main.moc"

 

 

温馨提示 :

  1. 需要添加一个MVS的项目!
  2. 需要添加源代码的相关路径!

二.核心理解

【1】:Qt在处理任何的队列事件后,都会尝试去清空windows的消息队列,所以windows的消息是不进入Qt事件队列的,另外部分事件具备穿透性,例如鼠标事件,默认会派发给鼠标位置所在顶层窗口,如果该窗口未吞噬事件,则再次派发给它的父窗口,以此类推,派发到根部窗口,这就会导致

以此鼠标点击,同时会执行多次sendEvent, 如果使用全局过滤,则可能会多次捕获鼠标点击事件。

【2】:在处理任何事件时,都可以主动调用(直接或者间接)sendEvent, 这会导致notifyEvent嵌套,同时我们还可以局部嵌套一个消息循环,比如

调用QDialog::exec, QMenu::exec, QDrag::exec,最终会在局部运行一个消息循环,该消息循环,会堵住当前的消息,但会重新接手Qt的消息队列,

同样这也会导致notifyEvent嵌套,但这种两种方式形成的嵌套。

因为这两种嵌套有一个最大区别,就是是否经过事件队列,我们可以用以下几种方法,判断是否进入局部消息循环,当进入QApplicaiton::notifyEvent时,

往消息队列压入一个消息,当嵌套进如notiftEvent时,如果在处理压入的消息,则证明可能进入了消息循环,否则肯定没有进入。

其中,左右关系是顺序执行关系,而非条件分支。

需要说明的几点:

  1.  Qt的消息循环涵盖了window的消息循环,Qt执行每一个Qt的消息,先处理当前消息,然后都会进入一次windows的消息循环,直到清空windows的消息为止。
  2.  Qt在处理windows消息,会将windows的消息进行包装,继而最终会调用QApplication::notify函数,而其他消息,殊途同归。
  3.  Qt在分发消息前,首先是需要确定目标,不同的消息目标获取方式不一样,这里暂不解释。
  4.  确定目标后,需要走过两道过滤器,一道为全局过滤器,任何目标的任何事件都会走一遍全局过滤器,执行过滤器的eventFilter函数。走完全局过滤器,然后

            在走一遍目标对象的局部过滤器,任何一道过滤器都有可能拦截掉消息,另外Windows消息还会走另外一道过滤,NativeEventFilter。

       5.  做完过滤器之后,执行QObject::event进行消息分发,其中包括:基础用户消息、槽函数消息以及自定义用户消息3大类,用户消息分支非常多,而槽消息,执行

            执行提前绑定好的槽函数,而事件Type大于1000的消息,则为自定义用户消息,执行QObject::CustomEvent,这里面大多数函数都是虚函数,因此不要理会是QObject

            还是QWidget。

       6.  从上述的流程就可以看出来,postEvent只不过是将消息压入到消息队列,最终还是会执行sendEvent的, 所以sendEvent是不进队列的。

       7.  Windows的系统消息,是不会压入到Qt消息队列,它是现抓现杀的鲜货。

       8.  主动调用SendEvent的事件指针,由发送者释放,PostEvent的事件指针归队列释放(PostEvent发送者没有时机释放,除非用智能指针)。

整个流程,我简化了一下,想更详细的了解, 可以直接去查看事件处理的堆栈。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。