前段时间研究了qml后,返回来用qt,发现qt的布局和qml区别挺大(qml比起QWidget下的布局要好用太多了)。写点东西记录一下
1.可能出现的演示控件:
QPushButton: 默认的 HorizontalSizePolicy: QSizePolicy::Minimum ; VerticalSizePolicy: QSizePolicy::Fixed
QComboBox:默认的 HorizontalSizePolicy: QSizePolicy::Preferred ; VerticalSizePolicy: QSizePolicy::Fixed
QLineEdit:默认的HorizontalSizePolicy: QSizePolicy::Expanding ; VerticalSizePolicy: QSizePolicy::Fixed
QTextEdit:默认的HorizontalSizePolicy: QSizePolicy::Expanding ; VerticalSizePolicy: QSizePolicy::Expanding
QTextBrower:默认的HorizontalSizePolicy: QSizePolicy::Expanding ; VerticalSizePolicy: QSizePolicy::Expanding
先从简单的开始。
2.QSizePolicy需要结合布局一起使用,否则不会生效。
main.cpp如下,默认的。
#include "widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp也是默认如下:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
简单例子,随便通过ui文件拜访几个控件,不加布局,重新编译后如下。
预览如下图:
此时,没有添加任何布局QLayout的子类,随便鼠标缩放Widget,我们放置的控件并无变化。
3.以QHBoxLayout为例,开始分析控件的QSizePolicy与布局之间的相互影响。这里我们用代码而不以ui拖拽来实现,便于后续分析。代码如下:
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
qDebug()<itemAt(i)->widget()->sizePolicy();
}
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QHBoxLayout *pHlayout;
};
#endif // WIDGET_H
简单说,就是将QPushButton,QComboBox,QLineEdit用QHBoxLayout进行水平排列。
qDebug()打印三者SizePolicy,这里我们只关注HorizontalSizePolicy进行分析。
QSizePolicy(horizontalPolicy = QSizePolicy::Minimum, verticalPolicy = QSizePolicy::Fixed)
QSizePolicy(horizontalPolicy = QSizePolicy::Preferred, verticalPolicy = QSizePolicy::Fixed)
QSizePolicy(horizontalPolicy = QSizePolicy::Expanding, verticalPolicy = QSizePolicy::Fixed)
这里可能有人对QSizePolicy不太了解,先进行一些解释,这里只解释文档当中关于Policy部分(enum,枚举类型)的内容。
QSizePolicy::Fixed,持有该Policy,那么窗口(widget)只接受缺省大小作为尺寸选择,因此无法进行放大或者缩小。
QSizePolicy::Minimum,持有该Policy,那么窗口将会以最小尺寸作为缺省大小,在此基础上(最小尺寸的基础上),可以放大,但是默认不会优先进行放大,只有在布局的其他部分不会放大的情况下,才会“不得已”、“不情愿”地进行放大。
QSizePolicy::Maximum,持有该Policy,那么窗口(widget)将会将最大尺寸作为缺省大小,在此基础上(最大尺寸的基础上),可以为布局中其他需要空间的控件进行缩小。简而言之,可以不“不主动”的进行缩小。
QSizePolicy::Preferred,持有该Policy,那么窗口会以一种“所谓的最佳尺寸”作为缺省大小(一般就是介于minimun和maximum之间某个尺寸大小),可以“被动式的”为布局中其他控件缩小以腾出空间给需要空间的控件,也可以放大,以占据多余的空间。
QSizePolicy::Expanding,持有该Policy,那么窗口会在布局中,以一种“所谓的最佳尺寸”作为缺省大小,然后会""“尽可能多地”、“主动地”占据占据多余的空间。
QSizePolicy::MinimumExpanding,持有该Policy,与上面的Expanding很像,唯一区别就是,在这里,缺省大小是最小尺寸,因此,以缺省大小(也就是最小尺寸)为标准,只能放大,而且是“主动地”,“尽可能多的”占据多余的空间。
QSizePolicy::Ignored,持有该Policy,忽视缺省尺寸( sizeHint( ) ),“主动地”,“尽可能多地”占据多余的空间。
回到主题,我们水平拉伸widget时,因为QLineEdit为Expanding,QComboBox为Preferred,QPushButton为Minimun,所以只有QLineEdit在此时会跟着拉伸。
我们将QComboBox的水平SizePolicy修改为Expanding,QLineEdit修改为Preferred,再拉伸如下:
可以看到,QLineEdit修改后为Preferred,由于多余的空间被QComboBox“主动的”的占据,QLineEdit并没有随widget拉伸而拉伸,而是保持尺寸不变
代码只是简单修改了widget.cpp,如下:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
combobox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
lineedit->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
qDebug()<itemAt(i)->widget()->sizePolicy();
}
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
简单的综上所述,只有结合布局,空间的QSizePolicy才能生效。
4.布局(以QHBoxLayout为例)会影响(或者说优先级高于)布局当中的控件的QSizePolicy。
这里引入QHBoxLayout的stretch属性,该属性表示拉伸因子。
查阅Qt文档的函数addWidget原型
void QBoxLayout::addWidget(QWidget *widget, int stretch = 0, Qt::Alignment alignment = ...)
Adds widget to the end of this box layout, with a stretch factor of stretch and alignment alignment.
The stretch factor applies only in the direction of the QBoxLayout, and is relative to the other boxes and widgets in this QBoxLayout. Widgets and boxes with higher stretch factors grow more.
If the stretch factor is 0 and nothing else in the QBoxLayout has a stretch factor greater than zero, the space is distributed according to the QWidget:sizePolicy() of each widget that's involved.
The alignment is specified by alignment. The default alignment is 0, which means that the widget fills the entire cell.
看蓝色部分,默认缺省stretch为0,如果QBoxLayout当中,没有控件的stretch的拉伸因子(stretch factor)大于0(其实就是就是指的全为0),那么空间分配根据QWidget:sizePolicy()来分配。
可以看出stretch就是一个可以绕过QSizePolicy进行空间分配的参数。我们先设置为stretch都为1,代码如下:
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
pHlayout->setStretch(i, 1); // 修改之处
qDebug()<itemAt(i)->widget()->sizePolicy();
}
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
可以看到,qDebug()打印的QSizePolicy没有变化,但是,水平方向上的空间,刚好按照stretch factor 1:1:1进行分配的。
而修改前,stretch factor为0:0:0,没有大于0的stretch factor,因此按照的是QSizePolicy进行分配的,如下图
代码如下:
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
// pHlayout->setStretch(i, 1);
qDebug()<itemAt(i)->widget()->sizePolicy();
}
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
那么对于stretch factor有大于0,且存在等于0的情况呢?
我们简单修改代码,将stretch factor修改为1:1:0
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
pHlayout->setStretch(i, 1);
qDebug()<itemAt(i)->widget()->sizePolicy();
}
pHlayout->setStretch(2,0);
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
演示图如下:
QPushButton与QComboBox保持1:1的宽度,与stretch factor一致,但是QLineEdit的stretch factor为0,默认show()以后,size为sizehint().进行拉伸,效果如下图:
可以看到QLineEdit虽然水平方向的SizePolicy为Expanding,但是在stretch factor为0的影响下,仍然保持的sizehint()大小,并没有跟着拉伸,反而是QPushButton和QComboBox 保持stretch factor 1:1的比例,等比例拉伸。
进行缩放时,效果如下图:
起初,QLineEdit的size保持sizehint(),未缩小,而是QPushButton和QCombobox1:1缩小,直到某一个缩小到minimunsize,无法再缩小,QLineEdit才开始“被动地”,“不情愿地”进行缩小,到minimum后,整个widget,无法再进行缩小。
5.若子widget的size大小,大于父widget的size大小,则多余的空间不会显示。这里是和qml不同之处
举个例子就是,在该QComboBox中,添加一个QPushButton,设置其最小size为(500,100).可以看到QpushButton没有显示完全。当然拉伸后可以显示出来。
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// ui->setupUi(this);
pHlayout = new QHBoxLayout();
QPushButton *btn1 = new QPushButton(this);
btn1->setText("btn1");
pHlayout->addWidget( btn1 );
QComboBox *combobox = new QComboBox(this);
pHlayout->addWidget( combobox );
QPushButton *btn2 = new QPushButton(combobox);
btn2->setMinimumSize(500,100);
QLineEdit *lineedit = new QLineEdit(this);
pHlayout->addWidget( lineedit );
setLayout( pHlayout );
for( int i = 0 ; i < pHlayout->count(); i++ ){
pHlayout->setStretch(i, 1);
qDebug()<itemAt(i)->widget()->sizePolicy();
}
pHlayout->setStretch(2,0);
}
Widget::~Widget()
{
delete ui;
if( pHlayout ){
delete pHlayout;
pHlayout = nullptr;
}
}
这里顺便提一句,如果是qml的话,子类是会显示完全的,即子窗口的size可以大于其父窗口的size,并完整显示出来。对于多余空间(与其他窗口有可能有重叠部分)这时候可以设置z属性,来决定谁显示在上层。即有一个图层的概念,窗口层叠时,图层在上(z值大的窗口)会显示出来。
6.顺便提一句QGridLayout,stretch factor的作用原理类似。默认addwidget函数,stretch factor为0,布局,拉伸,缩小,由SizePolicy决定,但是stretch factor不全为0时,规律和前文一致。读者可以通过setColumnStretch(),setRowStretch()函数进行验证