QML MouseArea:点击、悬停与坐标获取

这两个 demo 覆盖鼠标交互最基础的能力:怎么接收点击,怎么感知鼠标是否悬停在区域上,怎么拿到点击的具体位置。学会了它们,就理解了 MouseArea 的核心用法,后面的拖拽、缩放、自定义按钮都是在这个基础之上实现的。

这篇介绍两个 demo:

  • 基础点击与悬停 --- 点击计数,鼠标移入移出时,边框加粗提示
  • 点击坐标获取 --- 在点击处画标记点,实时显示最近一次点击的坐标

点击与悬停

效果很简单:一个圆角方块,每点击一次标题下方的数字加1,方块本身的颜色在两种淡蓝之间切换;鼠标悬停在方块上时边框加粗,下方一行文字实时显示鼠标在区域内还是区域外。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        TitleSeparator {
            title: "基础点击与悬停"
            description: "演示 MouseArea 的 clicked 信号与 containsMouse 悬停状态检测"
        }

        Rectangle {
            id: box
            Layout.fillWidth: true
            Layout.fillHeight: true
            radius: 8
            color: clickCount % 2 === 0 ? "#EAF4FF" : "#BCE6FF"
            border.width: mouseArea.containsMouse ? 3 : 1
            border.color: "#1296FF"

            property int clickCount: 0

            Text {
                anchors.centerIn: parent
                text: "点击我\n已点击: " + box.clickCount + " 次"
                font.pointSize: 14
                color: "#1296FF"
                horizontalAlignment: Text.AlignHCenter
            }

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                hoverEnabled: true
                onClicked: box.clickCount++
            }
        }

        Text {
            Layout.alignment: Qt.AlignHCenter
            text: "悬停状态: " + (mouseArea.containsMouse ? "鼠标在内" : "鼠标在外")
            font.pointSize: 12
            color: "#999"
        }
    }
}

关键逻辑解析

MouseArea 是什么

MouseArea 是 QML 里接收鼠标操作的透明区域控件。它本身不可见,通常铺在某个可见控件(这里是 Rectangle)上面,通过 anchors.fill: parent 让它铺满整个方块。鼠标落在方块上,就等于落在 MouseArea 上,点击、悬停都能感知到。

点击计数

onClicked: box.clickCount++MouseArea 的点击信号处理器,鼠标左键点一下触发一次。这里每次点击给自定义属性 clickCount 加一,这个 property int clickCount 是给方块额外定义的一个整数属性,可以在 QML 里随意声明。

方块颜色用取余切换:clickCount % 2 === 0 判断是偶数还是奇数,偶数用浅色,奇数用深色,这样每点一次颜色就换一次,视觉上有反馈。

悬停检测

判断鼠标在不在区域里无需监听坐标,其实 MouseArea 内置了 containsMouse 这个只读属性,直接用即可。它返回 true 表示鼠标在区域内,false 表示在外面。

但有个前提:必须开 hoverEnabled: true。默认它是关的,关着的时候 MouseArea 只在按下、点击这类操作时才有反应,鼠标单纯划过不感知。开了之后才能跟踪悬停,containsMouse 才有意义,边框加粗和下方"鼠标在内/在外"的文字才会跟着变。

点击坐标获取

MouseArea 的点击信号会带一个 mouse 参数,里面存了本次点击的坐标。这个 demo 用 mouse.xmouse.y 在点击处画一个小圆点,并且实时更新一行文字显示最近一次点击的坐标。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        TitleSeparator {
            title: "点击坐标获取"
            description: "通过 mouse.x / mouse.y 获取点击位置,并在点击处绘制标记点"
        }

        Rectangle {
            id: canvas
            Layout.fillWidth: true
            Layout.fillHeight: true
            radius: 8
            color: "#FAFAFA"
            border.color: "#ddd"
            border.width: 1
            clip: true

            // 点击标记点
            Repeater {
                model: clickModel
                delegate: Rectangle {
                    x: model.x - 8
                    y: model.y - 8
                    width: 16
                    height: 16
                    radius: 8
                    color: "#1296FF"
                    opacity: 0.7

                    Text {
                        anchors.centerIn: parent
                        text: index + 1
                        color: "white"
                        font.pixelSize: 9
                        font.bold: true
                    }

                    SequentialAnimation on opacity {
                        loops: Animation.Infinite
                        NumberAnimation { from: 0.7; to: 0.3; duration: 1000 }
                        NumberAnimation { from: 0.3; to: 0.7; duration: 1000 }
                    }
                }
            }

            // 最新点击位置连线指示
            Text {
                id: hintText
                anchors.centerIn: parent
                text: "在区域内点击以标记位置"
                color: "#999"
                font.pointSize: 13
                visible: clickModel.count === 0
            }

            ListModel { id: clickModel }

            MouseArea {
                id: clickArea
                anchors.fill: parent
                onClicked: (mouse) => {
                    clickModel.append({ x: mouse.x, y: mouse.y })
                    if (clickModel.count > 10)
                        clickModel.remove(0)
                    coordText.text = "最近点击: (" + Math.round(mouse.x) + ", " + Math.round(mouse.y) + ")"
                }
            }
        }

        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 20

            Text {
                id: coordText
                text: "最近点击: (-, -)"
                font.pointSize: 12
                color: "#999"
            }

            Button {
                text: "清除标记"
                onClicked: {
                    clickModel.clear()
                    coordText.text = "最近点击: (-, -)"
                }
            }
        }
    }
}

关键逻辑解析

拿到点击坐标

onClicked: (mouse) => { ... } 这里的 mouse 是信号自带的事件对象,mouse.xmouse.y 就是本次点击在 MouseArea 区域内的横向和纵向位置(原点在方块左上角)。这是再普通不过的操作------用鼠标交互,十有八九都要读坐标,比如画板、游戏、地图都靠它定位。

点击处画圆点

项目用 RepeaterListModel 来做:每点一次,onClicked 里往 clickModel 加一条数据,记录 xyRepeater 自动为每条数据创建一个圆点,xy 减去半径 8,让圆点中心正好落在点击处。点多了就把最老的删掉(超过 10 个时 remove(0)),保持界面清爽。

这套"数据里有内容、界面按内容画",也就是 QML 的模型-视图分工。clickModel 只管存点击的位置,界面上有几个点由它决定,自己不用手动增删控件。

圆点动画

圆点加了透明度呼吸的循环动画(opacity 在 0.7 和 0.3 之间来回),让标记点有轻微闪烁感,视觉上更醒目。这只是锦上添花,演示 Repeater 的每个子项都能独立配动画,不影响坐标获取的核心逻辑。

运行验证

  1. Qt Creator 打开示例的 CMakeLists.txt
  2. Ctrl+R 运行;
  3. 基础点击与悬停:多点几次方块看计数和颜色切换,鼠标移出移入看边框粗细变化,看底部"鼠标在内/在外"文字;
  4. 点击坐标获取:在区域里点几下看出现带顺序号的圆点,看底部实时更新的坐标,点"清除标记"看圆点全部消失。

扩展复用方向

  • 点击计数可以扩展成"统计不同按键的点击次数",为后面的多按键 demo 做铺垫
  • 坐标获取配合 Drag 可以做简单的画板:按下拖动画线
  • 把"点击位置加圆点"这套抽成组件,做成可复用的标记层
  • containsMouse 常用来做工具条、按钮的悬浮高亮,很多悬停交互都可以用它

已验证环境

相关推荐
程与留1 小时前
04_Qt项目构建流程深度解析——MOC、UIC、RCC 的作用与原理
qt
Quz1 小时前
QML MouseArea 事件:单击双击、长按与多按键检测
qt
秋田君2 小时前
SqlServer2022 服务以及客户端可视化安装详细教程
qt
平常心的技术小牛12 小时前
Qt-快速上手-QMenuBar
开发语言·qt
qq_150841991 天前
从CVI(C)到Pyside(python)/Qt(C++)/VS(C#)的一点吐槽
c++·qt·c#
我不是疯子是傻子1 天前
Qt 程序打包部署全攻略:从 windeployqt 到 Inno Setup 的静默安装与版本迭代实战
开发语言·qt
小小龙学IT1 天前
Qt 模型/视图框架(Model/View)深度解析:从 MVC 演化到数据展示架构的王者
qt·架构·mvc
Quz1 天前
QML ComboBox 完全自定义样式:背景、内容、下拉项与箭头
qt
skr爱码士1 天前
03_第一个Qt项目:Hello World 的完整拆解
c++·qt·客户端