正文内容
Qt Widgets布局管理
所有的QWidget的子类的实例都可以使用布局管理器管理位于它们之中的子部件,QWidget::setLayout()函数可以在一个部件上应用布局管理器。一旦一个部件上设置了布局管理器,那么它会完成以下几种任务:
- 定位子部件
- 感知窗口默认大小
- 感知窗口最小大小
- 窗口大小变化时进行处理
- 当内容改变时自动更新:
- 字体大小、文本或子部件的其他内容随之改变
- 隐藏或显示子部件
- 移除一个子部件
布局管理器简介
QLayout类是布局管理器的基类,它是一个抽象基类,继承自QObject和QLayoutItem类,其中QLayoutItem类提供了一个供QLayout操作的抽象项目。
QLayout和QLayoutItem都是在设计自己的布局管理器时才使用的,一般只需要使用QLayout的几个子类即可:
- QBoxLayout(基本布局管理器)
- QGridLayout(栅格布局管理器)
- QFormLayout(窗体布局管理器)
- QStackedLayout(栈布局管理器)
基本布局管理器
基本布局管理器QBoxLayout类可以使子部件在水平方向或者垂直方向排成一列,将所有的空间分成一行盒子,然后将每个部件放入对应盒子中。属于抽象类,不能直接实例化,它有两个子类:
QHBoxLayout:水平布局管理器QVBoxLayout:垂直布局管理器
例如:可以使用addWidget()函数向布局管理器的末尾添加部件,还有一个insertWidget()函数可以实现向指定位置添加部件,它比前者更灵活。
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(ui->fontComboBox);
layout->addWidget(ui->textEdit);
layout->setSpacing(50);
layout->setContentsMargins(0, 0, 50, 100); setLayout(layout);
常用属性:
| 属性 | 说明 |
|---|---|
layoutName |
现在所使用的布局管理器的名称 |
layoutLeftMargin |
设置布局管理器到界面左边界的距离 |
layoutTopMargin |
设置布局管理器到界面上边界的距离 |
layoutRightMargin |
设置布局管理器到界面右边界的距离 |
layoutBottomMargin |
设置布局管理器到界面下边界的距离 |
layoutSpacing |
布局管理器中各个子部件间的距离 |
layoutStretch |
伸缩因子,设置部件的大小比例 |
layoutSizeConstraint |
设置大小约束条件,例如QLayout::SetFixedSize设置窗口无法改变大小 |
栅格布局管理器
栅格布局管理器QGridLayout类使部件在网格中进行布局,它将所有的空间分隔成一些行和列,行和列的交叉处就形成了单元格,然后将部件放入一个确定的单元格中。
这里主要是设置部件在栅格布局管理器中的位置,将fontComboBox部件设置为占据1行2列,而pushButton部件占据1行1列,这主要是为了将fontComboBox部件和pushButton部件的长度设置为2:1。这样一来,textEdit部件要想占满剩下的空间,就要使它的跨度为3列。
QGridLayout *layout = new QGridLayout;
// 添加部件,从第0行0列开始,占据1行2列
layout->addWidget(ui->fontComboBox, 0, 0, 1, 2);
// 添加部件,从第0行2列开始,占据1行1列
layout->addWidget(ui->pushButton, 0, 2, 1, 1);
// 添加部件,从第1行0列开始,占据1行3列
layout->addWidget(ui->textEdit, 1, 0, 1, 3);
setLayout(layout);
窗体布局管理器
窗体布局管理器QFormLayout类用来管理表单的输入部件以及与它们相关的标签。窗体布局管理器将它的子部件分为两列,左边是一些标签,右边是一些输入部件,比如行编辑器或者数字选择框等。
- 在设计模式从部件列表窗口中找到Form Layout,将其拖入到界面上,然后双击或者在它上面右击并在弹出的级联菜单中选择“添加窗体布局行”。
- 在弹出的“添加表单布局行”对话框中填入标签文字“姓名(&N):”,这样下面便自动填写了“标签名称”、“字段类型”和“字段名称”等,并且设置了伙伴关系。
- 这里使用了QLineEdit行编辑器,当然也可以选择其他部件。填写的标签文字中的“(&N)”必须是英语半角的括号,表明它的快捷键是Alt+N。
- 设置伙伴关系表示当按下Alt+N时,光标会自动跳转到标签后面对应的行编辑器中。
- 单击“确定”键,则会在布局管理器中添加一个标签和一个行编辑器。
综合使用布局管理器
前面讲到了3种布局管理器,真正使用时一般是将它们综合起来应用。
例如:
- 按下Ctrl键的同时选中界面上的字体选择框fontComboBox和按钮pushButton,然后按下Ctrl+H快捷键将它们放入一个水平布局管理器中(其实也可以从部件列表窗口中拖入一个Horizontal Layout,然后再将这两个部件放进去,效果是一样的)。
- 然后再从部件列表窗口中拖入一个Vertical Spacer垂直分隔符,用来在部件间产生间隔,将它放在窗体布局管理器与水平布局管理器之间。
- 最后单击主界面并按下Ctrl+L快捷键,让整个界面处于一个垂直布局管理器中。这时可以在右上角的对象查看器中选择MyWidget对象,然后在属性编辑器的最后将layoutStretch设置为“4,1,1,10”。

设置部件大小
这里再来解两个概念:大小提示(sizeHint)和最小大小提示(minimumSizeHint)。凡是继承自QWidget的类都有这两个属性。
sizeHint属性保存了部件的建议大小,对于不同的部件,默认拥有不同的sizeHint。minimumSizeHint保存了一个建议的最小大小提示。- 可以使用
sizeHint()函数来获取sizeHint的值,使用minimumSizeHint()函数获取minimumSizeHint的值。
Note
如果使用setMinimumSize()函数设置了部件的最小大小,那么最小大小提示将会被忽略。
下面再来看一下大小策略(sizePolicy)属性,它也是QWidget类的属性。这个属性保存了部件的默认布局行为,在水平和垂直两个方向分别起作用,控制着部件在布局管理器中的大小变化行为。
| 常量 | 描述 |
|---|---|
QSizePolicy::Fixed |
只能使用 sizeHint () 提供的值,无法伸缩 |
QSizePolicy::Minimum |
sizeHint () 提供的大小是最小的,部件可以被拉伸 |
QSizePolicy::Maximum |
sizeHint () 提供的是最大大小,部件可以被压缩 |
QSizePolicy::Preferred |
sizeHint () 提供的大小是最佳大小,部件可以被压缩或拉伸 |
QSizePolicy::Expanding |
sizeHint () 提供的是合适的大小,部件可以被压缩,不过它更倾向于被拉伸来获得更多的空间 |
QSizePolicy::MinimumExpanding |
sizeHint () 提供的大小是最小的,部件倾向于被拉伸来获取更多的空间 |
QSizePolicy::Ignored |
sizeHint () 的值被忽略,部件将尽可能的被拉伸来获取更多的空间 |
可扩展窗口
一个窗口可能很多选项是扩充的,只有在必要的时候才显示出来,这时就可以使用一个按钮,用来隐藏或者显示多余的内容,就是所谓的可扩展窗口。要实现可扩展窗口,就要得力于布局管理器的特性,那就是当子部件隐藏时,布局管理器自动缩小,当子部件重新显示时,布局管理器再次放大。
void MyWidget::on_pushButton_toggled(bool checked)
{
ui->textEdit->setVisible(checked);
if(checked) {
setFixedHeight(270);
ui->pushButton->setText(tr("隐藏可扩展窗口"));
} else {
setFixedHeight(170);
ui->pushButton->setText(tr("显示可扩展窗口"));
}
}


Qt Quick布局管理
Qt Quick中提供了多种布局方式,比如可以通过属性绑定来设置位置或者大小;还有基于锚 anchors的布局;另外,还提供了定位器,可以用来为多个项目进行常规的布局;如果需要同时管理项目的位置和大小,可以使用布局管理器
定位器
定位器是一个容器,可以管理其中子项目的布局,包括Column、Row、Grid和Flow。如果它们的子项目不可见(visible为false)、宽度或者高度为0,那么该子项目不会显示也不会被布局。
Column和Row
Column项目可以将其子项目垂直排成一列。下面的例子使用了Column定位了几个形状不同的Rectangle。Column的spacing属性用来为这几个Rectangle添加间距,padding属性用来设置Column子项目和边界之间的距离,也可以通过topPadding、bottomPadding、leftPadding、rightPadding分别进行设置。
Column {
spacing: 2; padding: 5
Rectangle { color: "white"; border.width: 1; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "lightgrey"; width: 50; height: 20 }
}

Grid
Grid项目可以将其子项目排列在一个网格中。Grid会计算一个足够大的矩形网格来容纳所有的子项目。向网格中添加项目,会按照从左向右、从上向下的顺序进行排列。每一个项目都会被放置在网格左上角(0, 0)的位置。一个Grid默认有四列,可以有无限多的行容纳所有的子项目。行数和列数也可以通过rows和columns属性指定。另外,Grid也可以通过spacing属性设置子项目之间的间距,此时,水平方向和垂直方向会使用相同的间距,如果需要分别设置,可以使用rowSpacing和columnSpacing属性。
Grid {
columns: 3; spacing: 2; padding: 5
Rectangle { color: "white"; border.width: 1; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "lightgrey"; width: 50; height: 20 }
Rectangle { color: "cyan"; width: 50; height: 50 }
Rectangle { color: "magenta"; width: 10; height: 10 }
}

Flow
Flow项目可以从前向后,像流一样布局其子项目,如同单词放置在页面上一样,通过换行,使这些子项目排列成多行或列。Flow排列项目的规则与Grid相似,它们的主要区别是,Flow的子项目会在超出边界后自动换行,每行的子项目数不一定相同。Flow有一个flow属性,包含两个值:Flow.LeftToRight(默认)和Flow.TopToBottom。前者是按照从左向右的顺序排列子项目,直到超出Flow的宽度,然后换到下一行;后者则按照从上到下的顺序排列其子项目,直到超出Flow的高度,然后换到下一列。
Rectangle {
color: "lightblue"; width: 300; height: 200
Flow {
anchors.fill: parent;
anchors.margins: 4; spacing: 10
Text { text: "Text"; font.pixelSize: 40 }
Text { text: "items"; font.pixelSize: 40 }
Text { text: "flowing"; font.pixelSize: 40 }
Text { text: "inside"; font.pixelSize: 40 }
Text { text: "a"; font.pixelSize: 40 }
Text { text: "Flow"; font.pixelSize: 40 }
Text { text: "item"; font.pixelSize: 40 }
}
}

使用过渡
定位器添加或删除一个子项目时,可以使用一个过渡(Transition),使这些操作具有动画效果。四个定位器都有add、move和populate属性,它们需要分配一个Transition对象。
- add过渡应用在定位器创建完毕后,向定位器中添加一个子项目,或者将子项目通过更换父对象的方式变为定位器的孩子时
- move过渡应用在删除定位器中的一个子项目,或者通过更换父对象方式从定位器中移除对象时
- populate过渡应用在定位器第一次创建时,只会运行一次。此外,将项目的透明度更改为0时,会使用move过渡隐藏项目
- 当项目的透明度为非0时,会使用add过渡显示项目。定位器过渡只会影响项目的位置(x, y)
Column {
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { id: greenRect; color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
move: Transition {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
focus: true
Keys.onSpacePressed: greenRect.visible = !greenRect.visible
}
Positioner
在Column、Row、Grid和Flow中会附加一个Positioner类型的对象作为顶层子项目,它可以为定位器中的子项目提供索引等信息。例如,Grid通过Repeater创建了16个子矩形,每一个子矩形都使用Positioner.index显示了它在Grid中的索引,而第一个矩形使用了不同颜色进行绘制。
Grid {
padding: 5
Repeater {
model: 16
Rectangle {
id: rect; width: 40; height: 40; border.width: 1
color: Positioner.isFirstItem ? "yellow" : "lightsteelblue"
Text {
text: rect.Positioner.index;
anchors.centerIn: parent
}
}
}
}

Repeater
Repeater类型用来创建大量相似的项目。一个Repeater包含一个模型model属性和一个委托delegate属性。委托用来将模型中的每一个条目进行可视化显示。一个Repeater通常会包含在一个定位器中,用于直观地对Repeater产生的众多委托项目进行布局。
Rectangle {
width: 400; height: 240; color: "black"
Grid {
x: 5; y: 5; rows: 5; columns: 5; spacing: 10
Repeater {
model: 12
Rectangle {
width: 70; height: 70; color: "lightgreen"
Text {
text: index; font.pointSize: 30
anchors.centerIn: parent
}
}
}
}
}

基于锚(anchors)的布局
除了前面讲解的定位器外,Qt Quick还提供了一种基于锚的概念来进行项目布局的方法。每一个项目都可以认为有一组无形的“锚线”:left、horizontalCenter、right、top、verticalCenter、baseline和bottom,如图所示。图中没有显示baseline,它是一条假想的线,文本坐落在这条线上,对于没有文本的项目,它与top相同。

7条锚线分别对应了Item项目中的anchors属性组的相关属性。因为Qt Quick中所有可视项目都继承自Item,所以所有可视项目都可以使用锚进行布局。Qt Quick的锚定系统允许不同项目的锚线之间建立关系
示例:
Item {
width: 250; height: 300
Rectangle{
id: rect1; x:10; y:20
width: 100; height: 100; color: "lightgrey"
Text { text: "rect1"; anchors.centerIn: parent }
}
Rectangle{
id: rect2
width: 100; height: 100; color: "black"
anchors.left: rect1.right
Text { text: "rect2"; color: "white"; anchors.centerIn: parent }
}
}

锚边距和偏移
锚定系统也允许为一个项目的锚指定边距(margin)和偏移(offset)。边距指定了项目锚到外边界的空间量,而偏移则允许使用中心锚线进行定位。一个项目可以通过leftMargin、rightMargin、topMargin和bottomMargin独立指定锚边距,如图所示。也可以使用anchor.margins同时为4个边指定相同的边距。锚偏移可以使用horizontalCenterOffset、verticalCenterOffset和baselineOffset来指定。
Rectangle { id: rect1; ... }
Rectangle {
id: rect2;
anchors.left: rect1.right; anchors.leftMargin: 5;
anchors.top: rect1.top
...
}

布局管理器
Qt Quick布局管理器是一组用于在用户界面中排列项目的类型。与前面讲到的定位器不同,布局管理器不仅进行布局,而且会改变项目的大小,所以更适用于需要改变用户界面大小的应用。因为布局管理器也是继承自Item,所以它们可以嵌套。Qt Quick布局管理器与Qt Widgets应用中的布局管理器很相似。
Note
Qt Quick Layouts模块在Qt 5.1中引入,使用时需要进行导入:import QtQuick.Layouts
Qt Quick布局管理器主要包括:
- RowLayout
- ColumnLayout
- GridLayout
- StackLayout
Qt Quick Layouts拥有下面几个主要特色:
- 项目的对齐方式可以使用Layout.alignment属性指定,主要有:QtAlignLeft、QtAlignHCenter、QtAlignRight、QtAlignTop、QtAlignVCenter、QtAlignBottom、Qt::AlignBaseline。
- 可变大小的项目可以使用Layout.fillWidth和Layout.fillHeight属性指定,当将其值设置为true时会根据约束条件变宽或变高。
- 大小约束可以通过Layout.minimumWidth、Layout.preferredWidth和Layout.maximumWidth属性(另外还有相对height的类似属性)指定。
- 间距可以通过spacing、rowSpacing和columnSpacing属性指定。
除了上面所述的这些特色,在GridLayout中还添加了如下特色:
- 网格中的坐标可以通过Layout.row和Layout.column指定。
- 自动网格坐标同时使用了flow、rows、column属性。
- 行或列的跨度可以通过Layout.rowSpan和Layout.columnSpan属性来指定。
大小约束
要想使一个项目可以通过布局管理器调整大小,需要指定其最小宽高(minimumWidth和minimumHeight)、最佳宽高(preferredWidth和preferredHeight)和最大宽高(maximumWidth和maximumHeight),并将对应的Layout.fillWidth或Layout.fillHeight设置为true。
RowLayout {
id: layout; anchors.fill: parent; spacing: 6
Rectangle {
color: 'lightgrey'
Layout.fillWidth: true; Layout.minimumWidth: 50
Layout.preferredWidth: 100; Layout.maximumWidth: 300
Layout.minimumHeight: 150
Text {
anchors.centerIn: parent
text: parent.width + 'x' + parent.height
}
}
Rectangle {
color: 'black'
Layout.fillWidth: true; Layout.minimumWidth: 100
Layout.preferredWidth: 200; Layout.preferredHeight: 100
Text {
anchors.centerIn: parent; color: "white"
text: parent.width + 'x' + parent.height
}
}
}

有效的最佳(preferred)属性的值,可能来自几个候选属性。要决定有效的最佳属性,会对这些候选属性以下面的顺序进行查询,使用第一个有效的值。
- Layout.preferredWidth或Layout.preferredHeight
- implicitWidth或implicitHeight
- width或height
一个项目可以仅指定Layout.preferredWidth而不指定Layout.preferredHeight,此时,有效的最佳高度会从implicitHeight或最终的height中选取。
为了将布局管理器与窗口进行关联,可以为布局管理器添加锚anchors.fill,确保布局管理器能够跟随窗口一起改变大小。
布局管理器的大小约束可以用来确保窗口大小不会超过约束条件,还可以将布局管理器的约束设置到窗口项目的minimumWidth、minimumHeight、maxmumWidth和maximumHeight等属性。
StackLayout
StackLayout栈布局管理器可以管理多个项目,但只能显示一个项目。可以通过currentIndex属性来设置当前显示的项目,索引号对应布局管理器中子项目的顺序,从0开始。另外,StackLayout还包含index和isCurrentItem等附加属性。
Window {
width: 640; height: 480; visible: true
StackLayout {
id: layout; anchors.fill: parent; currentIndex: 1
Rectangle {
color: 'teal'; implicitWidth: 200; implicitHeight: 200
}
Rectangle {
color: 'plum'; implicitWidth: 300; implicitHeight: 200
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (layout.currentIndex === 1)
layout.currentIndex = 0;
else
layout.currentIndex = 1
}
}
}

正文内容已启用复制保护,代码块与行内代码仍支持复制。
读者留言
还没有评论,来发表第一条评论吧。