QML中常见热区及层级结构

目录

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

引言

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

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

层级结构

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

默认层级结构

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

    Rectangle {x: 50y: 50width: 100height: widthcolor: Qt.rgba(241 / 255, 148 / 255, 138 / 255)}Rectangle {x: 100y: 100width: 100height: widthcolor: Qt.rgba(72 / 255, 201 / 255, 176 / 255)}Rectangle {x: 150y: 150width: 100height: widthcolor: Qt.rgba(95 / 255, 174 / 255, 227 / 255)}Rectangle {x: 200y: 200width: 100height: widthcolor: Qt.rgba(247 / 255, 220 / 255, 111 / 255)}

[图片]

z值

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

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

作用范围

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

    Item {id:rootRectangle {id: rect1x: 50y: 50width: 100height: widthcolor: Qt.rgba(241 / 255, 148 / 255, 138 / 255)}Rectangle {id: rect2x: 100y: 100width: 100height: widthcolor: Qt.rgba(72 / 255, 201 / 255, 176 / 255)z: 999}Rectangle {id: rect3x: 150y: 150width: 100height: widthcolor: Qt.rgba(95 / 255, 174 / 255, 227 / 255)}}Rectangle {id: rect4x: 50y: 150width: 100height: widthcolor: 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: rect2x: 50y: 150width: 100height: widthcolor: Qt.rgba(247 / 255, 220 / 255, 111 / 255)z: 0}Rectangle {id: rootcolor: Qt.rgba(241 / 255, 148 / 255, 138 / 255)width: 150height: widthRectangle {id: rect1x: 100y: 100width: 100height: widthcolor: Qt.rgba(72 / 255, 201 / 255, 176 / 255)z: -1}}

[图片]

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

热区嵌套

与普通元素

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

    Rectangle {id: rect1x: 100y: 100width: 100height: widthcolor: mouseArea.containsMouse ? "green" : Qt.rgba(72 / 255, 201 / 255, 176 / 255)MouseArea {id: mouseAreaanchors.fill: parenthoverEnabled: true}}Rectangle {id: rect2color: Qt.rgba(241 / 255, 148 / 255, 138 / 255)width: 150height: 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: 12Label {anchors.fill: parenttext: "Info"color: "white"font.family: "Microsoft YaHei UI"font.pixelSize: 16horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}MouseArea {id: mouseAreaanchors.fill: parenthoverEnabled: true}Button {anchors.right: parent.rightanchors.bottom: parent.bottomanchors.margins: 8width: 16height: widthpadding: 0text: "+"visible: mouseArea.containsMouse}
}

在这里插入图片描述

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

    MouseArea {id: mouseAreaanchors.fill: parenthoverEnabled: trueButton {anchors.right: parent.rightanchors.bottom: parent.bottomanchors.margins: 8width: 16height: widthpadding: 0text: "+"visible: mouseArea.containsMouse}}

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

与Flickable

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

    GridView {id: gridViewanchors.fill: parentmodel: 50delegate: FootageCard {id: infoCardwidth: 96height: widthtext: "Info" + indexcolor: Qt.rgba(Math.random(), Math.random(), Math.random())MouseArea {anchors.fill: parentonPositionChanged: {dragCard.visible = truelet global = mapToGlobal(mouse.x, mouse.y)let local = gridView.mapFromGlobal(global)dragCard.x = local.xdragCard.y = local.y}onReleased: {dragCard.visible = false}onCanceled: {console.log("onCanceled")}}}FootageCard {id: dragCardwidth: 96height: 96text: "Drag"visible: false}}

在这里插入图片描述

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

MouseArea {anchors.fill: parentpreventStealing: true//...
}

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

事件透传

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

    MouseArea {anchors.fill: parentonClicked: {console.log("onClicked MouseArea_1")}}MouseArea {anchors.fill: parentpropagateComposedEvents: trueonClicked: {console.log("onClicked MouseArea_2")mouse.accepted = false}}

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

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

米贸搜|Facebook在购物季使用的Meta广告投放流程

一、账户简化 当广告系列开始投放后,每个广告组都会经历一个初始的“机器学习阶段”。简化账户架构可以帮助AI系统更快获得广告主所需的成效。例如: 每周转化次数超过50次的广告组,其单次购物费用要低28%;成功结束机器学习阶段的…

图像处理入门:OpenCV的基础用法解析

图像处理入门:OpenCV的基础用法解析 引言OpenCV的初步了解深入理解OpenCV:计算机视觉的开源解决方案什么是OpenCV?OpenCV的主要功能1. 图像处理2. 图像分析3. 结构分析和形状描述4. 动态分析5. 三维重建6. 机器学习7. 目标检测 OpenCV的应用场…

嵌入式中轻松识别STM32单片机是否跑飞方法

单片机项目偶尔经常出现异常,不知道是程序跑飞了,还是进入某个死循环了? 因为发生概率比较低,也没有规律,所以没办法在线调试查找问题。 结合这个问题,给大家分享一下用ST-LINK Utility识别单片机程序是否…

Linux版Black Basta勒索病毒针对VMware ESXi服务器

前言 Black Basta勒索病毒是一款2022年新型的勒索病毒,最早于2022年4月被首次曝光,主要针对Windows系统进行攻击,虽然这款新型的勒索病毒黑客组织仅仅才出来短短两个多月的时间,就已经在其暗网平台上已经公布了几十个受害者之多&…

编译原理实验1——词法分析(python实现)

文章目录 实验目的实现定义单词对应的种别码定义输出形式:三元式python代码实现运行结果检错处理 总结 实验目的 输入一个C语言代码串,输出单词流,识别对象包含关键字、标识符、整型浮点型字符串型常数、科学计数法、操作符和标点、注释等等。…

[计算机提升] 还原系统:系统映像

6.4 还原系统:系统映像 1、打开系统设置,进入到恢复页面,然后点击高级启动中的立即重新启动进入到高级启动页面。 2、点击疑难解答 3、点击高级选项 4、点选查看更多恢复选项到下一步系统映像修复: 5、点选系统映像恢复 …

Poller描述符监控类实现(模块四)

目录 类功能 类设计 类实现 编译 类功能 类设计 //Poller描述符监控类 #define MAX_EPOLLEVENTS class Poller{private:int _epfd;struct epoll_event _evs[MAX_EPOLLEVENTS];std::unordered_map<int, Channel *> _channels;private:// 对epoll的直接操作void Updat…

探索C语言中的联合体与枚举:数据多面手的完美组合!

​ ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 联合体的定义 联合体又叫共用体&#xff0c;它是一种特殊的数据类型&…

Qt信号和槽机制(什么是信号和槽,connect函数的形式,按钮的常用信号,QWidget的常用槽,自定义槽函数案例 点击按钮,输出文本)

一.什么是信号和槽 信号槽式Qt中的一个很重要的机制。信号槽实际上是观察者模式,当发生了感兴趣的事件&#xff0c;某一个操作就会被自动触发。当某个事件发生之后&#xff0c;比如按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号。这种发出类似广播。如果有对象对…

Mysql为什么使用B+Tree作为索引结构

B树和B树 一般来说&#xff0c;数据库的存储引擎都是采用B树或者B树来实现索引的存储。首先来看B树&#xff0c;如图所示&#xff1a; B树是一种多路平衡树&#xff0c;用这种存储结构来存储大量数据&#xff0c;它的整个高度会相比二叉树来说&#xff0c;会矮很多。 而对于数…

Elasticsearch: 非结构化的数据搜索

很多大数据组件在快速原型时期都是Java实现&#xff0c;后来因为GC不可控、内存或者向量化等等各种各样的问题换到了C&#xff0c;比如zookeeper->nuraft(https://www.yuque.com/treblez/qksu6c/hu1fuu71hgwanq8o?singleDoc# 《olap/clickhouse keeper 一致性协调服务》)&a…

航芯ACM32G103开发板评测 08 ADC Timer外设测试

航芯ACM32G103开发板评测 08 ADC Timer外设测试 1. 软硬件平台 ACM32G103 Board开发板MDK-ARM Keil 2. 定时器Timer 在一般的MCU芯片中&#xff0c;定时器这个外设资源是非常重要的&#xff0c;一般可以分为SysTick定时器&#xff08;系统滴答定时器&#xff09;、常规定时…

XGBOOST算法Python实现(保姆级)

摘要 XGBoost算法&#xff08;eXtreme Gradient Boosting&#xff09;在目前的Kaggle、数学建模和大数据应用等竞赛中非常流行。本文将会从XGBOOST算法原理、Python实现、敏感性分析和实际应用进行详细说明。 目录 0 绪论 一、材料准备 二、算法原理 三、算法Python实现 3…

创建本地yum源并安装tree命令(openEuler-20.03-LTS-SP3)

步骤 1&#xff1a;下载ISO镜像 首先&#xff0c;您需要从提供的URL下载ISO镜像文件&#xff1a; cd /opt wget https://mirrors.dotsrc.org/openeuler/openEuler-20.03-LTS-SP3/ISO/x86_64/openEuler-20.03-LTS-SP3-x86_64-dvd.iso步骤 2&#xff1a;挂载ISO镜像 接下来&am…

备战蓝桥杯---动态规划(理论基础)

目录 动态规划的概念&#xff1a; 解决多阶段决策过程最优化的一种方法 阶段&#xff1a; 状态&#xff1a; 决策&#xff1a; 策略&#xff1a; 状态转移方程&#xff1a; 适用的基本条件 1.具有相同的子问题 2.满足最优子结构 3.满足无后效性 动态规划的实现方式…

使用QT编写一个简单QQ登录界面

widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//设置窗口标题this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(…

k8s学习-Kubernetes的包管理器Helm

1.1 为何需要Helm Kubernetes能够很好地组织和编排容器&#xff0c;但它缺少⼀个更高层次的应用打包工具&#xff0c;而Helm就是来干这件事的。 先来看个例子。 比如对于⼀个MySQL服务&#xff0c;Kubernetes需要部署下面这些对象&#xff1a; &#xff08;1&#xff09;Serv…

three.js 箭头ArrowHelper的实践应用

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div></div></el-main></…

Golang数据库编程详解 | 深入浅出Go语言原生数据库编程

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie。 Golang学习专栏&#xff1a;https://blog.csdn.net/qq_35716689/category_12575301.html 前言 对数据库…

STM32F1 - 上电启动过程

BOOT 1> 内存映射2> 启动模式 1> 内存映射 Flash起始地址是 【0x0800 0000】 SRAM起始地址是【0x2000 0000】 2> 启动模式 STM32F103的BOOT1和BOOT0引脚&#xff0c; 决定哪块存储区&#xff0c;映射到4G内存空间【0x0000 0000】地址处。 例如 BOOT0引脚接地后&…