大道至简,在 Qt5 C++入门教程的这一部分,,我们将讨论窗口部件(widget)的布局管理。我们会提到 QHBoxLayout(水平布局管理器)、QVBoxLayout(垂直布局管理器)、QFormLayout(表单布局管理器)和 QGridLayout(网格布局管理器)。
一个典型的应用程序由各种窗口部件组成。这些窗口部件被放置在布局中。程序员必须管理应用程序的布局。在 Qt5 中,我们有两种选择:
- 绝对定位
- 布局管理器
- 绝对定位
程序员以像素为单位指定每个窗口部件的位置和大小。当我们使用绝对定位时,必须了解以下几点:
- 当我们调整窗口大小时,窗口部件的大小和位置不会改变。
- 应用程序在不同平台上的显示效果可能不同(通常效果不佳)。
- 在应用程序中更改字体可能会破坏布局。
- 如果我们决定更改布局,必须完全重新设计布局,这既繁琐又耗时。
可能存在一些情况适合使用绝对定位。但在大多数实际应用程序中,程序员会使用布局管理器。
- absolute.cpp
#include <QApplication>
#include <QDesktopWidget>
#include <QTextEdit>
class Absolute : public QWidget {
public:
Absolute(QWidget *parent = nullptr);
};
Absolute::Absolute(QWidget *parent)
: QWidget(parent) {
auto *ledit = new QTextEdit(this);
ledit->setGeometry(5, 5, 200, 150);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Absolute window;
window.setWindowTitle("Absolute");
window.show();
return app.exec();
}
setGeometry 方法用于以绝对坐标的方式在窗口上定位窗口部件。
auto *edit = new QTextEdit(this);
ledit->setGeometry(5, 5, 200, 150);
我们创建了一个 QTextEdit 窗口部件,并手动对其进行定位。setGeometry 方法有两个作用:它将窗口部件定位到绝对坐标,并调整窗口部件的大小。
- Qt5 QVBoxLayout
QVBoxLayout 类将窗口部件垂直排列。使用 addWidget 方法将窗口部件添加到布局中。 - vertical_box.h
#pragma once
#include <QWidget>
class VerticalBox : public QWidget {
public:
VerticalBox(QWidget *parent = nullptr);
};
- vertical_box.cpp
#include <QVBoxLayout>
#include <QPushButton>
#include "vertical_box.h"
VerticalBox::VerticalBox(QWidget *parent)
: QWidget(parent) {
auto *vbox = new QVBoxLayout(this);
vbox->setSpacing(1);
auto *settings = new QPushButton("Settings", this);
settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
auto *accounts = new QPushButton("Accounts", this);
accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
auto *loans = new QPushButton("Loans", this);
loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
auto *cash = new QPushButton("Cash", this);
cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
auto *debts = new QPushButton("Debts", this);
debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
vbox->addWidget(settings);
vbox->addWidget(accounts);
vbox->addWidget(loans);
vbox->addWidget(cash);
vbox->addWidget(debts);
setLayout(vbox);
}
在我们的示例中,我们有一个垂直布局管理器。我们向其中放入了五个按钮。我们使所有按钮在两个方向上都可扩展。
auto *vbox = new QVBoxLayout(this);
vbox->setSpacing(1);
我们创建了 QVBoxLayout,并在子窗口部件之间设置了 1 像素的间距。
auto *settings = new QPushButton("Settings", this);
settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
我们创建了一个按钮,并为其设置了大小策略。子窗口部件由布局管理器管理。默认情况下,按钮在水平方向上扩展,在垂直方向上具有固定大小。如果我们想更改它,就设置一个新的大小策略。在我们的例子中,按钮在两个方向上都可扩展。
vbox->addWidget(settings);
vbox->addWidget(accounts);
...
我们使用 addWidget 方法将子窗口部件添加到布局管理器中。
setLayout(vbox);
我们为窗口设置了 QVBoxLayout 管理器。
- main.cpp
#include <QApplication>
#include "vertical_box.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
VerticalBox window;
window.resize(240, 230);
window.setWindowTitle("VerticalBox");
window.show();
return app.exec();
}
- 按钮
在下面的示例中,我们在窗口的客户区显示两个按钮。它们将被放置在窗口的右下角。
buttons.h
#pragma once
#include <QWidget>
#include <QPushButton>
class Buttons : public QWidget {
public:
Buttons(QWidget *parent = nullptr);
private:
QPushButton *okBtn;
QPushButton *applyBtn;
};
buttons.cpp
#include <QVBoxLayout>
#include <QHBoxLayout>
#include "buttons.h"
Buttons::Buttons(QWidget *parent)
: QWidget(parent) {
auto *vbox = new QVBoxLayout(this);
auto *hbox = new QHBoxLayout();
okBtn = new QPushButton("OK", this);
applyBtn = new QPushButton("Apply", this);
hbox->addWidget(okBtn, 1, Qt::AlignRight);
hbox->addWidget(applyBtn, 0);
vbox->addStretch(1);
vbox->addLayout(hbox);
}
假设我们想在窗口的右下角放置两个按钮。
auto *vbox = new QVBoxLayout(this);
auto *hbox = new QHBoxLayout();
我们创建了两个盒式布局管理器:一个垂直布局管理器和一个水平布局管理器。
okBtn = new QPushButton("OK", this);
applyBtn = new QPushButton("Apply", this);
我们创建了两个按钮。
hbox->addWidget(okBtn, 1, Qt::AlignRight);
hbox->addWidget(applyBtn, 0);
使用 addWidget 方法将按钮放置在水平布局管理器中。这些按钮右对齐。第一个参数是子窗口部件。第二个参数是拉伸因子,最后一个参数是对齐方式。通过将 “OK” 按钮的拉伸因子设置为 1,我们为它在窗口的左侧到右侧之间分配了空间。该窗口部件不会扩展到分配给它的所有空间。最后,Qt::AlignRight 常量将窗口部件对齐到分配空间的右侧。
vbox->addStretch(1);
vbox->addLayout(hbox);
我们通过调用 addStretch 方法在垂直盒式布局中放入一个空的、可扩展的空间。然后,我们将水平盒式布局添加到垂直盒式布局中。
main.cpp
#include <QApplication>
#include "buttons.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Buttons window;
window.resize(290, 170);
window.setWindowTitle("Buttons");
window.show();
return app.exec();
}
- Qt5 嵌套布局
下面示例的目的是展示布局管理器可以组合使用。通过组合即使是简单的布局,我们也可以创建复杂的对话框或窗口。为了实现布局嵌套,我们使用 addLayout 方法。
nesting.h
#pragma once
#include <QWidget>
class Layouts : public QWidget {
public:
Layouts(QWidget *parent = nullptr);
};
nesting.cpp
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include "nesting.h"
Layouts::Layouts(QWidget *parent)
: QWidget(parent) {
auto *vbox = new QVBoxLayout();
auto *hbox = new QHBoxLayout(this);
auto *lw = new QListWidget(this);
lw->addItem("The Omen");
lw->addItem("The Exorcist");
lw->addItem("Notes on a scandal");
lw->addItem("Fargo");
lw->addItem("Capote");
auto *add = new QPushButton("Add", this);
auto *rename = new QPushButton("Rename", this);
auto *remove = new QPushButton("Remove", this);
auto *removeall = new QPushButton("Remove All", this);
vbox->setSpacing(3);
vbox->addStretch(1);
vbox->addWidget(add);
vbox->addWidget(rename);
vbox->addWidget(remove);
vbox->addWidget(removeall);
vbox->addStretch(1);
hbox->addWidget(lw);
hbox->addSpacing(15);
hbox->addLayout(vbox);
setLayout(hbox);
}
在这个示例中,我们创建了一个窗口,它由四个按钮和一个列表部件组成。这些按钮被分组在一个垂直列中,并放置在列表部件的右侧。如果我们调整窗口大小,列表部件也会随之调整大小。
auto *vbox = new QVBoxLayout();
QVBoxLayout 是按钮所在的列。
auto *hbox = new QHBoxLayout(this);
QHBoxLayout 是这些窗口部件的基础布局。
auto *lw = new QListWidget(this);
lw->addItem("The Omen");
lw->addItem("The Exorcist");
lw->addItem("Notes on a scandal");
lw->addItem("Fargo");
lw->addItem("Capote");
创建了 QListWidget。
auto *add = new QPushButton("Add", this);
auto *rename = new QPushButton("Rename", this);
auto *remove = new QPushButton("Remove", this);
auto *removeall = new QPushButton("Remove All", this);
在这里我们创建了四个按钮。
vbox->setSpacing(3);
vbox->addStretch(1);
vbox->addWidget(add);
vbox->addWidget(rename);
vbox->addWidget(remove);
vbox->addWidget(removeall);
vbox->addStretch(1);
创建了包含四个按钮的垂直盒式布局。我们在按钮之间设置了一些小间距。注意,我们在垂直盒式布局的顶部和底部都添加了拉伸因子。这样,按钮就会在垂直方向上居中。
hbox->addWidget(lw);
hbox->addSpacing(15);
hbox->addLayout(vbox);
列表部件和按钮的垂直盒式布局被放置到水平盒式布局中。addLayout 方法用于将一个布局添加到另一个布局中。
setLayout(hbox);
我们为父窗口设置了基础布局。
main.cpp
#include <QApplication>
#include "nesting.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Layouts window;
window.setWindowTitle("Layouts");
window.show();
return app.exec();
}
- Qt5 FormLayout
QFormLayout 是一个简单的布局管理器,用于管理输入窗口部件及其相关标签组成的表单。它以两列的形式排列其子窗口部件。左列由标签组成,右列由像 QLineEdit 或 QSpinBox 这样的输入窗口部件组成。
form.h
#pragma once
#include <QWidget>
class FormEx : public QWidget {
public:
FormEx(QWidget *parent = nullptr);
};
form.cpp
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include "form.h"
FormEx::FormEx(QWidget *parent)
: QWidget(parent) {
auto *nameEdit = new QLineEdit(this);
auto *addrEdit = new QLineEdit(this);
auto *occpEdit = new QLineEdit(this);
auto *formLayout = new QFormLayout;
formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
formLayout->addRow("Name:", nameEdit);
formLayout->addRow("Email:", addrEdit);
formLayout->addRow("Age:", occpEdit);
setLayout(formLayout);
}
这个示例创建了一个由三个标签和三个行编辑框组成的表单。
auto *formLayout = new QFormLayout;
创建了一个 QFormLayout 的实例。
formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
使用 setLabelAlignment 方法,我们设置了标签窗口部件的对齐方式。
formLayout->addRow("Name:", nameEdit);
addRow 方法在表单布局的底部添加一个新行,并带有给定的标签和输入窗口部件。
main.cpp
#include <QApplication>
#include "form.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
FormEx window;
window.setWindowTitle("Form example");
window.show();
return app.exec();
}
- Qt5 QGridLayout
QGridLayout 将其窗口部件放置在一个网格中。它是一个功能强大的布局管理器。
calculator.h
#pragma once
#include <QWidget>
class Calculator : public QWidget {
public:
Calculator(QWidget *parent = nullptr);
};
calculator.cpp
#include <QGridLayout>
#include <QPushButton>
#include "calculator.h"
Calculator::Calculator(QWidget *parent)
: QWidget(parent) {
auto *grid = new QGridLayout(this);
grid->setSpacing(2);
QVector<QString> values({ "7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
});
int pos = 0;
for (int i=0; i<4; i++) {
for (int j=0; j<4; j++) {
auto *btn = new QPushButton(values[pos], this);
btn->setFixedSize(40, 40);
grid->addWidget(btn, i, j);
pos++;
}
}
setLayout(grid);
}
我们创建了一个计算器的框架。
auto *grid = new QGridLayout(this);
grid->setSpacing(2);
我们创建了网格布局,并在子窗口部件之间设置了 2 像素的间距。
QVector<QString> values({ "7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
});
这些是显示在按钮上的字符。
for (int i=0; i<4; i++) {
for (int j=0; j<4; j++) {
auto *btn = new QPushButton(values[pos], this);
btn->setFixedSize(40, 40);
grid->addWidget(btn, i, j);
pos++;
}
}
我们将十六个窗口部件放置到网格布局中。每个按钮都有固定的大小。
main.cpp
#include <QApplication>
#include "calculator.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Calculator window;
window.setWindowTitle("Calculator");
window.show();
return app.exec();
}