总结了部分常用了基本控件之后,本篇笔记对Qt的布局管理进行总结。Qt自带一系列简单而强大的布局管理工具,以自动在窗体中排布控件。这极大的方便了开发人员管理GUI控件,达到几乎强迫症级别的优美排布,尤其是在适应不同分辨率界面的时候。
所有的QWidget子类都可以用layout管理他们的控件,用QWidget::setLayout()函数既可以为这个widget设置layout。如果一个Layout被这样用于一个widget,那么它将具备以下功能:
1.放置(设置位置)子窗体
2.适应于windows的合理默认大小
3.适应于windows的合理最小窗口
4.大小变化控制
5.当内容发生变化时的自动更新,包括:
文字大小,文本或其他子窗体的内容;
显示或隐藏一个子窗体
重新移动子窗体
Qt的layout类非常简单实用。一方面可以作为源码写在具体的代码里,另一方面也可以在用QDesigner可视化设计时应用他们。这些类有:
类名 |
描述 |
QGraphicsAnchor |
Represents an anchor between two items in a QGraphicsAnchorLayout |
QGraphicsAnchorLayout |
Layout where one can anchor widgets together in Graphics View |
QBoxLayout |
Lines up child widgets horizontally or vertically |
QHBoxLayout |
Lines up widgets horizontally |
QVBoxLayout |
Lines up widgets vertically |
QFormLayout |
Manages forms of input widgets and their associated labels |
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 |
最简单的布局方式是水平,垂直,栅格和表单布局工具:QHBoxLayout, QVBoxLayout, QGridLayout, 和 QFormLayout.这些类都继承于QLayout基类。稍微复杂点的,可能需要上述几个布局的混合使用。
QHBoxLayout把子控件水平排布,类似于:
QVBoxLayout把子控件垂直排布,类似于:
QGridLayout把子控件以二维栅格的形式排布,类似于:
QFormLayout把子控件以两列表单形式排布,类似于:
水平或垂直布局控制:
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();
栅格布局控制:
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();
上面的代码中按钮被分成了两列,button3的addWidget方法有5个参数,1表示开始行,0表示开始列,1表示持续行数,2表示持续列数。所以,button3布满了第二行。
表单布局控制:
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();
上述代码可以看到,QFormLayout为每行添加两个控件,通常是把一个label和一个LineEdit放在一行,并且LineEdit会成为该label的buddy。上述是将button和LineEdit放在一行中。
注意,在new一个Layout对象时,不需要为它指定父对象,它会自动在创建布局时认领父对象。另外,这些子控件终究也不是Layout的子对象,他们的父对象依然还是上层widget。我们也可以用addLayout()函数来嵌套Layout,内部的Layout则会变成被插入的Layout的子对象。
当为一个window执行setLayout()时,layout将按照以下步骤工作:
1.所有的子控件会通过调用QWidget::sizePolicy() 和QWidget::sizeHint()函数初始化一些列空间出来。
2.如果存在有伸缩的控件,这时则重新分配空间以适应他们的拉伸系数。
3.如果存在控件的伸缩系数被置为0,在没有其他控件干涉的前提下,它会获取更多的空间,这种size policy被称作Expanding。
4.如果存在控件的空间比其设置的最小尺寸(通过接口函数设置的最小值或是初始化的最小值)还小,那么此时为他们分配这个最小值。(控件如果设置了伸缩系数则不是必须有尺寸最小值)。
5.如果存在控件的空间比其设置的最大尺寸还要大,则此时重新为它分配所需的最大尺寸空间。(同样,控件如果设置了伸缩系数则不是必须有尺寸最大值)。
通常来说,控件没有伸缩系数,他们是被QWidget::sizePolicy()或者最小尺寸值更大的值限制,从而分配其在layout中的空间。而伸缩系数是用来设置控件到控件之间的空间的大小的。如果三个水平排布的控件通过QHBoxLayout控制,默认排布如下:
如果我们为每个控件都应用了伸缩系数,那么他们就会按系数比例分布,当然永远不会小于他们的最小尺寸:
如果你设计了一个自定义控件,应该也要实现它的layout属性。如果自定义控件使用了某种Qt的Layout,自然不必多说。如果自定义控件没有子控件,后者用了手动的布局,可以通过以下方法实现一些可视化效果:
1.重写QWidget::sizeHint()函数以返回默认的初始化尺寸;
2.重写QWidget::minimumSizeHint()函数以返回默认的最小值尺寸;
3.调用QWidget::setSizePolicy() 函数以设置控件所需空间。
4.调用QWidget::updateGeometry()当默认尺寸要求、最小尺寸要求或空间需求发生变化时。
5.重写QWidget::heightForWidth()函数以实现默认高度决定于控件宽度值。
也可以自定义布局。
可以像上述方法自定义控件,然后重写QWidget::resizeEvent()来计算所需的空间进而调用setGeometry()函数进行子控件位置尺寸更改。另一个更好的方法是通过继承QLayout编写自己的布局管理类。编写派生类时,需要实现以下方法:
1.一个保存items的数据结构,每一个item都是QLayoutItem对象,通过用QList来实现。
2.addItem(), 如何向layout中添加item.
3.setGeometry(), 如何实现item.
4.sizeHint(),默认尺寸.
5.itemAt(),如何遍历布局item.
6.takeAt(), 如何从Layout中删除某个item.
7.大多数情况下,也需要实现minimumSize().
一个例子:
//card.h
#ifndef CARD_H
#define CARD_H
#include
#include
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 list;
};
#endif
//card.cpp
//#include "card.h"
int CardLayout::count() const
{
// QList::size() returns the number of QLayoutItems in the list
return list.size();
}
QLayoutItem *CardLayout::itemAt(int idx) const
{
// QList::value() performs index checking, and returns 0 if we are
// outside the valid range
return list.value(idx);
}
QLayoutItem *CardLayout::takeAt(int idx)
{
// QList::take does not do index checking
return idx >= 0 && idx < list.size() ? list.takeAt(idx) : 0;
}
void CardLayout::addItem(QLayoutItem *item)
{
list.append(item);
}
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());
}
1.在label控件中使用富文本时,会导致父窗体布局出现问题。
2.如果父窗体设置了QLayout::FreeResize mode,它将不会适应更小窗口中的内容布局。
3.如果一个窗体要被用于QDockWidget或者QScrollArea,请在QDockWidget::setWidget() and QScrollArea::setWidget()之前设置他们自己的布局,不然这个窗体将不会出现。
按照惯例,最后上几个例子:https://download.csdn.net/download/bjtuwayne/12027192
1.基本布局
2.边框布局
3.动态布局
4.流动布局