绝对定位(直接在像素级指定各组件的位置与大小)有一些缺点:
1、对于某些平台,窗口部件的大小可能并不合适(无法自适应父窗口的变化)
2、位置与大小需要人工计算,难以维护
布局管理器
提供相关类对界面组件进行布局管理
使用布局管理器:
1、任意容器类的组件都可以指定布局管理器
2、能够自动排列窗口中的界面组件
3、窗口变化后自动更新界面组件的大小
此处组件1与组件2拥有相同的父对象(QWidget对象)
布局管理器特性:
1、统一布局管理器中的所有组件拥有相同的父组件(为使用管理器的组件)
2、设置布局管理器的同时隐式的指定了父子关系
布局管理器的抽象基类QLayout
1、 通过继承QLayout实现了功能各异且互补的预定义布局管理器(QBoxLayout、QGridLayout、QFormLayout、QStackedLayout)
2、 布局管理器之间可以相互嵌套,形成更加复杂的布局方式(QStackedLayout内不能直接嵌套其他布局管理器)
3、通过继承自QLayout类可以根据需要自定义布局管理器(需要继承自QLayout)
0)、定义一个数据结构作为类成员,数据结构用于储存要被布局管理的项,每个项都是一个QLayoutItem对象
1)、重写
virtual void | addItem ( QLayoutItem * item ) = 0 |
如何添加一个项到布局管理器
2)、重写
virtual void | setGeometry ( const QRect & r ) |
如何控制布局管理器的布局
3)、重写
virtual QSize | sizeHint () const = 0 |
布局管理器的首选大小
4)、重写
virtual QLayoutItem * | itemAt ( int index ) const = 0 |
如何遍历布局管理器
5)、重写
virtual QLayoutItem * | takeAt ( int index ) = 0 |
如何删除布局管理器中的项
6)、重写
virtual QSize | minimumSize () const |
项最小大小
4、需要注意的是,布局管理器不是界面组件,而是界面组件的定位策略
QBoxLayout布局管理器
以水平(QHBoxLayout)或垂直(QVBoxLayout)的方式管理界面组件
布局方式:
水平布局管理器例子:
QHBoxLayout*layout = new QHBoxLayout();
TestBtn1.setText("Test Button 1");
TestBtn1.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); //设置TestBtn1被布局时的大小策略
TestBtn1.setMinimumSize(160, 30);
TestBtn2.setText("Test Button 2");
TestBtn2.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn2.setMinimumSize(160, 30);
TestBtn3.setText("Test Button 3");
TestBtn3.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn3.setMinimumSize(160, 30);
TestBtn4.setText("Test Button 4");
TestBtn4.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn4.setMinimumSize(160, 30);
layout->setSpacing(30); //各个BUTTON组件之间的间隔
layout->addWidget(&TestBtn1);
layout->addWidget(&TestBtn2);
layout->addWidget(&TestBtn3);
layout->addWidget(&TestBtn4);
setLayout(layout); //别忘了使用布局管理器
嵌套布局(垂直嵌套水平)的例子:
QHBoxLayout*hLayout1 = new QHBoxLayout(); //构造水平布局管理器
QHBoxLayout* hLayout2 = new QHBoxLayout();
QVBoxLayout* vLayout = new QVBoxLayout(); //构造垂直布局管理器
TestBtn1.setText("Test Button 1");
TestBtn1.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn1.setMinimumSize(160, 30);
TestBtn2.setText("Test Button 2");
TestBtn2.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn2.setMinimumSize(160, 30);
hLayout1->setSpacing(10); //指定布局管理器hLayout1管理的组件之间的间隔
hLayout1->addWidget(&TestBtn1);
hLayout1->addWidget(&TestBtn2);
TestBtn3.setText("Test Button 3");
TestBtn3.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn3.setMinimumSize(160, 30);
TestBtn4.setText("Test Button 4");
TestBtn4.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TestBtn4.setMinimumSize(160, 30);
hLayout2->setSpacing(10);
hLayout2->addWidget(&TestBtn3);
hLayout2->addWidget(&TestBtn4);
vLayout->setSpacing(10); //设置布局管理器vLayout管理的布局(水平布局管理器hLayout1与hLayout2)间间隔
vLayout->addLayout(hLayout1); //向布局管理器中添加布局(嵌套布局管理器的方式)
vLayout->addLayout(hLayout2);
setLayout(vLayout); //设置布局管理器布局当前对象
嵌套之后是这样的
QBoxLayout与QGridLayout可以设置布局管理器伸展时的比例系数
1、默认情况下以等比例的方式更新组件大小
2、可以自定义组件大小更新时的比例系数
3、比例系数决定组件大小的相对变化
注:由于组件的初始大小是独立于布局管理器设置的,因此不能保证组件的大小始终符合比例系数
QBoxLayout中比例系数设置函数
void | setStretch ( int index, int stretch ) |
bool | setStretchFactor ( QWidget * widget, int stretch ) |
bool | setStretchFactor ( QLayout * layout, int stretch ) |
网格布局管理器(QGridLayout)
以网格(二维)的方式管理界面组件
QGridLayout中比例系数:
void | setColumnStretch ( int column, int stretch ) |
void | setRowStretch (int row,int stretch ) |
例子:
QGridLayout*layout = new QGridLayout(); //构造一个网格布局
//……..
layout->setSpacing(10);
layout->addWidget(&TestBtn1, 0, 0); //向网格布局对象中0行0列的位置添加组件
layout->addWidget(&TestBtn2, 0, 1);
layout->addWidget(&TestBtn3, 1, 0);
layout->addWidget(&TestBtn4, 1, 1);
layout->setRowStretch(0, 1); //设置0行随窗口伸展时的系数
layout->setRowStretch(1, 3);
layout->setColumnStretch(0, 1); //设置0列随窗口伸展时的系数
layout->setColumnStretch(1, 3);
setLayout(layout);
QGridLayout中的组件可以根据需要跨越多个网格
void QGridLayout::addWidget ( QWidget * widget,int fromRow, int fromColumn, int rowSpan,int columnSpan,Qt::Alignment alignment =0 )
例子:
QGridLayout*layout = new QGridLayout();
//……
layout->setSpacing(10);
//在网格布局器的第0行,0列处开始,添加占用2行,1列的组件TestBtn1
layout->addWidget(&TestBtn1, 0, 0, 2, 1);
layout->addWidget(&TestBtn2,0, 1, 2, 1);
//第0行,第1行被TestBtn1占用,所以只能从第2行开始
layout->addWidget(&TestBtn3, 2, 0, 1, 2);
layout->addWidget(&TestBtn4, 3, 0, 1, 2);
表单布局管理器(QFormLayout)
1、以表单的方式管理界面组件
2、表单布局中的标签与组件是相互对应的关系
QFormLayout成员:
添加组件
void | addRow ( QWidget * label, QWidget * field ) |
void | addRow ( QWidget * label, QLayout * field ) |
void | addRow ( const QString & labelText, QWidget * field ) |
void | addRow ( const QString & labelText, QLayout * field ) |
void | addRow ( QWidget * widget ) |
void | addRow ( QLayout * layout ) |
控制样式
void | setRowWrapPolicy ( RowWrapPolicy policy ) |
void | setLabelAlignment (Qt::Alignment alignment ) |
例子:
QLineEdit*nameEdit = new QLineEdit();
QLineEdit* mailEdit = new QLineEdit();
QLineEdit* addrEdit = new QLineEdit();
QFormLayout* layout = new QFormLayout(); //构建一个表单布局管理器
layout->addRow("Name:", nameEdit); //使用指定的 label "Name:"与组件nameEdit向布局管理器中添加新行
layout->addRow("Email:", mailEdit);
layout->addRow("Address:", addrEdit);
layout->setRowWrapPolicy(QFormLayout::WrapLongRows); //设置组件排布方式
layout->setLabelAlignment(Qt::AlignRight); //设置标签的对齐方式
layout->setSpacing(10);
setLayout(layout); //设置布局
setWindowTitle("FTP");
栈式布局管理器(QStackedLayout):
1、所有组件在垂直屏幕的方向上被管理
2、每次只有一个组件会显示在屏幕上
3、只有顶层的组件会被显示
栈式布局管理器特点:
组件大小一致且充满父组件的显示区域
不能直接嵌套其他布局管理器
能够自由切换需要显示的组件
每次能且仅能显示一个组件
QStackedLayout的几个成员:
int | addWidget ( QWidget * widget ) |
QWidget * | currentWidget () const |
void | setCurrentIndex ( int index ) |
int | currentIndex () const |
由于栈式布局管理器的特点(不能直接嵌套其他布局管理器),要形成复杂的布局就必须借用中间组件
例子:
Widget::Widget(QWidget *parent) :QWidget(parent),
TestBtn1(this), TestBtn2(this), TestBtn3(this), TestBtn4(this)
{
initControl();
}
void Widget::initControl()
{
QStackedLayout* sLayout = new QStackedLayout(); //创建栈布局管理器
QHBoxLayout*hLayout = new QHBoxLayout(); //创建水平布局管理器
//创建对象widget使用水平布局管理器,以便栈布局管理器间接嵌套水平布局管理器
QWidget* widget = new QWidget();
QTimer* timer = new QTimer(this);
TestBtn1.setText("1st Button");
TestBtn2.setText("2rd Button");
TestBtn3.setText("3th Button");
TestBtn4.setText("Test Button 4");
TestBtn2.setParent(widget); //由于往布局管理器中添加组件时也会更改父组件(将被添加的子组件的父对象设置为使用布局管理器的对象,组件只能以组件作为父对象)
TestBtn3.setParent(widget); //所以此处不用修改父组件亦可
hLayout->addWidget(&TestBtn2); //将组件TestBtn2加入布局管理器hLayout
hLayout->addWidget(&TestBtn3);
widget->setLayout(hLayout); //在组件widget中使用布局管理器hLayout
sLayout->addWidget(&TestBtn1); // 0 //将组件TestBtn1加入到栈布局管理器中,同时组件TestBtn1为下标0组件
sLayout->addWidget(widget); // 1 //将组件widget加入到布局管理器中,下标1,下标被布局管理器内部记录,不用额外使用变量
sLayout->addWidget(&TestBtn4); // 2
sLayout->setCurrentIndex(0); //设置当前下标值为0,即使下标为0的组件在当前显示
setLayout(sLayout); //使用布局管理器
connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
timer->start(2000); //启动定时器并设定触发信号时间间隔为2s(2000ms)
}
void Widget::timerTimeout()
{
QStackedLayout* sLayout = dynamic_cast<QStackedLayout*>(layout());
if( sLayout != NULL )
{//currentIndex函数返回布局管理器中当前组件的下标 count函数获取布局管理器中组件数量
int index = (sLayout->currentIndex() + 1) % sLayout->count(); //防止下标越界
sLayout->setCurrentIndex(index); //显示下标值为index的组件
}
}
Widget::~Widget()
{
}
声明:
此文根据 狄泰学院唐老师的《QT实验分析教程》创作,并根据自身理解对其进行了少许的扩展