本文介绍在 Qt 编程中如何实现当鼠标点击弹窗外部区域时,该弹窗可以自动关闭的效果。

1. ActivationChange 事件

弹窗通过监听 Activation 改变事件,来判断自身是否还是当前的活动窗口,如果不是则关闭自身。

在 QWidget 里面重写 event,捕获 QEvent::ActivationChange 事件:

1
2
3
4
5
6
7
8
9
bool UserCenterDlg::event(QEvent* e) {
if (e->type() == QEvent::ActivationChange) {
QWidget* curActiveWin = QApplication::activeWindow();
if (curActiveWin != this) {
this->close();
}
}
return QWidget::event(e);
}

2. Qt::WA_NoMouseReplay 介绍

摘自 Qt 帮助文档:

Qt::WA_NoMouseReplay: Used for pop-up widgets. Indicates that the most recent mouse press event should not be replayed when the pop-up widget closes. The flag is set by the widget’s author and cleared by the Qt kernel every time the widget receives a new mouse event.

Qt::WA_NoMouseReplay 属性可以用来避免如下情况的发生:
在鼠标点击 Popup Widget 外部区域时,该 Widget 接收到自身的 Activation 状态发生改变,关闭自身,但在该 Widget 在关闭后,Qt 依然会将鼠标点击事件继续向下传递,从而窗口外区域下面的控件也会被点击。

由于 Qt::WA_NoMouseReplay 属性只对具有 Popup 属性的 Widget 起作用,因此只能使用 QWidget,不能使用 QDialog,并设置 Qt::Popup 属性。

1
setWindowFlags(windowFlags() | Qt::Popup);

何时设置 Qt::WA_NoMouseReplay 属性了?

重写 mousePressEvent,在鼠标按下事件发生时,设置 Qt::WA_NoMouseReplay 属性。

1
2
3
4
void UserCenterDlg::mousePressEvent(QMouseEvent* e) {
setAttribute(Qt::WA_NoMouseReplay);
QWidget::mousePressEvent(e);
}

3. 自定义 closed 信号

QWidget 没有关闭信号,我们可以自定义关闭信号,该信号在 closeEvent 中触发。

1
2
3
4
void UserCenterDlg::closeEvent(QCloseEvent* e) {
emit closed();
QWidget::closeEvent(e);
}

限于政策原因,在您看到该文章时,博客可能已经关闭了评论功能🥺

您可以通过在 blog-comment 项目中提交Issue来间接地发表评论🍀