✅ 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,一经查实,立即删除!

相关文章

RabbitMQ 单机与集群部署教程

目录 RabbitMQ 单机与集群部署教程第一部分:RabbitMQ 概述第二部分:RabbitMQ 单机部署教程1. 安装 RabbitMQ1.1 安装依赖项1.2 安装 RabbitMQ1.3 验证安装2. 配置 RabbitMQ2.1 配置环境变量2.2 启用 Web 管理插件2.3 创建用户与虚拟主机3. 单机案例代码实现(Python)4. 常见…

直接调用本地API(NTAPI)

文章目录 Windows操作系统中的功能调用流程直接调用NTAPI的重要性 Windows操作系统中的功能调用流程 在Windows操作系统中&#xff0c;应用程序与操作系统内核之间的交互是通过一系列精心设计的函数调用流程来实现的。让我们以一个常见的操作——创建文件为例&#xff0c;来详…

为什么我们需要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继…

数仓工具—Hive语法之窗口函数中的 case when

窗口函数中的 case when 今天我们看一下窗口函数和case when 的各种花活,最近的需求各种窗口,一个需求中十几个窗口,加上各种条件边界,所以写了大量的窗口函数和case when的组合,今天我们来看一下。 我们的数据如下 %spark.pyspark df2 = spark.createDataFrame([(&quo…

高危,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;、物料…

Flink整合Hudi及使用

1、jar包上传 上传jar包即可完成整合 # 1、将hudi-flink1.15-bundle-0.15.0.jar包上传到flink的lib目录下 /usr/local/soft/flink-1.15.3/lib-- 如果没有启动集群&#xff0c;直接忽略 # 2、重启flink的集群 yarn application -list yarn application -kill application_170…

Flink 常用问题及常用配置(有用)

一、Flink 常用问题及常用配置 参数 示例 说明 execution.checkpointing.interval 3min Checkpoint 触发间隔 state.backend rocksdb / filesystem 用于设置statebackend类型, 默认会以内存为statebackend(无法支持大状态) taskmanager.memory.jvm-overhead.max 204…

vue3(十九)-基础入门之vue-nuxt反向代理

一、反向代理 1、下载 nuxtjs/proxy 使用 npm npm install nuxtjs/proxy 或使用 yarn yarn add nuxtjs/proxy 2、配置 nuxt.config.js 文件 export default {modules: [nuxtjs/axios,nuxtjs/proxy],axios: {baseURL: /,proxy: true},proxy: {/api/: {target: https://i.maoya…

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

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

【数据结构-队列】力扣232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返回队列开头…

推荐一个QDirStat基于 Qt 的目录统计工具

QDirStat 是一款基于 Qt 的目录统计工具&#xff0c;旨在帮助用户分析磁盘空间使用情况并进行清理。QDirStat的一些主要特点和功能&#xff1a; 跨平台兼容性&#xff1a;QDirStat 支持 Linux、BSD、Unix-like 系统以及 macOS&#xff0c;确保了广泛的用户基础。 直观的数据展…

C语言数学函数库<math.h>的常用函数讲解

math函数 一. 基础数学函数1. fabs(double x) — 绝对值2. fmod(double x, double y) — 余数3. pow(double x, double y) — 幂运算4. sqrt(double x) — 平方根5. cbrt(double x) — 立方根6. hypot(double x, double y) — 计算斜边 二. 对数和指数函数1. exp(double x) — …

【海思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; …

【AI日记】24.11.19 GraphRAG

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 核心工作 1 内容&#xff1a;修改简历和投简历时间&#xff1a;1 小时结果&#xff1a;都失败了&#xff0c;不过我不会气馁&#xff0c;我会越挫越勇 核心工作 2 内容&#xff1a;学习微软的开源项目 Grap…