Qt-布局管理

2024-09-06 21:36
文章标签 布局 qt 管理

本文主要是介绍Qt-布局管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

布局管理

Qt布局系统提供了一种简单而强大的方式,可以自动在窗口组件中排列子窗口组件,以确保它们充分利用可用空间。

介绍

Qt包含了一组布局管理类,用于描述窗口组件在应用程序用户界面中的布局方式。当可用空间发生变化时,这些布局会自动定位和调整窗口组件的大小,确保它们的排列一致,并且用户界面作为一个整体保持可用。

所有QWidget子类都可以使用布局来管理它们的子组件。函数QWidget::setLayout()为部件应用布局。当以这种方式在部件上设置布局时,它将负责以下任务:

  • 子部件的定位
  • 合理的窗口默认大小
  • 合理的窗口最小尺寸
  • 调整处理
  • 内容变更时自动更新:
    • 子部件的字体大小、文本或其他内容
    • 隐藏或显示子部件
    • 移除子部件

Qt的布局类

Qt的布局类是为手写的c++代码设计的,为了简单起见,可以用像素来指定测量值,所以它们很容易理解和使用。使用Qt Designer创建的表单生成的代码也使用了layout类。在尝试设计表单时,Qt Designer非常有用, 因为它避免了用户界面开发中通常涉及的编译、链接和运行循环。

QBoxLayout

水平或垂直排列子部件

QButtonGroup

容器来组织按钮小部件组

QFormLayout

管理输入小部件的表单及其相关标签

QGraphicsAnchor

表示QGraphicsAnchorLayout中两个项目之间的锚点

QGraphicsAnchorLayout

可以在图形视图中将小部件固定在一起的布局

QGridLayout

在网格中布局小部件

QGroupBox

带标题的组框框架

QHBoxLayout

水平排列小部件

QLayout

几何图形管理器的基类

QLayoutItem

QLayout操作的抽象项

QSizePolicy

描述水平和垂直调整大小策略的布局属性

QSpacerItem

布局中的空白空间

QStackedLayout

一次只能看到一个小部件的小部件堆栈

QStackedWidget

一次只能看到一个小部件的小部件堆栈

QVBoxLayout

垂直排列部件

QWidgetItem

表示小部件的布局项

使用布局的技巧

使用布局时,在构造子部件时不需要传递父组件。布局将自动重新设置小部件的父部件(使用QWidget::setParent()),使它们成为安装了布局的小部件的子部件。

注意:布局中的部件是安装布局的部件的子部件,而不是布局本身的子部件。部件只能有其他部件作为父部件,而不能有布局。

你可以在布局上使用addLayout()嵌套布局;然后,内部布局成为它插入的布局的子布局。

向布局中添加部件

向布局中添加部件时,布局过程如下所示:

  1. 所有小部件最初将根据它们的QWidget::sizePolicy()和QWidget::sizeHint()分配一定的空间。
  2. 如果任何小部件设置了拉伸因子,且其值大于零,则按其拉伸因子的比例为它们分配空间(将在下面解释)。
  3. 如果任何部件将拉伸因子设置为0,则只有在没有其他部件需要空间的情况下,它们才会获得更多空间。其中,空间首先分配给具有扩展大小策略的部件。
  4. 任何分配的空间小于其最小大小(如果没有指定最小大小,则为最小大小提示)的部件都会分配其所需的最小大小。(部件不需要有最小尺寸或最小尺寸提示,在这种情况下,拉伸因子是它们的决定因子。)
  5. 任何分配的空间超过其最大尺寸的部件都会分配它们所需的最大尺寸空间。(部件不需要有最大尺寸,在这种情况下,拉伸因子是它们的决定因子。)

延伸的因素

widget通常在创建时没有设置任何拉伸因子。当它们在布局中被布局时,小部件根据它们的QWidget::sizePolicy()或它们的最小大小提示(以较大的为准)获得一份空间份额。拉伸因子用于改变部件之间的空间比例。

如果我们使用没有设置拉伸因子的QHBoxLayout布局三个窗口组件,我们将得到如下布局:

如果我们对每个小部件应用拉伸因子,它们将按比例布局(但绝不会小于它们的最小尺寸提示),例如:

布局中的自定义部件

在创建自己的窗口组件类时,还应该告知它的布局属性。如果组件使用了Qt的布局,这一点已经解决了。如果部件没有任何子部件,或者使用手动布局,则可以使用以下任何或所有机制更改部件的行为:

  • 重新实现QWidget::sizeHint()以返回widget的首选大小。
  • 重新实现QWidget::minimumSizeHint(),以返回widget可以拥有的最小尺寸。
  • 调用QWidget::setSizePolicy()来指定小部件的空间需求。

每当大小提示、最小大小提示或大小策略发生变化时,调用QWidget::updateGeometry()。这将导致布局重新计算。对QWidget::updateGeometry()的多次连续调用只会导致一次布局重新计算。

如果小部件的首选高度取决于它的实际宽度(例如,具有自动断字功能的标签),则在小部件的大小策略中设置height-for-width标志,并重新实现QWidget::heightForWidth()。

即使您实现了QWidget::heightForWidth(),提供一个合理的sizeHint()仍然是一个好主意。

有关实现这些函数的进一步指导,请参阅Qt季度文章Trading Height For Width。

布局问题

在标签小部件中使用富文本可能会给其父小部件的布局带来一些问题。当标签被换行时,Qt的布局管理器处理富文本的方式会导致问题。

在某些情况下,parent布局被设置为QLayout::FreeResize模式,这意味着它将不能适应其内容的布局以适应小尺寸的窗口,甚至阻止用户使窗口太小而无法使用。这可以通过对有问题的部件进行子类化,并实现适当的sizeHint()和minimumSizeHint()函数来解决。

在某些情况下,当向部件添加布局时,它是相关的。当你设置QDockWidget或QScrollArea

的widget时(使用QDockWidget::setWidget()和QScrollArea::setWidget()), widget上必须已经设置了布局。否则,部件将不可见。

手动布局

如果你正在制作一个独一无二的特殊布局,你也可以像上面描述的那样制作一个自定义部件。重新实现QWidget::resizeEvent()来计算所需的大小分布,并在每个子节点上调用setGeometry()。

当布局需要重新计算时,widget将获得一个类型为QEvent::LayoutRequest的事件。重新实现QWidget::event()来处理QEvent::LayoutRequest事件。

如何编写自定义布局管理器

手动布局的另一种选择是通过继承QLayout来编写自己的布局管理器。Border布局和Flow布局的例子展示了如何做到这一点。

这里我们详细介绍一个例子。CardLayout类的灵感来自于同名的Java布局管理器。它将项目(窗口组件或嵌套布局)置于彼此之上,每个项目通过QLayout::spacing()进行偏移。

要编写自己的布局类,必须定义以下内容:

  • 存储由布局处理的项的数据结构。每一项都是一个QLayoutItem。在这个例子中,我们将使用QVector。
  • addItem(),如何向布局中添加项。
  • setGeometry(),如何执行布局
  • sizeHint(),布局的首选大小。
  • itemAt(),如何遍历布局
  • takeAt():从布局中删除元素的方法。

大多数情况下,还需要实现minimumSize()。

#ifndef CARD_H
#define CARD_H#include <QtWidgets>
#include <QVector>class CardLayout : public QLayout
{
public:CardLayout(int spacing): QLayout(){ setSpacing(spacing); }CardLayout(int spacing, QWidget *parent): QLayout(parent){ setSpacing(spacing); }~CardLayout();void addItem(QLayoutItem *item) override;QSize sizeHint() const override;QSize minimumSize() const override;int count() const override;QLayoutItem *itemAt(int) const override;QLayoutItem *takeAt(int) override;void setGeometry(const QRect &rect) override;private:QVector<QLayoutItem*> m_items;
};
#endif

首先定义count()来获取列表中的项数。

int CardLayout::count() const
{// QVector::size() returns the number of QLayoutItems in m_itemsreturn m_items.size();
}

然后定义两个遍历布局的函数:itemAt()和takeAt()。布局系统内部使用这些函数来处理部件的删除。应用程序程序员也可以使用它们。

itemAt()返回指定索引处的元素takeAt()删除给定索引处的元素,并返回它。在这种情况下,我们使用列表索引作为布局索引。在其他数据结构更复杂的情况下,我们可能需要花费更多的精力来定义元素的线性顺序。

QLayoutItem *CardLayout::itemAt(int idx) const
{// QVector::value() performs index checking, and returns nullptr if we are// outside the valid rangereturn m_items.value(idx);
}QLayoutItem *CardLayout::takeAt(int idx)
{// QVector::take does not do index checkingreturn idx >= 0 && idx < m_items.size() ? m_items.takeAt(idx) : 0;
}

addItem()实现了布局项的默认放置策略。必须实现该函数。它由QLayout::add()使用,由QLayout构造函数使用,该构造函数接受一个布局作为父布局。如果您的布局有需要参数的高级放置选项,则必须提供额外的访问函数,例如QGridLayout::addItem()、QGridLayout::addWidget()和QGridLayout::addLayout()的跨行和跨列重载。

void CardLayout::addItem(QLayoutItem *item)
{m_items.append(item);
}

布局承担了添加项目的责任。由于QLayoutItem不继承QObject,我们必须手动删除这些项。在析构函数中,使用takeAt()从列表中移除每一项,然后将其删除。

CardLayout::~CardLayout()
{QLayoutItem *item;while ((item = takeAt(0)))delete item;
}

setGeometry()函数实际执行布局。作为参数提供的矩形不包括margin()。如果相关,使用spacing()作为项目之间的距离。

void CardLayout::setGeometry(const QRect &r)
{QLayout::setGeometry(r);if (m_items.size() == 0)return;int w = r.width() - (m_items.count() - 1) * spacing();int h = r.height() - (m_items.count() - 1) * spacing();int i = 0;while (i < m_items.size()) {QLayoutItem *o = m_items.at(i);QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h);o->setGeometry(geom);++i;}
}

sizeHint()和minimumSize()在实现上通常非常相似。两个函数返回的大小应该包括spacing(),但不包括margin()。

QSize CardLayout::sizeHint() const
{QSize s(0, 0);int n = m_items.count();if (n > 0)s = QSize(100, 70); //start with a nice default sizeint i = 0;while (i < n) {QLayoutItem *o = m_items.at(i);s = s.expandedTo(o->sizeHint());++i;}return s + n * QSize(spacing(), spacing());
}QSize CardLayout::minimumSize() const
{QSize s(0, 0);int n = m_items.count();int i = 0;while (i < n) {QLayoutItem *o = m_items.at(i);s = s.expandedTo(o->minimumSize());++i;}return s + n * QSize(spacing(), spacing());
}

进一步指出

  • 这个自定义布局不处理宽度对应的高度。
  • 我们忽略QLayoutItem::isEmpty();这意味着布局将把隐藏的部件视为可见的。
  • 对于复杂的布局,缓存计算值可以大大提高速度。在这种情况下,实现QLayoutItem::invalidate()来标记缓存的数据是脏的。
  • 调用QLayoutItem::sizeHint()等方法的开销可能很大。因此,如果以后在同一个函数中还需要它,你应该将它的值存储在一个局部变量中。
  • 你不应该在同一个函数中对同一项调用两次QLayoutItem::setGeometry()。如果项目有多个子部件,则此调用可能非常昂贵,因为布局管理器每次都必须执行完整的布局。相反,计算几何形状,然后设置它。(这不仅适用于布局,例如,如果你实现了自己的resizeEvent(),也应该这样做。)

布局的例子

许多Qt Widgets示例已经使用了布局,但是,存在一些示例来展示各种布局

Address Book Tutorial

介绍GUI编程,展示如何组合一个简单但功能齐全的应用程序。

Border Layout Example

演示如何沿边框排列子部件。

Calculator Example

该示例展示了如何使用信号和槽来实现计算器小部件的功能,以及如何使用QGridLayout在网格中放置子小部件。

Calendar Widget Example

CalendarWidget示例展示了QCalendarWidget的用法。

Echo Plugin Example

这个例子展示了如何创建一个Qt插件。

Flow Layout Example

展示如何为不同的窗口大小排列小部件

Image Composition Example

展示了QPainter中的合成模式是如何工作的。

Menus Example

菜单示例演示了如何在主窗口应用程序中使用菜单。

Simple Tree Model Example

简单树模型示例展示了如何在Qt的标准视图类中使用分层模型。

Sub-Attaq

这个例子展示了Qt结合动画框架和状态机框架来创建游戏的能力。

Layout Management | Qt Widgets 5.15.17

这篇关于Qt-布局管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143183

相关文章

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

IDEA中的Kafka管理神器详解

《IDEA中的Kafka管理神器详解》这款基于IDEA插件实现的Kafka管理工具,能够在本地IDE环境中直接运行,简化了设置流程,为开发者提供了更加紧密集成、高效且直观的Kafka操作体验... 目录免安装:IDEA中的Kafka管理神器!简介安装必要的插件创建 Kafka 连接第一步:创建连接第二步:选

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho