百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

QT进阶之路 : 布局详解

connygpt 2024-08-26 13:29 124 浏览

转载自:http://blog.csdn.net/fanyun_01/article/details/52623777

Qt布局管理手册:

http://doc.qt.io/qt-5/qtwidgets-index.html#styles

http://doc.qt.io/qt-5/qtwidgets-index.html#widgets

http://doc.qt.io/qt-5/qtwidgets-index.html#layouts

以下是Qt手册中的《布局管理》的译文:

在一个Widget中,Qt布局管理系统提供了一个简单而有效的方式来自动组织子widget,以保证他们能够很好地利用可用空间。

介绍:

Qt包含一个布局管理类的集合,它们被用来描述widgets如何在应用程序的用户界面中呈现的。当可用空间发生变化时,这些布局将自动调整widgets的位置和大小,以确保它们布局的一致性和用户界面主体可用。

所有QWidget的子类都可以用布局来管理它们的子类。QWidget::setLayout()函数给widget提供一个布局。当布局通过这种方式设置到widget,它将负责以下任务:

1.子widget的定位

2.窗口的合理默认空间

3.窗口的合理最小空间

4.调整大小处理

5.当内容发生变化时自动调整

6.字体、大小或者内容变化

7.显示或 隐藏widget

8.移除子widget

Qt的布局类:

QGraphicsAnchorLayout

Layout where one can anchor widgets together in Graphics View

在制图视图中布局widget

QGraphicsAnchor

Represents an anchor between two items in a QGraphicsAnchorLayout

表示一个QGraphicsAnchorLayout中两个图元之间的anchor

QBoxLayout

Lines up child widgets horizontally or vertically

水平或垂直整理子widget

QHBoxLayout

Lines up widgets horizontally

水平整理子控件

QVBoxLayout

Lines up widgets vertically

垂直整理子控件

QFormLayout

Manages forms of input widgets and their associated labels

label-inputwidget模式表单布局

QGridLayout

Lays out widgets in a grid

网格布局

QLayout

The base class of geometry managers

布局,几何管理的基类

QLayoutItem

Abstract item that a QLayout manipulates

管理的抽象元素

QSpacerItem

Blank space in a layout

空白区域布局

QWidgetItem

Layout item that represents a widget

布局元素

QSizePolicy

Layout attribute describing horizontal and vertical resizing policy

大小策略

QStackedLayout

Stack of widgets where only one widget is visible at a time

栈模式布局,一次只显示一个

QButtonGroup

Container to organize groups of button widgets

管理按钮的容器

QGroupBox

Group box frame with a title

带标题的组箱框架

QStackedWidget

Stack of widgets where only one widget is visible at a time

栈模式的widget,一次只显示一个

水平、垂直、网格和表格布局:

Qt布局类之间的关系如图1所示:

图1 Qt布局类之间的关系

给widgets一个很好布局的最好方式是使用内置的布局管理器: QHBoxLayout, QVBoxLayout, QGridLayout, and QFormLayout. 这些类都从QLayout继承而来,它们都来源于QObject(而不是QWidget)。创建更加复杂的布局,可以让它们彼此嵌套完成。

QHBoxLayout:水平布局

QVBoxLayout:垂直布局

QGridLayout: 表格布局

QGridLayout::addWidget()语法

layout->addWidget(widget, row, column, rowSpan, columnSpan);

参数widget:为插入到这个布局的子控件;

参数(row,column)为控件占据的左上角单元格位置;

参数rowSpan是控件占据的行数,

参数colunmSpan是控件占据的列的个数。

(rowSpan和colunmSpan默认值为1)

Stacked Layouts:分组布局

QStackedLayout类把子控件进行分组或者分页,一次只显示一组或者一页,隐藏其他组或者页上的控件。

使用这些Qt布局管理类的另一个原因是,在程序、系统改变字体,语言或者在不同的平台上运行时,布局管理器能够自动调整窗体里所有控件的大小和尺寸。

其他可进行布局管理的类:这些类的共同特点是提供了更加灵活的布局管理,在一定程度上用户能够控制窗体内控件的大小。

QSplitter,QScrollArea,QMainWindow,QWorkspace(对多文档的支持)

2) 布局管理中结合控件的sizePolicy属性,进行调整

结合控件的SizePolicy属性,来控制布局管理中的控件的尺寸自适应方式。

控件的sizePolicy说明控件在布局管理中的缩放方式。Qt提供的控件都有一个合理的缺省sizePolicy,但是这个缺省值有时不能适合所有的布局,开发人员经常需要改变窗体上的某些控件的sizePolicy。一个QSizePolicy的所有变量对水平方向和垂直方向都适用。下面列举了一些最长用的值:

A. Fixed:控件不能放大或者缩小,控件的大小就是它的sizeHint。

B. Minimum:控件的sizeHint为控件的最小尺寸。控件不能小于这个sizeHint,但是可以

放大。

C. Maximum:控件的sizeHint为控件的最大尺寸,控件不能放大,但是可以缩小到它的最小

的允许尺寸。

D. Preferred:控件的sizeHint是它的sizeHint,但是可以放大或者缩小

E. Expandint:控件可以自行增大或者缩小

注:sizeHint(布局管理中的控件默认尺寸,如果控件不在布局管理中就为无效的值)

1.QHBoxLayout是水平布局,将从左往右(orright to left for right-to-left languages )widget布局成水平行

2.QVBoxLayout是垂直布局,从顶部到底部

3.QGridLayout 是二位的网格布局。它可以容纳多个单元格:

4.QFormLayout是两列label-field式的表单布局

代码举例:

下面代码创建QHBoxLayout来管理5个QPushButtons的几何图形:

QWidget *window= new QWidget;
QPushButton *button1= new QPushButton("One");
QPushButton *button2= new QPushButton("Two");
QPushButton *button3= new QPushButton("Three");
QPushButton *button4= new QPushButton("Four");
QPushButton *button5= new QPushButton("Five");
QHBoxLayout *layout= new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();

QGridLayout示例如下:

 QWidget *window = new QWidget;
 QPushButton *button1 = new QPushButton("One");
 QPushButton *button2 = new QPushButton("Two");
 QPushButton *button3 = new QPushButton("Three");
 QPushButton *button4 = new QPushButton("Four");
 QPushButton *button5 = new QPushButton("Five");
 
 QGridLayout *layout = new QGridLayout;
 layout->addWidget(button1, 0, 0);
 layout->addWidget(button2, 0, 1);
 layout->addWidget(button3, 1, 0, 1, 2);
 layout->addWidget(button4, 2, 0);
 layout->addWidget(button5, 2, 1);
 
 window->setLayout(layout);
 window->show();

QFormLayout示例如下:

 QWidget *window = new QWidget;
 QPushButton *button1 = new QPushButton("One");
 QLineEdit *lineEdit1 = new QLineEdit();
 QPushButton *button2 = new QPushButton("Two");
 QLineEdit *lineEdit2 = new QLineEdit();
 QPushButton *button3 = new QPushButton("Three");
 QLineEdit *lineEdit3 = new QLineEdit();
 
 QFormLayout *layout = new QFormLayout;
 layout->addRow(button1, lineEdit1);
 layout->addRow(button2, lineEdit2);
 layout->addRow(button3, lineEdit3);
 
 window->setLayout(layout);
 window->show();

布局技巧:

当使用布局的时候,在创建子widget时,没必要给它传递父类。布局会自动重新定义它们的父类(通过QWidget::setParent())以确保它们是装载布局的widget的子类。

注意1:布局中的控件是装载布局控件的子控件,不是布局的子控件。控件只能以其他控件作为父类,不可以以布局作为父类。在布局上,可以使用addLayout来嵌套布局;被嵌套的布局,将变成上层布局的子布局。

向布局添加widgets:

添加布局到widgets时,布局过程执行如下:

1.所有widgets将根据它们的 QWidget::sizePolicy() and QWidget::sizeHint()首先分配一些空间。

2. 如果有widgets设置了大于0的拉伸系数,接下来它们将按照拉伸系数的比例来分配空间。

3. 如果有widgets设置的拉伸系数是0,它将在没有其他widgets需要空间时获取更多空间。其中,带Expanding大小策略的widget将首先获得空间。

4. 所有分配了小于最小空间(或者设置了最小的sizehint)的widget将按要求分配最小空间。(在拉伸系数成为决定因子时,widgets没必要再用最小值或者最小hint)。

5. 任何分配了大于最大空间的widget将按要求分配最大空间。(拉伸系数起着决定作用)

拉伸系数:

通常,widgets创建的时候没有设置拉伸系数。当widget整理到一个布局中时,它们将根据QWidget::sizePolicy()或者最小大小hint(取决于谁更大)分配一定空间。拉伸系数被用于按比例改变widget的分配空间。

如果3个widget用QHBoxLayout 来布局,不带拉伸系数,它们将得到像下面的布局:

如果带上拉伸系数,情况将变成这样:

自定义widget的布局:

当编写自定义widget类时,需要显示提供它的布局属性。如果widget有Qt自带的布局,它能够自己满足自己。如果没有任何子布局,或者使用手动布局,可以通过下面的机制来改变widget的行为:

1.实现QWidget::sizeHint() 来返回首先大小

2.实现QWidget::minimumSizeHint()来返回widget可以拥有的最小空间

3.调用QWidget::setSizePolicy来描述widget所需的空间

当size hint、minimum size或size policy改变时,调用QWidget::updateGeometry()。这将促使布局重新进行计算。连续多次调用QWidget::updateGeometry()只会发生一次布局重新计算。

即便实现了QWidget::heightForWidth(),也有必要提供合理的sizeHint()。

进一步了解,参见:Trading Height for Width.

布局问题:

The use of rich text in a label widget can introduce some problemsto the layout of its parent widget. Problems occur due to the way rich text ishandled by Qt's layout managers when the label is word wrapped.

Incertain cases the parent layout is put into QLayout::FreeResize mode, meaningthat it will not adapt the layout of its contents to fit inside small sizedwindows, or even prevent the user from making the window too small to beusable. This can be overcome by subclassing the problematic widgets, andimplementing suitable sizeHint() andminimumSizeHint() functions.

Insome cases, it is relevant when a layout is added to a widget. When you set thewidget of a QDockWidget ora QScrollArea (with QDockWidget::setWidget() andQScrollArea::setWidget()), the layout mustalready have been set on the widget. If not, the widget will not be visible.

在QLabel中使用富文本会给布局的父类widget带来一些问题。问题发生的原因是因为当label被文字环绕时,富文本被Qt的布局管理器控制。

在某些情况下,父类布局被放入QLayout::FreeResize模式,这意味着它将不适应内容布局所设置的最小窗口,或者甚至阻止用户让窗口小到不可用的情况。这个可以通过将问题控件作为子类来解决,并实现合适的sizeHint()和minimumSizeHint()函数。

在一些情况下,当布局被添加到widget时需要特别注意。当设置QDockWidget ora QScrollArea widget时(用QDockWidget::setWidget() andQScrollArea::setWidget()),布局必须已经被设置到widget上。否则,这些widget将不可见。

手动布局:

如果想自定义一个独特的布局,可以按 如上所述地自定义一个widget。实现QWidget::resizeEvent()来计算所需的大小分配并在每个子类中调用setGeometry() 。

需要布局需要重新计算大小时,widget将提供一个事件接口QEvent::LayoutRequest 。实现QWidget::event()来接收QEvent::LayoutRequest事件。

自定义布局管理:

自定义布局的唯一方法是继承QLayout来完成自己布局管理器。Border Layout 和Flow Layout 例子将说明如何来完成。

下面将举个例子来说明。CardLayout 类,受同名java布局管理的启发。它分层管理每个元素,每个元素的通过QLayout::spacing()来设置位移量。

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

由布局控制的存放元素的数据结构。每个元素都是一个QLayoutItem。在这个例子中,我们将使用QList 。

1. addItem(),描述如何添加元素到布局。

2.setGeometry(),描述如何完成布局

3.sizeHint(),布局的首选大小

4.itemAt(),描述如何递归布局

5.takeAt(),描述如何移除布局中的元素。

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

头文件
 
card.h
#ifndef CARD_H
#define CARD_H
 
#include <QtGui>
#include <QList>
 
class CardLayout : public QLayout
{
public:
 CardLayout(QWidget *parent, int dist): QLayout(parent, 0, dist) {}
 CardLayout(QLayout *parent, int dist): QLayout(parent, dist) {}
 CardLayout(int dist): QLayout(dist) {}
 ~CardLayout();
 
 void addItem(QLayoutItem *item);
 QSize sizeHint() const;
 QSize minimumSize() const;
 int count() const;
 QLayoutItem *itemAt(int) const;
 QLayoutItem *takeAt(int);
 void setGeometry(const QRect &rect);
 
private:
 QList<QLayoutItem*> list;
};
#endif
 
实现文件
 
card.cpp
#include "card.h"
int CardLayout::count() const
{
 // QList::size() returns the number of QLayoutItems in the list
 return list.size();
}
 
int CardLayout::count() const
{
 // QList::size() returns the number of QLayoutItems in the list
 return list.size();
}
 
int CardLayout::count() const
{
 // QList::size() returns the number of QLayoutItems in the list
 return list.size();
}
 
CardLayout::~CardLayout()
{
 QLayoutItem *item;
 while ((item = takeAt(0)))
 delete item;
}
 
void CardLayout::setGeometry(const QRect &r)
{
 QLayout::setGeometry(r);
 
 if (list.size() == 0)
 return;
 
 int w = r.width() - (list.count() - 1) * spacing();
 int h = r.height() - (list.count() - 1) * spacing();
 int i = 0;
 while (i < list.size()) {
 QLayoutItem *o = list.at(i);
 QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h);
 o->setGeometry(geom);
 ++i;
 }
}
QSize CardLayout::sizeHint() const
{
 QSize s(0,0);
 int n = list.count();
 if (n > 0)
 s = QSize(100,70); //start with a nice default size
 int i = 0;
 while (i < n) {
 QLayoutItem *o = list.at(i);
 s = s.expandedTo(o->sizeHint());
 ++i;
 }
 return s + n*QSize(spacing(), spacing());
}
 
QSize CardLayout::minimumSize() const
{
 QSize s(0,0);
 int n = list.count();
 int i = 0;
 while (i < n) {
 QLayoutItem *o = list.at(i);
 s = s.expandedTo(o->minimumSize());
 ++i;
 }
 return s + n*QSize(spacing(), spacing());
}
 

进一步说明:自定义布局没有控制宽和高。

忽略了 QLayoutItem::isEmpty(),这意味着布局将把隐藏widget作为可见的。

对于复杂布局,通过缓存计算将大大提高速度。在那种情况下,实现QLayoutItem::invalidate() 来标记数据是脏数据。

调用QLayoutItem::sizeHint()等的代价比较大。在通过函数中,需要再次使用,最好将结果保存在本地变量中。

在同样函数的同一个元素中,不应该调用两次 QLayoutItem::setGeometry()。 这个调用将耗费巨大,如果它用几个子widget,因为布局管理器每次都要做一个完整的布局。替代方法:先计算geometry,然后再设置(这种事情,不仅应该在布局时注意,在实现resizeEvent()时也需要按同样方法来做)。

另外:

作为QLayout的父类,QLayoutItem提供了下列方法,包括绘制和范围的信息:

virtual QSize sizeHint() const = 0
virtual QRect geometry() const = 0
virtual void invalidate()
virtual QLayout * layout()
Qt::Alignment alignment() const 

QLayout提供的信息就比较多了:提供了子页面、子Layout的添加接口,设置边界、菜单项等的接口

virtual void addItem(QLayoutItem *item) = 0
void addWidget(QWidget*w)
void setContentsMargins(intleft, int top, int right, int bottom)
void setMenuBar(QWidget*widget)

QBoxLayout作为QLayout的子类,提供了一些额外的信息:提供元素的拉伸比例,添加空元素等

void addLayout(QLayout*layout, int stretch = 0)
void addSpacerItem(QSpacerItem*spacerItem)
void addSpacing(intsize)
bool setStretchFactor(QWidget*widget, int stretch)
bool setStretchFactor(QLayout*layout, int stretch)

界面的继承情况,在http://doc.qt.io/qt-5/qlayout.html有详细的介绍,不做额外的介绍。

使用的过程的例子介绍一些:

// 设置边界为0

QHBoxLayout *pLayout1 = new QHBoxLayout();
QHBoxLayout *pLayout2 = new QHBoxLayout();
pLayout1->setMargin(0);
pLayout2->setMargin(0);

// 设置拉伸比例2:3

pLayoutMain->addLayout(pLayout1);
pLayoutMain->addLayout(pLayout2);
pLayoutMain->setStretch(0, 2);
pLayoutMain->setStretch(1, 3);

// 底面边距设0

int left = 0, right = 0, top = 0, bottom = 0;
pMainLayout->getContentsMargins(&left, &top, &right, &bottom);
pMainLayout->setContentsMargins(left, right, top, 0);

// 添加填充弹簧条

pLayout1->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));

// 元素设置位置:左对齐,上下居中

pLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);

// 设置固定高度

pLabel->setFixedHeight(21);

// 设置固定宽度

pLabel->setFixedWidth(21);

相关推荐

3分钟让你的项目支持AI问答模块,完全开源!

hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...

干货|程序员的副业挂,12个平台分享

1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...

Github标星超200K,这10个可视化面板你知道几个

在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...

开箱即用的炫酷中后台前端开源框架第二篇

#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...

搭建React+AntDeign的开发环境和框架

搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...

基于.NET 5实现的开源通用权限管理平台

??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...

StreamPark - 大数据流计算引擎

使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...

教你使用UmiJS框架开发React

1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...

简单在线流程图工具在用例设计中的运用

敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...

【开源分享】神奇的大数据实时平台框架,让Flink&amp;Spark开发更简单

这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...

聊聊规则引擎的调研及实现全过程

摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...

【开源推荐】Diboot 2.0.5 发布,自动化开发助理

一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...

微软推出Copilot Actions,使用人工智能自动执行重复性任务

IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...

Electron 使用Selenium和WebDriver

本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...

Quick &#39;n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具

一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...