Qt 的流式布局 FlowLayout

一直苦寻于一个比较智能的布局方式,能够满足软件界面进行resize的时候,对已经存在的布局进行重新布局。能够合理的判断界面的size,在界面放大的时候,显示的item的行数减少,相反,界面缩小的时候,显示的 item 行数能相应的增加,也就是能够满足界面是充盈的并且不会有超出界面的显示 item。

而我们已知的在Qt中已有的几种布局方式,都没办法直接达到我们的预期。比较接近的是栅格布局,但也要进行二次开发才能满足需求。

1、 利用栅格布局和 QScrollArea 实现类似的流式布局

这种方式比较适合每个 item size 相同的情况,实现的原理也就是在调用 resizeEvent() 函数的以后,对界面已经存在的 item 先全部移除,再重新添加。

测试的时候发现,如果每个 item 的 size 不做限制,可能不同列的 item 宽度会存在不一样的情况,但至少每列的 item 均是一样大小。

如下,首先我们 new 一个 QGridLayout , 当然这种方式可以通过在 Qt designer 直接拖的方式创建,我这样做是因为我是直接在 Qt FlowLayout 的例子中做了测试,方便后面切换成 FlowLayout 。

QGridLayout* layout = new QGridLayout;
ui->scrollAreaWidgetContents->setLayout(layout);
QStringList list{"Short", "Longer", "Different text", "More text", "Even longer button text" };for(auto& t : list)
{auto btn = new QPushButton(t);layout->addWidget(btn);m_list.push_back(btn);
}

接下来就是如何在调用 resizeEvent 函数的时候对界面上的 item 重新布局。

首先就是计算出当前的界面最多能放几列。我下面直接用了一个固定的值,实际使用的时候,如果所有的 item 是固定大小的,则直接使用固定大小的宽度即可。

接下来就是先将界面上所有的 item 移除,然后再根据重新计算的数值将所有的 item 再次添加,为了方便,我用了一个 QPoint 类型的变量来记录当前是栅格布局的某个栅格。

void Window::resizeEvent(QResizeEvent *event)
{int column = event->size().width() / 100;auto layout = ui->scrollAreaWidgetContents->layout();for (int index = 0, size = layout ->count(); index < size ; ++index){auto btn = layout ->itemAt(index)->widget();layout ->removeWidget(btn);}QPoint pt(0, 0);for (auto& btn : m_list){if(pt.y() == column){pt.setX(pt.x() + 1);pt.setY(0);}static_cast<QGridLayout*>(layout)->addWidget(btn, pt.x(), pt.y());pt.setY(pt.y() + 1);}
}

这种方法在布局 item 是等大的时候比较有效。

2、使用 Qt 给的例子 Flowlayout

Qt 提供了一个 FlowLayout 的例子,但是并没有将其收录进模块,而是以源码的形式直接给出了可运行测试的例子程序。该类继承自 QLayout。

class FlowLayout : public QLayout
{
public:explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);explicit FlowLayout(int left, int top, int right, int bottom, int hSpacing = -1, int vSpacing = -1);explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);~FlowLayout();void addItem(QLayoutItem *item) override;void setSpacing(int hSpacing = -1, int vSpacing = -1);int horizontalSpacing() const;int verticalSpacing() const;Qt::Orientations expandingDirections() const override;bool hasHeightForWidth() const override;int heightForWidth(int) const override;int count() const override;QLayoutItem *itemAt(int index) const override;QSize minimumSize() const override;void setGeometry(const QRect &rect) override;QSize sizeHint() const override;QLayoutItem *takeAt(int index) override;private:int doLayout(const QRect &rect, bool testOnly) const;int smartSpacing(QStyle::PixelMetric pm) const;
};

通过重载的方式,实现部分成员函数,最后再通过调用 doLayout(const QRect &rect, bool testOnly) 成员函数实现布局显示。

因此,该类的主要功能集中在该函数中。

int FlowLayout::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 = 0;QLayoutItem *item = nullptr;foreach(item, m_itemList) {QSize sizeItemHint(item->sizeHint());if (item == m_itemList.last()){sizeItemHint = QSize(qMax(m_minimumSize.width(), sizeItemHint.width()), qMax(m_minimumSize.height(), sizeItemHint.height()));}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);}int nextX = x + sizeItemHint.width() + spaceX;if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {x = effectiveRect.x();y = y + lineHeight + spaceY;nextX = x + sizeItemHint.width() + spaceX;lineHeight = 0;}if (!testOnly){item->setGeometry(QRect(QPoint(x, y), sizeItemHint));}x = nextX;lineHeight = qMax(lineHeight, sizeItemHint.height());}return y + lineHeight - rect.y() + bottom;
}

函数功能,主要是:通过遍历item list, 计算每个 item 绘制的坐标,如果矩形的剩余长度不够 item widget 的长度及两个控件中间的间距,则将 item widget 的 x 坐标设为矩形的起始,也就是另起一行。另起一行之后则需要考虑 item widget 所处的 y 坐标,最后通过取当前 item 的高度和 lineHeight 的较大值, lineHeight = qMax(lineHeight, sizeItemHint.height()); 得到每行的固定高度。

使用也是比较简单。

1、可以通过定义成员变量的方式

这样通过定义成员变量的方式,会方便后续对布局内的 widget 进行操作,比如如下:

m_layout = new FlowLayout(this);
m_layout->setMargin(0);
m_layout->setSpacing(10, 10);
ui->wdgCard->setLayout(m_layout);

使用时:

auto ptr = new XXXX(this);
m_layout->addWidget(ptr);...for(int index = 0, size = m_layout->size(); index < size; ++index)
{auto ptr = static_cast<XXXX*>(m_layout->item(index)->widget());...
}

2、使用 Qt 给的例子 Flowlayout

auto layout = new FlowLayout(this);
layout->setMargin(0);
layout->setSpacing(10, 10);
ui->wdgCard->setLayout(layout);

使用时:

auto ptr = new XXXX(this);
ui->wdgCard->layout()->addWidget(ptr);
...
while (ui->wdgCard->layout()->count())
{auto ptr = ui->wdgCard->layout()->itemAt(0)->widget();ui->wdgCard->layout()->removeWidget(wdg);wdg->deleteLater();
}

FlowLayout源码

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

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

相关文章

软件测试|测试平台开发-Flask 入门:编写第一个简单 Web 应用

简介 Flask 是一个轻量级的 Python Web 框架&#xff0c;它使得创建 Web 应用变得简单快捷。相比于Django框架&#xff0c;它具有以下的优点&#xff1a; 轻&#xff1a;Flask 是一个轻量级的 Web 框架&#xff0c;使用 Python 语言编写易&#xff1a;较其他同类型框架更为灵…

Golang-strconv库学习笔记

前言&#xff1a; strconv库是go官方提供的一个标准包&#xff0c;主要用于字符串相关的处理。通过参考官方文档、中文文档和其他工具&#xff0c;进行学习记录。学习重点是其中的内置方法。 本文分为Atoi&#xff0c;Format系列&#xff0c;Parse系列&#xff0c;Append系列,…

obs推流失败问题解决

点击开始直播&#xff0c;出现上图的问题 【解决办法】需要升级显卡驱动 使用360驱动大师升级 点击升级 他会先备份再升级 安装完成需要重启

【LMM 014】NExT-GPT:能够输入和生成任意模态的多模态大模型

论文标题&#xff1a;NExT-GPT:Any-to-Any Multimodal Large Language Model 论文作者&#xff1a;Shengqiong Wu, Hao Fei*, Leigang Qu, Wei Ji, Tat-Seng Chua 作者单位&#xff1a; NExT Lab, National University of Singapore 论文原文&#xff1a;https://arxiv.org/abs…

Veeam Backup Replication介绍

新钛云服已累计为您分享781篇技术干货 veeam 提供了一种全面、灵活和高效的数据保护解决方案&#xff0c;可以使用 VeeamBackup & Replication 执行以下数据保护和灾难恢复任务&#xff1a; 创建 VM 和物理机备份 还原物理机、VM、 磁盘和文件 验证备份并准备恢复 创建…

Hive SQL / SQL

1. 建表 & 拉取表2. 插入数据 insert select3. 查询3.1 查询语句语法/顺序3.2 关系操作符3.3 聚合函数3.4 where3.5 分组聚合3.6 having 筛选分组后结果3.7 显式类型转换 & select产生指定值的列 4. join 横向拼接4.1 等值连接 & 不等值连接4.2 两表连接4.2.1 内连…

synchronized、volatile关键字

Java中的synchronized关键字 synchronized关键字介绍 synchronized块是Java提供的一种原子性内置锁&#xff0c;Java中的每个对象都可以把它当作一个同步锁来使用&#xff0c;这些Java内置的使用者看不到的锁被称为内部锁&#xff0c;也叫作监视器锁。 线程的执行代码在进入…

Linux之Ubuntu环境Jenkins部署前端项目

今天分享Ubuntu环境Jenkins部署前端vue项目 一、插件安装 1、前端项目依赖nodejs&#xff0c;需要安装相关插件 点击插件管理&#xff0c;输入node模糊查询 选择NodeJS安装 安装成功 2、配置nodejs 点击后进入 点击新增 NodeJS 配置脚手架类型&#xff1a;如果不填 默认npm …

XAgent调研

文章目录 1 简介2 快速测试 Quick Start3 结构分析 1 简介 XAgent&#xff08;链接&#xff09;是一个开源的&#xff0c;基于大语言模型的agent构建框架&#xff1b;其目标是构建出能够辅助人类处理各类任务的自动助手 定位&#xff1a;一个全能的&#xff0c;自动的辅助agen…

Transformer - Attention is all you need 论文阅读

虽然是跑路来NLP&#xff0c;但是还是立flag说要做个project&#xff0c;结果kaggle上的入门project给的例子用的是BERT&#xff0c;还提到这一方法属于transformer&#xff0c;所以大概率读完这一篇之后&#xff0c;会再看BERT的论文这个样子。 在李宏毅的NLP课程中多次提到了…

【LeetCode】1341. 电影评分

表&#xff1a;Movies ------------------------ | Column Name | Type | ------------------------ | movie_id | int | | title | varchar | ------------------------ movie_id 是这个表的主键(具有唯一值的列)。 title 是电影的名字。表&#xff1a…

深入浅出XTTS:Oracle数据库迁移升级利器

演讲大纲&#xff1a; 1. 什么是XTTS 2. 适用场景 3. XTTS的基本操作步骤 4. XTTS案例分享 今天主要跟大家分享一下XTTS,在网上曾看过相关讨论,但发现按网上讲的那些去实际操作的话,还是会遇到一些坑,并不能实际落下来,所以今天想跟大家分享一些实战干货. 一、什么是XTTS …

LeetCode 29. 两数相除

两数相除 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff0c;8.345 将被截断为 8 &#xff0c;-…

linux高级管理——Squid代理

一、squid服务基础&#xff1a; 1.1缓存代理的概述&#xff1a; 代理的工作机制 当客户机通过代理来请求Web页面时&#xff0e;指定的代理服务器会先检查自己的缓存&#xff0c;如果缓存中已经有客户机需要的页面&#xff0c;则直接将缓存中的页面内容反馈给客户机:如果缓存中…

web学习笔记(十一)

目录 1.数据类型 1.1数据类型分类 &#xff08;1&#xff09;简单&#xff08;基本&#xff09;数据类型 &#xff08;2&#xff09;复杂&#xff08;特殊&#xff09;数据类型 1.2判断数据类型的方法 &#xff08;1&#xff09;常规判断方法&#xff1a; &#xff08;2…

x-cmd pkg | trdsql - 能对 CSV、LTSV、JSON 和 TBLN 执行 SQL 查询的工具

目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 trdsql 是一个使用 sql 作为 DSL 的强大工具: 采用 SQL 对 CSV、LTSV、JSON 和 TBLN 文件执行查询与 MySQL&#xff0c;Postgresql&#xff0c;Sqlite 的 Driver 协同&#xff0c;可以实现对应数据库的表与文件的 JO…

Kafka_02_Producer详解

Kafka_02_Producer详解 ProducerProducerRecordSend&Close实现原理ProducerInterceptorSerializerPartitioner 事务 Producer Producer(生产者): 生产并发送消息到Broker(推送) Producer是多线程安全的(建议通过池化以提高性能)Producer实例后可发送多条消息(可对应多个P…

2024--Django平台开发-Django知识点(四)

1.知识回顾 创建项目&#xff1a;新项目、别人项目、新版版、老版本 项目目录&#xff08;v1.0版本&#xff09; 路由系统 常见路由编写加粗样式 /index/ 函数 /index/<str:v1> 函数 re_path(ryy/(\d{4})-(\d{2})-(\d{2})/, views.yy), re_path(ryy/(?…

科研上新 | 第4期:语言-音乐对比预训练;查找表实现的神经网络推理;大模型时代重新定义搜索框架

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

什么是ajax,为什么使用ajax!

前言&#xff1a; 要学习一门新的、技术之前&#xff0c;首先我们要了解一下他是什么&#xff0c;为什么使用&#xff0c;有什么好处&#xff0c;该怎么理解。现在就从下文开始了解吧 什么是ajax: Ajax即“Asynchronous Javascript And XML”(异步JavaScript 和XML)&#xff0…