功能实现 : 下面是一个扩展的示例代码,增加了几个按钮来演示 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"
温馨提示 :
-
需要添加一个MVS的项目!
-
需要添加源代码的相关路径!
二.核心理解
以此鼠标点击,同时会执行多次sendEvent, 如果使用全局过滤,则可能会多次捕获鼠标点击事件。
【2】:在处理任何事件时,都可以主动调用(直接或者间接)sendEvent, 这会导致notifyEvent嵌套,同时我们还可以局部嵌套一个消息循环,比如
调用QDialog::exec, QMenu::exec, QDrag::exec,最终会在局部运行一个消息循环,该消息循环,会堵住当前的消息,但会重新接手Qt的消息队列,
同样这也会导致notifyEvent嵌套,但这种两种方式形成的嵌套。
因为这两种嵌套有一个最大区别,就是是否经过事件队列,我们可以用以下几种方法,判断是否进入局部消息循环,当进入QApplicaiton::notifyEvent时,
往消息队列压入一个消息,当嵌套进如notiftEvent时,如果在处理压入的消息,则证明可能进入了消息循环,否则肯定没有进入。
其中,左右关系是顺序执行关系,而非条件分支。
需要说明的几点:
-
Qt的消息循环涵盖了window的消息循环,Qt执行每一个Qt的消息,先处理当前消息,然后都会进入一次windows的消息循环,直到清空windows的消息为止。
-
Qt在处理windows消息,会将windows的消息进行包装,继而最终会调用QApplication::notify函数,而其他消息,殊途同归。
-
Qt在分发消息前,首先是需要确定目标,不同的消息目标获取方式不一样,这里暂不解释。
-
确定目标后,需要走过两道过滤器,一道为全局过滤器,任何目标的任何事件都会走一遍全局过滤器,执行过滤器的eventFilter函数。走完全局过滤器,然后
在走一遍目标对象的局部过滤器,任何一道过滤器都有可能拦截掉消息,另外Windows消息还会走另外一道过滤,NativeEventFilter。
5. 做完过滤器之后,执行QObject::event进行消息分发,其中包括:基础用户消息、槽函数消息以及自定义用户消息3大类,用户消息分支非常多,而槽消息,执行
执行提前绑定好的槽函数,而事件Type大于1000的消息,则为自定义用户消息,执行QObject::CustomEvent,这里面大多数函数都是虚函数,因此不要理会是QObject
还是QWidget。
6. 从上述的流程就可以看出来,postEvent只不过是将消息压入到消息队列,最终还是会执行sendEvent的, 所以sendEvent是不进队列的。
7. Windows的系统消息,是不会压入到Qt消息队列,它是现抓现杀的鲜货。
8. 主动调用SendEvent的事件指针,由发送者释放,PostEvent的事件指针归队列释放(PostEvent发送者没有时机释放,除非用智能指针)。
整个流程,我简化了一下,想更详细的了解, 可以直接去查看事件处理的堆栈。
评论(0)