✅ Qt流式布局

Qt流式布局

前段时间,曾经对某个软件的一个“流式布局”有点感兴趣,什么叫“流式布局”呢?请看下图:

简而言之,流式布局就是布局应能够根据界面尺寸的变化自动调整其内部控件的位置。然而,Qt 提供的标准布局(如 QBoxLayout、QGridLayout 和 QFormLayout)并不能直接实现这一功能。因此,我们需要自行设计一个布局来达到目的。

🛠️ 创建自定义布局

在官方提供的布局管理文档,有这样一段描述:

理论上,通过继承QLayout,实现特定的函数,我们可以实现任何我们想要的布局效果。Qt官方提供了一个流式布局的例子(为什么不加到官方的控件里🤔),并对各个实现做了详细的解释。下面我们简单来分析分析这些函数的作用。具体的实现效果,我将在后续为大家展示。

🕵️‍♂️ 函数分析

  1. addItem/itemAt/takeItem

这组方法负责管理布局中的项。addItem用于向布局中添加一个新的项,itemAt用于检索指定索引处的项,而takeItem则用于从布局中移除一个特定的项。由于QLayout并不继承自QObject,因此它没有使用父对象机制来管理其子项,而是通过这组方法自行管理布局内的项目。

  1. horizontalSpacing/verticalSpacing

这两个属性或方法用于控制布局内各个元素之间的水平和垂直间距。

  1. hasHeightForWidth/heightForWidth

这些方法用于处理当宽度改变时,高度如何相应调整的问题。例如,当QLabel启用了自动换行(word wrap)功能时,它会实现heightForWidth方法,以根据文本换行情况动态调整自身的高度。这种机制对于确保内容在不同尺寸的容器中正确显示非常有用。有关于这部分的源码分析,挖个坑😉,之后我们有机会再一起来学习一下QLabel的换行机制是什么样的。

  1. sizeHint/minimumSizeHint/setGeometry

sizeHintminimumSizeHint分别返回布局推荐的大小和最小大小提示,这对于嵌套布局或界面设计特别重要,可以保证每个组件都能获得适当的空间setGeometry方法则是用于设置布局的具体几何形状,即它将如何根据给定的矩形区域重新定位和调整它内部控件的大小

  1. doLayout

上面那些函数都是Qt提供给我们重载的,而这个函数是Qt提供的例子中,用来实现布局的函数。对这个函数,稍微进行部分布局逻辑分析。

这些方法共同作用,确保了Qt应用程序中的用户界面既灵活又高效,能够适应不同的设备和屏幕尺寸。通过理解和运用这些布局管理,我们能够灵活的根据自己的需求来创建界面。

🔑 doLayout

int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{int left, top, right, bottom;getContentsMargins(&left, &top, &right, &bottom);// [1] 去除边距,获取实际有效的区域QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);int x = effectiveRect.x();int y = effectiveRect.y();int lineHeight = 0;// [2] 遍历布局中所有itemfor (QLayoutItem *item : std::as_const(itemList)) {const QWidget *wid = item->widget();int spaceX = horizontalSpacing();if (spaceX == -1)spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);int spaceY = verticalSpacing();if (spaceY == -1)spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);// [3] 计算下一个控件的x坐标: 当前x + 当前item的推荐宽度 + 布局的横向间距int nextX = x + item->sizeHint().width() + spaceX;// [4] 下一个控件需要的宽度超出了界面的宽度,//     将y坐标整体往下移动,移动的大小为当前行的控件的最大高度if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {x = effectiveRect.x();y = y + lineHeight + spaceY;nextX = x + item->sizeHint().width() + spaceX;lineHeight = 0;}if (!testOnly)item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));x = nextX;// [5] 计算布局当前行的高度lineHeight = qMax(lineHeight, item->sizeHint().height());}return y + lineHeight - rect.y() + bottom;
}

该函数的核心在于:

  1. 根据当前区域(Geometry)及每个控件所需占用的宽度,来计算每个控件的 x、y 坐标当 x 坐标超出当前区域时,通过向下扩展 y 坐标来确保控件位置的合理

  1. 根据控件的sizeHint来设置控件的大小(所以控件的sizeHint很重要🤓

⚙️ 工程实战

最终,实现了下面的效果,就直接贴代码了:

✅ 布局管理

int ParamComboLayout::doLayout(const QRect & rect, bool testOnly) const
{int left, top, right, bottom;getContentsMargins(&left, &top, &right, &bottom);QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);int x = effectiveRect.x();int y = effectiveRect.y();int lineHeight = LineHeight;m_bIsForm = effectiveRect.width() <= 350;QList<QRect> itemsRect;if (m_bIsForm) {int width = effectiveRect.width() - CheckBoxWidth - m_hSpace;switch (m_eType){case ParamComboLayout::String:case ParamComboLayout::Datetime:{int px = x + CheckBoxWidth + m_hSpace;itemsRect.append(QRect(px, y, width, lineHeight));y += (LineHeight + m_vSpace);itemsRect.append(QRect(px, y, width, lineHeight));break;}case ParamComboLayout::Enum:{itemsRect.append(QRect(x, CheckYStartPos, CheckBoxWidth, CheckBoxHeight));x += CheckBoxWidth + m_hSpace;itemsRect.append(QRect(x, y, width, lineHeight));y += (LineHeight + m_vSpace);itemsRect.append(QRect(x, y, width, lineHeight));if (!m_bIsCombineEnable)break;y += (LineHeight + m_vSpace);itemsRect.append(QRect(x, y, width, lineHeight));break;}case ParamComboLayout::Number:{itemsRect.append(QRect(x, CheckYStartPos, CheckBoxWidth, CheckBoxHeight));x += CheckBoxWidth + m_hSpace;itemsRect.append(QRect(x, y, width, lineHeight));y += (LineHeight + m_vSpace);itemsRect.append(QRect(x, y, width, lineHeight));if (!m_bIsCombineEnable)break;y += (LineHeight + m_vSpace);itemsRect.append(QRect(x, y, width, lineHeight));y += (LineHeight + m_vSpace);itemsRect.append(QRect(x, y, width, lineHeight));break;}default:break;}}if (!testOnly) {for (int i = 0; i < itemsRect.size(); ++i) {itemList.at(i)->setGeometry(itemsRect.at(i));}}return y + lineHeight - rect.y() + bottom;
}

✅ 布局宽高自适应

在调整界面宽度时,布局中的控件位置会相应地发生变化。如果界面宽度减小到当前高度无法容纳所有内部控件,可以通过增加界面高度,将控件向下布局。那怎样能够让布局自动处理这种问题呢?

答案是通过heightForWidth函数来解决问题

// ParamComboLayout是继承自FlowLayoutbool FlowLayout::hasHeightForWidth() const
{return true;
}int FlowLayout::heightForWidth(int width) const
{int height = doLayout(QRect(0, 0, width, 0), true);return height;
}

📚 参考

  1. Flow Layout Example | Qt Widgets 5.15.17
  2. Layout Management | Qt Widgets 5.15.17
  3. Trading Height for Width

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么我们需要DDD?DDD领域驱动设计入门及案例讲解(图文并茂+浅显易懂)

DDD领域驱动设计入门目录 Q1&#xff1a;为什么需要DDD领域驱动设计模型&#xff1f;Q2&#xff1a;DDD领域驱动设计模型怎么用&#xff1f;设计领域模型的一般步骤一、战略建模&#xff08;从高处俯瞰业务 - 微服务的宏观规划&#xff09;1. 领域划分2. 界限上下文定义3. 统一…

vue--制作购物车

&#x1f914;如何制作出下列效果呢&#xff1f;&#x1f447; &#x1f636;‍&#x1f32b;️首先: 设置css样式&#xff1a; <style>body {font-family: Arial, sans-serif;}.cart-item {width: 50%;margin-bottom: 15px;padding: 10px;border: 2px solid gray;borde…

MFC 对话框中显示CScrollView实例

有时候我们需要在对话框中显示CScrollView效果的控件&#xff0c;类似于以下效果&#xff1a; 使用实例可参考&#xff1a;MFC对话框显示CScrollView例子_哔哩哔哩_bilibili 创建CScrollView中显示的子对话框与子类&#xff1a; 两个对话框对应的类&#xff1a; CScrollView继…

高危,Laravel参数注入漏洞安全风险通告

今日&#xff0c;亚信安全CERT监控到安全社区研究人员发布安全通告&#xff0c;披露了Laravel 参数注入漏洞(CVE-2024-52301)。在受影响的版本中&#xff0c;Application.php 文件的 detectEnvironment 函数直接使用了 $_SERVER[argv]&#xff0c;但没有检查运行环境是否为 CLI…

【unity小技巧】unity常用的编辑器扩展

文章目录 1. **自定义 Inspector&#xff08;Inspector 面板&#xff09;**示例&#xff1a; 2. **Editor Window&#xff08;自定义编辑器窗口&#xff09;**示例&#xff1a; 3. **PropertyDrawer&#xff08;自定义属性绘制&#xff09;**示例&#xff1a; 4. **菜单项&…

数字化工厂 MES试点方案全解析(二)

生产过程监控与数据采集 在生产线上部署各类传感器、数据采集终端等设备&#xff0c;与 MES 系统相连&#xff0c;实时采集生产数据&#xff0c;如设备运行参数&#xff08;温度、压力、转速等&#xff09;、产品加工数据&#xff08;尺寸、重量、加工时间等&#xff09;、物料…

5G CPE与4G CPE的主要区别有哪些

什么是CPE&#xff1f; CPE是Customer Premise Equipment&#xff08;客户前置设备&#xff09;的缩写&#xff0c;也可称为Customer-side Equipment、End-user Equipment或On-premises Equipment。CPE通常指的是位于用户或客户处的网络设备或终端设备&#xff0c;用于连接用户…

【海思Hi3519DV500】双目网络相机套板硬件规划方案

Hi3519DV500双目网络相机套板是针对该芯片设计的一款 IP 编码板 PCBA&#xff0c;硬件接口支持双目sensor 接入&#xff0c;SDIO3.0 接口、USB2.0、USB3.0、UART 接口以及丰富的 IO 扩展应用&#xff0c;可根据各种使用场景设计相应扩展板&#xff0c;丰富外围接口&#xff0c;…

已阻止加载“http://localhost:8086/xxx.js”的模块,它使用了不允许的 MIME 类型 (“text/plain”)。

记录今天解决的一个小bug 在终端启动8080端口号监听后&#xff0c;打开网址http://localhost:8080&#xff0c;发现不能正确加载页面&#xff0c;打开检查-控制台&#xff0c;出现如下警告&#xff1a;已阻止加载“http://localhost:8086/xxx.js”的模块&#xff0c;它使用了不…

unity3d——基础篇小项目(开始界面)

示例代码&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine;public class BeginPanel : BasePanel<BeginPanel> {public UIButton btnBegin;public UIButton btnRank;public UIButton btnSetting;public UIButton btnQuit; …

时序预测 | Matlab实现PSO-Elman粒子群优化递归神经网络时间序列预测

时序预测 | Matlab实现PSO-Elman粒子群优化递归神经网络时间序列预测 目录 时序预测 | Matlab实现PSO-Elman粒子群优化递归神经网络时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现PSO-Elman粒子群优化递归神经网络时间序列预测&#xff08;完整源…

Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书

目录 一、CA机构 二、Lets Encrypt特点 三、申请SSL 一、CA机构 ‌Lets Encrypt‌是一个由非营利组织Internet Security Research Group (ISRG)运营的证书颁发机构&#xff08;CA&#xff09;&#xff0c;旨在通过自动化和开放的方式为全球网站提供免费、可靠的SSL/TLS证书。…

丹摩征文活动|基于丹摩算力Llama3.1实现私有化部署的Chatbot

一、简介 Llama 3.1 模型介绍 Llama 3.1 是一种大型的、开源的语言生成模型&#xff0c;类似于OpenAI的GPT-3&#xff0c;但它是由Meta&#xff08;前Facebook&#xff09;推出的。Llama 3.1具备强大的自然语言处理能力&#xff0c;可以执行各种语言任务&#xff0c;如对话生…

ssm168基于jsp的实验室考勤管理系统网页的设计与实现+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;实验室考勤管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本实验室考勤管…

(动画)Qt控件 QLCDNumer

文章目录 LCD Number1. 介绍2. 核心属性3 . 代码实现:倒计时1. 在界⾯上创建⼀个 QLCDNumber , 初始值设为 10.2. 修改 widget.h 代码, 创建⼀个 QTimer 成员, 和⼀个 updateTime 函数3. 修改 widget.cpp, 在构造函数中初始化 QTimer4. 修改 widget.cpp, 实现 updateTime 4. 动…

【线程】Java多线程编程

【线程】Java多线程编程 一、前言一个最简单的多线程编程示例可以使用的工具 二、创建线程的方式三、Thread类中重要的属性和方法3.1 构造方法3.2 常见属性 一、前言 当有多个线程的时候&#xff0c;这些线程的执行顺序是不确定的。这一点&#xff0c;是我们之前提到的操作系统…

Java技术复习提升 11 常用类

第11章 常用类 1 包装类 不同包装类都继承自Object类 Serialiazble接口表示该类表示序列化 Comparable接口用于定义自然顺序 包装类和基本数据的转换 jdk5之前手动装箱拆箱 jdk5之后自动装箱拆箱 自动装箱底层调用的是valueof方法 拆箱仍然是intvalue方法 public class Inte…

P1 练习卷(C++4道题)

1.纷繁世界 内存限制&#xff1a;256MB 时间限制&#xff1a;1s 问题描述 这是一个纷繁复杂的世界。 某一天清晨你起床很迟&#xff0c;没有吃上早饭。于是你骑着自行车去超市&#xff0c;但是你又发现商店的工作人员已经重新贴上了价格标签&#xff0c;零食价格都涨了50%。你…

Selenium的八种定位方式

1. 通过 ID 定位 ID 是最直接和高效的方式来定位元素&#xff0c;因为每个页面中的 ID 应该是唯一的。 from selenium import webdriverdriver webdriver.Chrome(executable_pathpath/to/chromedriver) driver.get(https://example.com)# 通过 ID 定位 element driver.find…

logback动态获取nacos配置

文章目录 前言一、整体思路二、使用bootstrap.yml三、增加环境变量四、pom文件五、logback-spring.xml更改总结 前言 主要是logback动态获取nacos的配置信息,结尾完整代码 项目springcloudnacosplumelog&#xff0c;使用的时候、特别是部署的时候&#xff0c;需要改环境&#…