如何自定义Qt Quick Popup的关闭行为以实现点击自身控件不关闭、外部区域点击关闭?
解决Popup自定义关闭行为的问题
要实现点击自定义控件时Popup不关闭,点击外部区域才关闭的需求,你可以通过以下两种方式来实现:
方法一:拦截Popup的pressOutsideEvent信号
当使用Popup.CloseOnPressOutside时,点击自定义控件会被判定为"outside"导致关闭,我们可以通过重写pressOutsideEvent来判断点击位置是否属于触发Popup的自定义控件,如果是则阻止关闭。
修改你的MenuSpinner.qml代码如下:
import QtQuick 2.0 import QtQuick.Controls 2.5 Rectangle{ width: 300 height: 50 property bool bTextClicked: false onBTextClickedChanged: { if(bTextClicked) popup.open() else popup.close() } Rectangle{ id: triggerRect width: 100 height: parent.height x: rect1.width border.color: "blue" Text { id: text anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: qsTr("value") font.pixelSize: 16 } MouseArea{ anchors.fill: parent onClicked: { bTextClicked = !bTextClicked } } } Popup { id: popup leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 width: parent.width height: parent.height // 基础关闭策略设为点击外部关闭 closePolicy: Popup.CloseOnPressOutside background: Rectangle { anchors.fill: parent color: "transparent" } Rectangle{ id: rect1 width: 100 height: 50 Text { text: qsTr("pop1") anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter font.pixelSize: 16 } color: "transparent" border.color: "blue" } Rectangle{ id: rect2 x: parent.width - rect1.width width: 100 height: 50 Text { text: qsTr("pop2") anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter font.pixelSize: 16 } color: "transparent" border.color: "blue" } // 重写外部点击事件,判断点击位置是否在触发控件范围内 function pressOutsideEvent(event) { const localPos = triggerRect.mapFromGlobal(event.globalPos) if (triggerRect.contains(localPos)) { // 点击的是触发控件,阻止Popup关闭 event.accepted = true } else { // 点击外部,允许关闭 event.accepted = false } } onClosed: { bTextClicked = false console.log("close") } } }
方法二:使用NoAutoClose+全局鼠标监听
如果方法一不生效,你可以设置closePolicy: Popup.NoAutoClose,然后自己监听全局鼠标点击事件,手动判断点击区域是否在自定义控件和Popup之外,若是则关闭Popup。
修改后的MenuSpinner.qml示例:
import QtQuick 2.0 import QtQuick.Controls 2.5 Rectangle{ width: 300 height: 50 property bool bTextClicked: false onBTextClickedChanged: { if(bTextClicked) popup.open() else popup.close() } Rectangle{ id: triggerRect width: 100 height: parent.height x: rect1.width border.color: "blue" Text { id: text anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: qsTr("value") font.pixelSize: 16 } MouseArea{ anchors.fill: parent onClicked: { bTextClicked = !bTextClicked } } } Popup { id: popup leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 width: parent.width height: parent.height closePolicy: Popup.NoAutoClose background: Rectangle { anchors.fill: parent color: "transparent" } Rectangle{ id: rect1 width: 100 height: 50 Text { text: qsTr("pop1") anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter font.pixelSize: 16 } color: "transparent" border.color: "blue" } Rectangle{ id: rect2 x: parent.width - rect1.width width: 100 height: 50 Text { text: qsTr("pop2") anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter font.pixelSize: 16 } color: "transparent" border.color: "blue" } onClosed: { bTextClicked = false console.log("close") } } // 监听全局鼠标点击事件,仅在Popup显示时生效 MouseArea{ anchors.fill: parent.parent visible: popup.visible propagateComposedEvents: true // 不阻断其他控件的点击事件 onClicked: { // 转换点击坐标到控件局部坐标系,判断是否在当前控件或Popup内 const selfLocalPos = mapFromGlobal(mouseX, mouseY) const popupLocalPos = popup.mapFromGlobal(mouseX, mouseY) if(!contains(selfLocalPos) && !popup.contains(popupLocalPos)){ popup.close() } } } }
补充说明
- 方法一更简洁,利用Popup自身的事件拦截机制,推荐优先使用。
- 方法二中的
propagateComposedEvents: true非常重要,能保证主界面上的其他按钮依然可以正常响应点击事件,不会被这个全局MouseArea阻断。
内容的提问来源于stack exchange,提问作者Zeus
相关产品推荐
相关产品推荐

