一、引言

在Qt Designer中,在左边部件栏的提供了界面布局相关部件,如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值

可以看到共包含有四种布局部件,分别是垂直布局(Vertical Layout)、水平布局(Horizontal Layout)、网格布局(Grid Layout)、表单布局(Form Layout),实际上除了以上布局之外,还有两种布局,一种是没有布局的布局,称为绝对布局,一种是多种布局组合或嵌套使用,称为组合布局。

二、布局概述

在Designer设计的GUI界面中,如果使用绝对布局,则窗口中的部件在窗口拉伸或缩放时大小和位置都不会改变,这对于窗口要进行拉伸以及应用适应屏幕分辨率变化导致的被动窗口大小调整的情况,界面就不是设计者需要的展示效果,为此需要进行布局管理,确保窗体拉伸时整体界面保持正常显示。

Designer中提供了四种布局,分别是垂直布局(Vertical Layout)、水平布局(Horizontal Layout)、网格布局(Grid Layout)、表单布局(Form Layout),这四种布局分别对应PyQt中的四个类,分别是QVBoxLayout、QHBoxLayout、QGridLayout、QFormLayout。

这四个类都是从QLayout类或其子类QBoxLayout派生出来的,相关继承关系如下:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_表单_02

实际上作为布局的类,除了上面介绍的四个类之外,还有一个类QStackedLayout,这个用于界面上有多个窗口但只显示一个窗口时使用,由代码控制,因此在Designer中没有,在此也不进行介绍。

三、布局管理相关的通用属性

由于所有布局管理部件都是QLayout类的子类或子类的子类,它们在Qt Designer中存在可以设置的共有属性,我们先对这些属性进行介绍。

3.1、contentsMargins属性

3.1.1、概述

Designer中可以看到,所有的布局部件都有layoutLeftMargin、layoutRightMargin、layoutTopMargin、layoutBottomMargin这4个属性,如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_03

这4个属性是表示布局部件内的子部件与布局部件四周的空余空间(留白)大小,layoutLeftMargin、layoutRightMargin、layoutTopMargin、layoutBottomMargin分别控制子部件与布局控件的左边、右边、顶端、底部的留白大小,该大小以像素为单位。

上图中的垂直布局部件的左边、右边、顶端、底部的空余空间大小分别设置为50、 20、 20 、100,可以明显看出在布局部件四周的留白大小比例。对于垂直布局来说,当子部件个数过多导致当前布局无法容纳或底部留白大小过小时,布局部件会自动往下扩展,其他布局类似。

3.1.2、相关的类属性

实际上layoutLeftMargin、layoutRightMargin、layoutTopMargin、layoutBottomMargin这4个属性并不是布局控件的直接属性,而是布局部件的contentsMargins属性的子属性,下面是上图设置界面通过将UI生成代码后的对应代码:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_04

3.1.3、contentsMargins属性的访问方法

contentsMargins属性是QMargins 类型(QMargins通过QMargins(int left, int top, int right, int bottom) 来创建),可以通过contentsMargins() 、setContentsMargins( QMargins )两个方法进行访问和设置。

3.2、layoutSizeConstraint属性

3.2.1、概述

所有布局控件都有layoutSizeConstraint这个属性,设置界面如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_表单_05

这个属性表示布局允许调整大小的模式,主要用于控制设置布局的窗口调整大小的范围。

3.2.2、相关类属性

Qt Designer的layoutSizeConstraint属性实际对应QLayout类的sizeConstraint属性,具体取值由枚举类型QLayout.SizeConstraint的元素取值来决定。

老猿针对官网公布的SizeConstraint属性相关取值及含义进行测试,发现验证结果和官网说明有些差异,相关情况如下:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_06

关于验证情况,补充说明如下:

1、 以上验证情况验证的场景有限,都是限定最小和最大大小的情况下验证的,老猿不能确保所有场景下是否都正确;

2、 屏幕最大尺寸可以通过QtWidgets.qApp.desktop().width(), app.desktop().height()获取,可能有1个像素点的差异,具体原因未研究;

3、 设置了窗口布局的sizeConstraint属性情况下,窗口的sizePolicy属性不起作用(关于sizePolicy属性请参考《PyQt(Python+Qt)入门:Designer组件属性编辑界面中QWidget类相关属性详解》);

4、 layoutSizeConstraint取值为SetMinimumSize时,不论minimumSizeHint是否比minimumSize大还是小,窗口尺寸的最小值都是minimumSizeHint限定值。

注意:

1、 要让布局起作用,其父部件也必须是布局,如果是最底层的窗口,必须设置窗体布局,请见本节后面的窗体布局介绍;

2、 布局的sizeConstraint属性设置只对顶层窗口起作用,对非窗口级的布局不起作用。

3.2.3、SizeConstraint属性访问方法

sizeConstraint属性的默认值是SetDefaultConstraint,可以通过sizeConstraint() 、setSizeConstraint(QLayout.SizeConstraint)来访问和设置。

3.3、layoutSpacing属性

3.3.1、概述

Vertical Layout(垂直布局)、Horizontal Layout(水平布局)这两个布局控件都有layoutSpacing这个属性。如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_07

layoutSpacing属性用于控制布局内部件之间的间距。

3.3.2、相关类属性

实际上布局控件对应的类QLayout并没有layoutSpacing这个属性,该属性实际上对应的是QLayout的spacing属性,用来指定布局控件内的部件之间的间距,这个间距对垂直布局部件来说就是子部件间垂直间距,对水平布局部件来说就是子部件间的水平间距。对于网格布局(Grid Layout)、表单布局(Form Layout)这个参数没有意义,,读取时会返回-1,因此Qt Designer中这两类布局没有这个属性的设置界面。

3.3.3、spacing属性访问方法

该属性如果没有设置,则会从父布局部件中继承,或者从父部件的样式设置中继承。

该属性通过spacing() 、setSpacing(int)来访问和设置。

四、绝对布局

绝对布局就是在部件中不使用布局,部件属性中跟绝对布局相关的属性是Geometry 属性,它包括左上角的坐标位置和宽度及高度。

绝对布局存在两大问题,一是部件大小调整或不同分辨率屏幕适配时,各部件的大小和位置不会调整,导致整体布局错乱,二是各子部件之间不能自动对齐,需要靠人工逐一对齐。因此一般情况下都不要使用绝对布局。

五、水平布局

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_08

水平布局就是在布局内的所有子部件成一条水平线自动排列,如果布局内的部件太多且在上层布局允许的情况下自动扩展布局的水平宽度,如果布局水平宽度不能扩展则压缩部件的宽度。如果部件(如TextEdit)及其大小策略支持垂直方向拉伸,且其高度没有限制或不小于布局的高度,则部件的高度自动扩展到整个布局允许的高度(就是扣除留白空间的高度)。

可以通过调整部件的拉伸因子来调整不同部件占用水平空间的比例,缺省每个部件拉伸因子都是0,如果部件宽度的大小策略及最小宽度、最大宽度、部件最小建议宽度没有影响,这时这些部件的宽度实际上是等比例的,每个部件占用一样的宽度。如果受其他影响,在受影响的部件宽度确认后,其他部件等分水平方向剩余空间。

可以通过layoutSpacing调整部件的间距,通过layoutLeftMargin、layoutRightMargin、layoutTopMargin、layoutBottomMargin这4个属性来调整部件和布局四周的留白空间。

一般并排排列的一类对象如按钮可以采用水平布局。如:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_09

六、垂直布局

垂直布局就是在布局内的所有子部件堆叠成一条垂直的直线自动排列,如果布局内的部件太多且在上层布局允许的情况下自动扩展布局的垂直高度,如果布局的高度不能扩展则压缩部件的高度。如果部件最大宽度没有限制或不小于布局的宽度,则部件的宽度自动扩展到整个布局允许的宽度(就是扣除留白空间的宽度)。

可以通过调整部件的拉伸因子来调整不同部件占用垂直空间的比例,缺省每个部件拉伸因子都是0,如果部件高度的大小策略及最小高度、最大高度、建议最小高度没有实际的影响,这时这些部件的高度实际上是等比例的,每个部件占用一样的高度。如果受其他影响,在受影响的部件高度确认后,其他部件等分剩余高度空间。

可以通过layoutSpacing调整部件的间距,通过layoutLeftMargin、layoutRightMargin、layoutTopMargin、layoutBottomMargin这4个属性来调整部件和布局四周的留白空间。

一般垂直排列的一类对象如垂直排列的单选按钮可以采用垂直布局。

七、QBoxLayout的Stretch属性

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_10

7.1、概述

在 Qt Designer中Vertical Layout(垂直布局)、Horizontal Layout(水平布局)这两个布局控件都有layoutStretch这个属性,如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_11

layoutStretch是布局内部件的拉伸因子。

7.2、相关类属性

实际上布局控件对应的类QLayout并没有layoutStretch这个属性,该属性实际上对应的是QBoxLayout类(QBoxLayout是QLayout的派生类,是垂直布局部件和水平布局部件的父类)的stretch属性。用于表示布局中不同子部件的伸缩因子,即不同部件在部件伸缩时调整部件所占大小的比例。但这个伸缩因子对按钮等Qt不建议拉伸的部件不会生效。如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_12

上图中将一个ListView、TextBrowser、PushButton三个子部件放到了布局部件中,设置拉伸因子为为2:1:4,可以看到前面两个控件是2:1的比例,但PushButton固定了高度。

7.3、访问方法:

可以通过stretch(int index)、setStretch(int index, int stretch)来访问或设置该属性,其中参数index为要设置拉伸因子的部件的位置索引,参数stretch为设置的拉伸因子值。

八、网格布局

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_13

8.1、概述

网格布局就是布局内的部件使用多行多列对齐的网格方式排列,所有行的列数一样,所有列的行数一样,允许某些网格内没有部件。如一个计算器的按键PushButton部件采用网格布局:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_14

8.2、layoutHorizontalSpacing和layoutVerticalSpacing属性

8.2.1、概述

layoutHorizontalSpacing和layoutVerticalSpacing属性在Qt Designer中是网格布局(gridLayout)和表单布局(formLayout)都有的属性,分别表示布局内部件的水平间距和垂直间距。

8.2.2、layoutHorizontalSpacing 布局的水平间距

layoutHorizontalSpacing属性为并排排列的部件之间的间距,如果未显式设置任何值,则布局的水平间距将继承自父布局或父部件的样式设置。

可以通过horizontalSpacing() 或setHorizontalSpacing(int spacing)来访问或设置layoutHorizontalSpacing 属性。

8.2.3、verticalSpacing 布局的垂直间距

verticalSpacing 属性为保存相互重叠的部件之间的间距,如果未显式设置任何值,则布局的垂直间距将继承自父布局或父部件的样式设置。

可以通过verticalSpacing()或setVerticalSpacing (int spacing)

来访问或设置verticalSpacing属性。

8.3、layoutRowStretch和layoutColumnStretc属性

layoutRowStretch和layoutColumnStretch两个属性是网格布局独有的特性,分别设置网格布局中行之间和列之间的拉伸因子,这两个属性是Qt Designer中设置使用,实际对应QGridLayout的rowStretch和columnStretch属性,只是rowStretch和columnStretch访问或设置属性时是要指定行或列来设置拉伸因子。

8.3.1、访问方法

1、columnStretch(int column)#获取指定列的拉伸因子

2、rowStretch(int row) #获取指定行的拉伸因子

3、setColumnStretch(int column, int stretch) #设置指定列的拉伸因子

4、setRowStretch(int row, int stretch) #设置指定行的拉伸因子

8.3.2、界面设置属性与类属性关系

而layoutRowStretch和layoutColumnStretch是一次指定所有行或列的拉伸因子。我们来看看这两个属性在Designer中设置后转换成的代码,如下图是属性设置界面设置的拉伸因子:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_15

上述截图生成的相关代码如下:

        self.gridLayout.setColumnStretch(0, 1)
self.gridLayout.setColumnStretch(1, 1)
self.gridLayout.setColumnStretch(2, 4)
self.gridLayout.setRowStretch(0, 1)
self.gridLayout.setRowStretch(1, 1)
self.gridLayout.setRowStretch(2, 1)
self.gridLayout.setRowStretch(3, 1)
self.gridLayout.setRowStretch(4, 1)
self.gridLayout.setRowStretch(5, 1)


因此layoutRowStretch和layoutColumnStretch是Designer中为了简化界面设计而设置的这两个属性,实际上是映射到rowStretch和columnStretch。

8.3.4、注意

1、在Qt Designer中同一列的网格具有相同的宽度,同一行的网格具有相同的高度,但不同行可以有不同高度,在部件允许拉伸且设置了不同的拉伸因子的情况下,窗口拉伸时会出现不同行有不同高度的情况,同理不同列可以有不同宽度。

下图是计算器的界面设置行和列的不同拉伸因子:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_16

拉伸之后:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_17

可以看到每行部件的高度未受拉伸因子影响,但是行间距发生了变化,而列宽受到了拉伸因子的影响。

2、在Qt Designer中每个部件都只能占用一个网格,不能出现跨网格的部件,但实际上Qt是支持一个部件占用多个网格的,不过那只能通过代码实现。具体方法在此不进行介绍。

九、 表单布局

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_18

9.1、概述

表单就是提示用户进行交互的一种模式,由两个列组成,第一列用于显示信息,给予用户提示,一般叫做标签(label)域,第二列是需用户选择输入的,一般叫字段(field)域。表单布局就是由包含多行其每行由这两列内容组成行的布局。

9.2、表单布局优点

可以使用两列的网格布局代替表单布局,之所以要单独提供表单布局,是相比较于网格布局,表单布局有如下优点:

1、 可以通过布局指定各行标签的对齐方式,而不用逐行设置;

2、 可以支持字段域的换行策略,换行策略就是什么情况下字段域和标签域是两行还是一行;

3、 可以支持设定统一的Filed域拉伸策略;

4、 程序代码访问不用通过控件名访问,可以通过行号和角色来访问或设置表单的标签和字段对象,其中行号是表单对应的记录行,角色是表明要访问的内容是标签(角色为QFormLayout.LabelRole)还是字段(角色为QFormLayout. FieldRole)或者横跨两个字段的部件(角色为QFormLayout. SpanningRole)。

9.3、表单布局案例

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_19

9.4、表单布局相关属性设置

9.4.1、layoutFieldGrowthPolicy属性

layoutFieldGrowthPolicy用于控制表单布局中字段域输入部件大小的增长方式。如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_20

该字段实际与QFormLayout类的FieldGrowthPolicy属性对应,其值为枚举类型QFormLayout.FieldGrowthPolicy的元素,相关取值及含义如下:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_21

默认值取决于部件或应用程序样式。对于QMacStyle,默认值为FieldsStayAtSizeHint;对于QCommonStyle派生样式(如Plastique和Windows),默认值为ExpandingFieldsGrow;对于Qt扩展样式,默认值为AllNonFixedFieldsGrow。

如果没有任何输入部件可以增长,并且窗体已调整大小,多余的空间则会根据当前窗体对齐方式分配。

该属性可以通过FieldGrowthPolicy()和setFieldGrowthPolicy(FieldGrowthPolicy policy)来进行访问和设置。

9.4.2、layoutRowWrapPolicy属性

9.4.2.1、概述

layoutRowWrapPolicy用于控制表单布局中表单行的标签域和字段域输入部件之间是否换行。属性设置界面如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_22

上图中蓝色标记圈起来的下拉列表数据是其可设置的值。

9.4.2.2 相关类属性

layoutRowWrapPolicy实际上是与QFormLayout的rowWrapPolicy属性相对应,默认值取决于部件或应用程序样式。对于Qt扩展样式,默认值是WrapLongRows;对于其他样式,默认值是DontWrapRows。

如果要在关联字段即输入部件上方(而不是旁边)显示每个标签,请将此属性设置为WrapAllRows。

layoutRowWrapPolicy对应取值及含义如下:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_字段_23

可以通过方法rowWrapPolicy() 、 setRowWrapPolicy(QFormLayout.RowWrapPolicy policy)来访问或设置该属性。

9.4.2.3、layoutRowWrapPolicy不同取值样例

下图是一个输入字段未换行的表单及其对应窗口:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_表单_24

下图是所有表单行的输入字段和标签分置在不同行的场景:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_25

下图是个按需换行的场景:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_表单_26

9.5、layoutLabelAlignment 属性

9.5.1、概述

layoutLabelAlignment 用于控制表单布局中标签的水平对齐方式(包括垂直和水平方向两个方向)。设置界面如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_27

9.5.2、相关属性

layoutLabelAlignment属性实际对应的是QFormLayout的labelAlignment属性,默认值取决于部件或应用程序样式。对于从QCommonStyle派生的样式,除了QPlastiqueStyle(KDE桌面环境的界面风格)默认值是Qt.AlignLeft,其他样式默认值都是Qt.AlignRight。

labelAlignment属性的类型是枚举类Qt.Alignment或Qt.AlignmentFlag,其中 Qt.AlignmentFlag是对齐标记,它包括水平对齐标记、垂直对齐标记、两维对齐标记、以及右对齐应用模式中的组合标记:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_28

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_表单_29

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_30

对于对齐模式的掩码常量,没有找到相关资料说明其用途,老猿估计是用于对该属性的赋值数据通过与掩码常量进行与操作进行过滤使用,防止非正常对齐标记的数据出现。

9.5.3、注意

Qt.Alignment是使用Qt.AlignmentFlag对应的对齐标记通过或(|)操作组合而成的,但如果组合时出现冲突的标记如水平对齐出现了两种不同的方式则该组合无效。

9.5.4、属性访问方法

可以通过方法labelAlignment() 、setLabelAlignment(Qt.Alignment alignment)来访问或设置labelAlignment属性的值。

9.5.5、案例

self.formLayout.setLabelAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignHCenter)

9.6、layoutFormAlignment 属性

9.6.1、概述

ayoutFormAlignment 属性用于控制表单布局中所有子部件在布局框内的对齐方式(与layoutLabelAlignment类似,也包括垂直和水平方向两个方向)。设置界面如图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_31

9.6.2、相关类属性

layoutFormAlignment属性实际对应的是QFormLayout的formAlignment 属性,缺省值取决于部件或应用程序样式。对于 QMacStyle样式,缺省值是Qt.AlignHCenter | Qt.AlignTop,其他样式默认值都是 Qt.AlignLeft | Qt.AlignTop。

formAlignment 属性的类型是枚举类Qt.Alignment,该类型是由枚举类型Qt.AlignmentFlag的水平和垂直对齐标记的值通过或操作组合而成。相关取值说明请参考前面layoutLabelAlignment 属性的介绍。

9.6.3、formAlignment 属性访问方法

可以通过formAlignment() 、setFormAlignment(Qt.Alignment alignment)来访问或设置formAlignment 属性。

十、 窗体布局

10.1、概述

前面介绍的布局是常见的四种部件布局,这些布局可以部署在窗口上,也可以放在其他的布局内。实际上除了窗口上部署布局之外,还可以基于窗体(窗口)设置布局,并且如果窗口上有布局部件,但窗口本身没有设置布局,在窗口拉伸时布局还是不起作用,因此承载布局部件的窗口必须设置布局。

10.2、设置方法:

通过窗口中点击鼠标右键->Lay out选择布局方式来设置布局,如下图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_默认值_32

可以通过”窗口名.layout()”方法访问窗口的布局。

十一、 组合布局

一般的布局都不是单一的布局,都是多种布局的组合,如下图:

第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_网格布局_33

先设置窗口的布局为垂直布局,再在上面增加了表单布局和水平布局。更复杂的场景下,可以在窗体上放置布局后,再在各布局内再放置布局,再基于这些布局来放置部件或更多的布局。

十二、 小结

本节详细介绍了Qt Designer中4种布局方式,以及各自的特性,并介绍了窗体布局的设置方式,实际上绝大多数界面的布局是组合布局,单一的布局使用很少。并且布局只有在设置了窗体布局的情况下才会真正发生作用。

布局在拉伸时对布局部件的影响受布局大小模式sizeConstraint、部件的最小大小、最大大小、建议大小、建议最小大小等因素的综合影响。


第15.12节PyQt(Python+Qt)入门学习:可视化设计界面组件布局详解_取值_34