QML中常见热区及层级结构

目录

  • 引言
  • 层级结构
    • 默认层级结构
    • z值
      • 作用范围
      • 遮罩实现
      • -1的作用
  • 热区嵌套
    • 与普通元素
    • 与其他热区
    • 与Flickable
  • 事件透传
  • 总结

引言

热区有很多种,诸如MouseArea、DropArea、PinchArea等等,基本都是拦截对应的事件,允许开发者在事件函数对事件进行响应。
本文则以MouseArea为例,对热区的嵌套进行讲解。热区嵌套是开发中经常会遇到的常见,如下图所示,需要为信息卡片的右下角增加额外的按钮,在鼠标悬浮时该按钮才显示,而按钮本身也会对鼠标事件进行响应,这就是所谓的热区嵌套。
[图片]

出现热区嵌套后,会出现事件无法正常传递的情况,导致两个组件单独使用都是好的,嵌套在一起就会出现问题,这也是后面将要讨论的问题。

层级结构

在解决热区嵌套所带来的问题前,需要先理解各个元素的层级结构,因为热区的父类就是Item,只是在Item的基础上进行特化。

默认层级结构

默认层级与生成的顺序有关,也就是QML中的顺序,如下所示:

    Rectangle {
        x: 50
        y: 50
        width: 100
        height: width
        color: Qt.rgba(241 / 255, 148 / 255, 138 / 255)
    }

    Rectangle {
        x: 100
        y: 100
        width: 100
        height: width
        color: Qt.rgba(72 / 255, 201 / 255, 176 / 255)
    }

    Rectangle {
        x: 150
        y: 150
        width: 100
        height: width
        color: Qt.rgba(95 / 255, 174 / 255, 227 / 255)
    }

    Rectangle {
        x: 200
        y: 200
        width: 100
        height: width
        color: Qt.rgba(247 / 255, 220 / 255, 111 / 255)
    }

[图片]

z值

如果不希望按照生成顺序去对图层进行排布,可以通过修改z值完成,代码如下:

    Rectangle {
        x: 100
        y: 100
        width: 100
        height: width
        color: Qt.rgba(72 / 255, 201 / 255, 176 / 255)
        z: 1
    }

作用范围

需要注意的是,z值只对兄弟节点有效,如下所示:

    Item {
        id:root

        Rectangle {
            id: rect1
            x: 50
            y: 50
            width: 100
            height: width
            color: Qt.rgba(241 / 255, 148 / 255, 138 / 255)
        }

        Rectangle {
            id: rect2
            x: 100
            y: 100
            width: 100
            height: width
            color: Qt.rgba(72 / 255, 201 / 255, 176 / 255)
            z: 999
        }

        Rectangle {
            id: rect3
            x: 150
            y: 150
            width: 100
            height: width
            color: Qt.rgba(95 / 255, 174 / 255, 227 / 255)
        }
    }

    Rectangle {
        id: rect4
        x: 50
        y: 150
        width: 100
        height: width
        color: Qt.rgba(247 / 255, 220 / 255, 111 / 255)
        z: 0
    }

[图片]

可以看到尽管rect2的z值设置为999,但并没有显示在rect4之上,也就是绿色矩形依然在黄色矩形之下。这是因为z并不是全局有效的数值,它只能决定兄弟节点之前的排序,并不能超过根节点的所在位置,也就是rect2可以显示在rect3之上,当时不能超过root的显示层级。

遮罩实现

所谓遮罩,就是显示在组件的最上层的一层半透明图层,一般是用在组件禁用时表现置灰。遮罩的实现会使用到z值,组件内堆叠顺序先按照z值进行排序,z值相同则按照生成顺序。
如果需要为组件添加遮罩,且组件内没有对z进行特殊处理,将z值设置为1即可,并不需要设置为999或者是99,这些数值多大都是没有意义的。
如果组件内对z进行了特殊的排序,用于各个图层之间的顺序规划,那么应该有一个属性用来确定当前显示层级的最大值,也就是maximumLevel,遮罩的z值设置成该属性即可,并不需要设置成999这一个令人迷惑的数字。

-1的作用

具有负z值的项绘制在其父项的内容下面,如下所示:

    Rectangle {
        id: rect2
        x: 50
        y: 150
        width: 100
        height: width
        color: Qt.rgba(247 / 255, 220 / 255, 111 / 255)
        z: 0
    }

    Rectangle {
        id: root
        color: Qt.rgba(241 / 255, 148 / 255, 138 / 255)
        width: 150
        height: width

        Rectangle {
            id: rect1
            x: 100
            y: 100
            width: 100
            height: width
            color: Qt.rgba(72 / 255, 201 / 255, 176 / 255)
            z: -1
        }
    }

[图片]

可以看到rect1的z值设置为-1之后,可以显示在root的下方,也就是绿色的矩形会显示在红色的矩形的下方,这是一个可以使用在特殊常见的技巧。当时和之前的作用范围相同,其作用范围不会干扰到其他元素,如上图所示,绿色并没有显示在黄色的底下,即便是rect1的z值为-1、rect2的z值为0。

热区嵌套

与普通元素

首先第一个的问题就是热区和普通像素会不会发生事件争抢,也就是当热区被普通元素覆盖时,事件是否能够继续传递至热区中,如下所示:

    Rectangle {
        id: rect1
        x: 100
        y: 100
        width: 100
        height: width
        color: mouseArea.containsMouse ? "green" : Qt.rgba(72 / 255, 201 / 255, 176 / 255)

        MouseArea {
            id: mouseArea
            anchors.fill: parent
            hoverEnabled: true
        }
    }

    Rectangle {
        id: rect2
        color: Qt.rgba(241 / 255, 148 / 255, 138 / 255)
        width: 150
        height: width
    }

在这里插入图片描述

动图中显示绿色矩形与红色矩形重叠且红色矩形覆盖在绿色矩形上方,当时鼠标在进入到的重叠部分时,底部的绿色矩形依旧变色,这说明附着在绿色矩形上的MouseArea尽管被遮挡,但依然能够生效。但为红色矩形同样附着一个相同的MouseArea,重叠区域将不能再触发。
上述的两个现象说明,普通元素并不会截断事件的传递,MouseArea之间才会相互影响。这一点非常重要,让Qt Quick在界面实现时有非常大的灵活性,可以轻松实现,热区与显示区域的完全分离,例如使用MouseArea和Rectangle实现按钮,MouseArea获取点击事件,Rectangle显示三态,Rectangle可以在MouseArea之上的很多个图层。
这一点与传统的Qt Widget非常不同,任意的QWidget都会截断事件往下方传递,即便是没有事件过滤器、事件函数,如下所示:

    auto btn = new QPushButton("Test", this);
    connect(btn, &QPushButton::clicked, this, [=]{
        btn->setText("Trigger");
    });

    auto widget = new QWidget(this);
    widget->setFixedSize(200, 100);
    widget->setAttribute(Qt::WA_StyledBackground);
    widget->setStyleSheet("background: rgba(255, 0, 0, 0.5)");

在这里插入图片描述

Qt Widget透传需要额外设置才能实现,如下所示:

    setAttribute(Qt::WA_TransparentForMouseEvents, true);

与其他热区

回到开篇的问题,为信息卡片添加按钮,鼠标悬浮至卡片上才显示,很快就可以写出下面的代码实现:

Rectangle {
    color: Qt.rgba(30 / 255, 33 / 255, 35 / 255)
    radius: 12

    Label {
        anchors.fill: parent
        text: "Info"
        color: "white"
        font.family: "Microsoft YaHei UI"
        font.pixelSize: 16
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        hoverEnabled: true
    }

    Button {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.margins: 8
        width: 16
        height: width
        padding: 0
        text: "+"
        visible: mouseArea.containsMouse
    }
}

在这里插入图片描述

如上述动图所示,会出现明明鼠标只是在按钮上方移动,却会出现按钮时而隐藏时而消失的情况,但是按钮的显示隐藏代码中是通过MouseArea的containsMouse属性去控制的,鼠标在显示层面并没有离开底部MouseArea的范围。
此处出现的问题就是热区嵌套时可能遇到错误,MouseArea和Button的出现事件的争取,问题的原因时因为上述代码中MouseArea和Button是兄弟节点,如果改为父子节点则可以解决此问题,如下所示:

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        hoverEnabled: true

        Button {
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            anchors.margins: 8
            width: 16
            height: width
            padding: 0
            text: "+"

            visible: mouseArea.containsMouse
        }
    }

这里可以参考Qt Widget中事件的传递方向,事件由OS产生,通过QApplication分发,先在子组件(比如说一个QButton)中发生的事件,调用了子组件的event函数之后,还会调用父组件(比如说QWidget)的 event 函数。

与Flickable

Flickable这个组件可能对于很多人比较陌生,它是ListView、GridView等的父类,顾名思义控制着滑动的功能,在视口移动时有回弹的动画,和MouseArea同样的会对鼠标事件进行处理。假设有一个信息卡片的容器,而这些卡片同样需要支持拖拽,很自然就想到如下代码(组件FootageCard代码与前文类似,此处不赘述):

    GridView {
        id: gridView
        anchors.fill: parent

        model: 50

        delegate: FootageCard {
            id: infoCard
            width: 96
            height: width
            text: "Info" + index
            color: Qt.rgba(Math.random(), Math.random(), Math.random())

            MouseArea {
                anchors.fill: parent

                onPositionChanged: {
                    dragCard.visible = true

                    let global = mapToGlobal(mouse.x, mouse.y)
                    let local = gridView.mapFromGlobal(global)
                    dragCard.x = local.x
                    dragCard.y = local.y
                }

                onReleased: {
                    dragCard.visible = false
                }
                
                onCanceled: {
                    console.log("onCanceled")
                }
            }
        }

        FootageCard {
            id: dragCard
            width: 96
            height: 96
            text: "Drag"
            visible: false
        }
    }

在这里插入图片描述

如动图所示,出现了明显不符合预期的效果,卡片在拖拽到一半的时候被卡在中间,也就是说鼠标移动事件onPositionChanged一开始时生效的,在移动了一定距离之后事件就被中断,鼠标松开事件onReleased也不再执行,反而是取消事件onCanceled打印了出来。
这里同样会遇到事件被争抢的问题,只需要为MouseArea开启preventStealing属性即可,如下:

MouseArea {
    anchors.fill: parent
    preventStealing: true

    //...
}

正常效果如下所示:
在这里插入图片描述

事件透传

上述常见是热区嵌套的场景,实际开发时也会遇到两个热区重叠的场景,一般是希望前一个MouseArea处理一些事件,后一个MouseArea又处理另一些事件,这里就需要使用到事件的透传。代码如下:

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log("onClicked MouseArea_1")
        }
    }

    MouseArea {
        anchors.fill: parent
        propagateComposedEvents: true
        onClicked: {
            console.log("onClicked MouseArea_2")
            mouse.accepted = false
        }
    }

不仅需要设置属性propagateComposedEvents为true,还需要重新事件函数,将事件的accepted设置为false,才能进行事件透传,这里依旧是Qt的事件传播机制。

总结

热区嵌套问题,本质上就是事件传递的问题,通常就是上层组件截断相关事件,导致下层无法接受对应的信号,抑或是接受的信号无法正确成对,如接收到鼠标按下事件缺无法接收到鼠标松开事件。排查时需要重点关注事件的传递。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/378733.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

kubectl 创建 Pod 背后到底发生了什么?

想象一下,如果我想将 nginx 部署到 Kubernetes 集群,我可能会在终端中输入类似这样的命令: $ kubectl run --imagenginx --replicas3然后回车。几秒钟后,你就会看到三个 nginx pod 分布在所有的工作节点上。这一切就像变魔术一样…

Java学习15-- 面向对象学习3. 对象的创建分析【★】

(本章看不懂多读几遍,弄懂后再往下章看) 面向对象学习3. 对象的创建分析 Java Memory Structure: 如上图所示: 主要分为Stack和Heap Memory 其中Stack主要放method包括main 程序从main开始所以main最先进入Stack,等…

蓝桥杯第八届省赛题笔记------基于单片机的电子钟程序设计与调试

题目要求: 一、基本要求 1.1 使用 CT107D 单片机竞赛板,完成“电子钟”功能的程序设计与调试; 1.2 设计与调试过程中,可参考组委会提供的“资源数据包”; 1.3 Keil 工程文件以准考证号命名,保存在…

C语言系列-文件操作

🌈个人主页: 会编程的果子君 ​💫个人格言:“成为自己未来的主人~” 为什么使用文件 如果没有文件,我们写的程序的数据是存储在电脑的内存上,如果程序退出,内存回收,数据就会丢失了,等再次运行…

MySQL 日志管理

4.6)日志管理 MySQL 支持丰富的日志类型,如下: 事务日志:transaction log 事务日志的写入类型为 "追加",因此其操作为 "顺序IO"; 通常也被称为:预写式日志 write ahead…

[职场] 服务行业个人简历 #笔记#笔记

服务行业个人简历 服务员个人简历范文1 姓名: XXX国籍:中国 目前所在地:天河区民族:汉族 户口所在地:阳江身材: 160cm43kg 婚姻状况:未婚年龄: 21岁 培训认证:诚信徽章: 求职意向及工作经历 人才类型:普通求职 应聘职位: 工作年限:职称:初级 求职类型:全职可到职日期:随时 月薪…

住宅供暖设备行业调研:市场环境将稳定发展阶段中

为使人们生活或进行生产的空间保持在适宜的热状态而设置的供热设施。 向一定的空间加热量的办法,可以直接把产生热量的火炉装在其中;也可以抽出其中的空气,加热后再送回;也可以在其中装置保持在较高温度的物体,向所在空间放热。这种温度较高的…

极值图论基础

目录 一,普通子图禁图 二,Turan问题 三,Turan定理、Turan图 1,Turan定理 2,Turan图 四,以完全二部图为禁图的Turan问题 1,最大边数的上界 2,最大边数的下界 五,…

机器人学、机器视觉与控制 上机笔记(第一版译文版 2.1章节)

机器人学、机器视觉与控制 上机笔记(第一版译文版 2.1章节) 1、前言2、本篇内容3、代码记录3.1、新建se23.2、生成坐标系3.3、将T1表示的变换绘制3.4、完整绘制代码3.5、获取点*在坐标系1下的表示3.6、相对坐标获取完整代码 4、结语 1、前言 工作需要&a…

【前端web入门第四天】03 显示模式+综合案例热词与banner效果

文章目录: 1. 显示模式 1.1 块级元素,行内元素,行内块元素 1.2 转换显示模式 综合案例 综合案例一 热词综合案例二 banner效果 1. 显示模式 什么是显示模式 标签(元素)的显示方式 标签的作用是什么? 布局网页的时候,根据标签的显示模式选择合适的标签摆放内容。…

编码安全风险是什么,如何进行有效的防护

2011年6月28日晚20时左右,新浪微博突然爆发XSS,大批用户中招,被XSS攻击的用户点击恶意链接后并自动关注一位名为HELLOSAMY的用户,之后开始自动转发微博和私信好友来继续传播恶意地址。不少认证用户中招,也导致该XSS被更…

21、数据结构/单向循环链表练习20240203

一、请编程实现单向循环链表的头插,头删、尾插、尾删。 二、请编程实现单向循环链表约瑟夫环 约瑟夫环:用循环链表编程实现约瑟夫问题 n个人围成一圈,从某人开始报数1, 2, …, m,数到m的人出圈,然后从出圈的下一个人…

pytorch入门第一天

今天作为入门pytorch的第一天。打算记录每天学习pytorch的一些理解和笔记,以用来后面回顾。当然如果能帮到和我一样的初学者,那也是不胜荣幸。作为一名初学者,难免有些地方会现错误,欢迎各位大佬指出 预备知识 这里主要介绍pyto…

gtkmm4 应用程序使用 CSS 样式

文章目录 前言css选择器css文件示例源代码效果 前言 程序样式和代码逻辑分离开 使代码逻辑更可观 css选择器 Cambalache提供了两种css-classes 相当于css里的类名:class“类名”css-name 相当于css里的标签名:spin div p 啥的 如上我设置了这个按钮控件的类名为testButton 标…

基于Chrome插件的Chatgpt对话无损导出markdown格式(Typora完美显示)

刚刚提交插件到Chrome插件商店正在审核,想尝试的可以先使用: https://github.com/thisisbaiy/ChatGPT-To-Markdown-google-plugin/tree/main 我将源代码上传至了GitHub,欢迎star, IssueGoogle插件名称为:ChatGPT to MarkDown plus…

元宇宙虚拟数字人实训室:推动高校培养创新技术人才

随着元宇宙时代的到来,虚拟数字人技术逐渐成为当下火热的产业赛道之一。虚拟数字人涉及了计算机、数字媒体、市场营销等学科技术领域,高校可以通过搭建元宇宙虚拟数字人实训室,有效培养对元宇宙行业的专业化理解和研究能力的专业型创新人才。…

Python中的数据类型

如果说python中的数据类型,那我们要从标准数据类型说起,在python中标准数据类型如下: 数字类型: 数字数据类型用于存储数值。 他们是不可改变的数据类型,这意味着改变数字数据类型会分配一个新的对象。 在python2.X中数据类型分的比较多,有int(有符号整型),long(…

原生JS使用PrintJs进行表格打印 -- 遇到的问题总结

需求1:表格自动分页之后,表头在每一页都需要显示 html中表头增加 thead 标签 css样式新增: thead {display: table-header-group; /* 这个属性使thead总是在新的page-break之后重新开始 */ }需求2:表格自动分页之后,…

MySQL优化器

优化器 MySQL存储引擎中存在了一个可插拔的优化器OPTIMIZER_TRACE,可以看到内部查询计划的TRACE信息,从而可以知道MySQL内部执行过程 查询优化器状态 show variables like optimizer_trace;Variable_name Valueoptimizer_trace enabledoff,one_lineoff…

【数据结构】二叉树的顺序结构及链式结构

目录 1.树的概念及结构 1.1树的概念 1.2树的相关概念 ​编辑 1.3树的表示 1.4树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构 2.1二叉树的概念 2.2现实中的二叉树 ​编辑 2.3特殊的二叉树 2.4二叉树的性质 2.5二叉树的存储结…