原文: http://zetcode.com/gui/qt4/layoutmanagement/

在 Qt4 编程教程的这一部分中,我们将讨论小部件的布局管理。

典型的应用由各种小部件组成。 这些小部件放置在布局内。 程序员必须管理应用的布局。 在 Qt4 中,我们有两个选择:

  • 绝对定位
  • 布局管理器

绝对定位

程序员以像素为单位指定每个小部件的位置和大小。 当使用绝对定位时,我们必须了解几件事。

  • 如果我们调整窗口大小,则小部件的大小和位置不会改变。
  • 在各种平台上,应用看起来有所不同(通常很差)。
  • 在我们的应用中更改字体可能会破坏布局。
  • 如果决定更改布局,则必须完全重做布局,这既繁琐又耗时。

在某些情况下,我们可能会使用绝对定位。 但是大多数情况下,在实际程序中,程序员使用布局管理器。

absolute.cpp

  1. #include <QApplication>
  2. #include <QDesktopWidget>
  3. #include <QTextEdit>
  4. class Absolute : public QWidget {
  5. public:
  6. Absolute(QWidget *parent = 0);
  7. };
  8. Absolute::Absolute(QWidget *parent)
  9. : QWidget(parent) {
  10. QTextEdit *ledit = new QTextEdit(this);
  11. ledit->setGeometry(5, 5, 200, 150);
  12. }
  13. int main(int argc, char *argv[]) {
  14. QApplication app(argc, argv);
  15. Absolute window;
  16. window.setWindowTitle("Absolute");
  17. window.show();
  18. return app.exec();
  19. }

setGeometry()方法用于以绝对坐标将窗口小部件放置在窗口上。

  1. QTextEdit *edit = new QTextEdit(this);
  2. edit->setGeometry(5, 5, 200, 150);

我们创建一个QTextEdit小部件并手动定位。 setGeometry()方法有两件事:将窗口小部件定位到绝对坐标并调整窗口小部件的大小。

Qt4 中的布局管理 - 图1

图:调整大小前

Qt4 中的布局管理 - 图2

图:调整大小后

QVBoxLayout

QVBoxLayout类垂直排列小部件。 使用addWidget()方法将小部件添加到布局。

verticalbox.h

  1. #pragma once
  2. #include <QWidget>
  3. class VerticalBox : public QWidget {
  4. public:
  5. VerticalBox(QWidget *parent = 0);
  6. };

头文件。

verticalbox.cpp

  1. #include "verticalbox.h"
  2. #include <QVBoxLayout>
  3. #include <QPushButton>
  4. VerticalBox::VerticalBox(QWidget *parent)
  5. : QWidget(parent) {
  6. QVBoxLayout *vbox = new QVBoxLayout(this);
  7. vbox->setSpacing(1);
  8. QPushButton *settings = new QPushButton("Settings", this);
  9. settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  10. QPushButton *accounts = new QPushButton("Accounts", this);
  11. accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  12. QPushButton *loans = new QPushButton("Loans", this);
  13. loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  14. QPushButton *cash = new QPushButton("Cash", this);
  15. cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  16. QPushButton *debts = new QPushButton("Debts", this);
  17. debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  18. vbox->addWidget(settings);
  19. vbox->addWidget(accounts);
  20. vbox->addWidget(loans);
  21. vbox->addWidget(cash);
  22. vbox->addWidget(debts);
  23. setLayout(vbox);
  24. }

在我们的示例中,我们有一个垂直布局管理器。 我们在其中放入了五个按钮。 我们使所有按钮都可以在两个方向上展开。

  1. QVBoxLayout *vbox = new QVBoxLayout(this);
  2. vbox->setSpacing(1);

我们创建QVBoxLayout并在子窗口小部件之间设置 1px 的间距。

  1. QPushButton *settings = new QPushButton("Settings", this);
  2. settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

我们创建一个按钮并为其设置大小策略。 子窗口小部件由布局管理器管理。 默认情况下,按钮水平扩展,垂直方向固定大小。 如果要更改它,我们将设置一个新的大小策略。 在我们的例子中,按钮可以向两个方向扩展。

  1. vbox->addWidget(settings);
  2. vbox->addWidget(accounts);
  3. ...

我们使用addWidget()方法将子窗口小部件添加到布局管理器。

  1. setLayout(vbox);

我们为窗口设置QVBoxLayout管理器。

main.cpp

  1. #include "verticalbox.h"
  2. #include <QApplication>
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. VerticalBox window;
  6. window.resize(240, 230);
  7. window.setWindowTitle("VerticalBox");
  8. window.show();
  9. return app.exec();
  10. }

主文件。

Qt4 中的布局管理 - 图3

图:QVBoxLayout

按钮

在下面的示例中,我们在窗口的客户区域上显示两个按钮。 它们将位于窗口的右下角。

buttons.h

  1. #pragma once
  2. #include <QWidget>
  3. #include <QPushButton>
  4. class Buttons : public QWidget {
  5. public:
  6. Buttons(QWidget *parent = 0);
  7. private:
  8. QPushButton *okBtn;
  9. QPushButton *applyBtn;
  10. };

头文件。

buttons.cpp

  1. #include "buttons.h"
  2. #include <QVBoxLayout>
  3. #include <QHBoxLayout>
  4. Buttons::Buttons(QWidget *parent)
  5. : QWidget(parent) {
  6. QVBoxLayout *vbox = new QVBoxLayout(this);
  7. QHBoxLayout *hbox = new QHBoxLayout();
  8. okBtn = new QPushButton("OK", this);
  9. applyBtn = new QPushButton("Apply", this);
  10. hbox->addWidget(okBtn, 1, Qt::AlignRight);
  11. hbox->addWidget(applyBtn, 0);
  12. vbox->addStretch(1);
  13. vbox->addLayout(hbox);
  14. }

假设我们想在窗口的右下角有两个按钮。

  1. QVBoxLayout *vbox = new QVBoxLayout(this);
  2. QHBoxLayout *hbox = new QHBoxLayout();

我们创建了两个框布局管理器:一个垂直框布局管理器和一个水平框布局管理器。

  1. okBtn = new QPushButton("OK", this);
  2. applyBtn = new QPushButton("Apply", this);

我们创建两个按钮。

  1. hbox->addWidget(okBtn, 1, Qt::AlignRight);
  2. hbox->addWidget(applyBtn, 0);

这些按钮位于水平布局管理器中。 使用addWidget()方法。 这些按钮右对齐。 第一个参数是子窗口小部件。 第二个参数是拉伸因子,最后一个参数是对齐。 通过将“确定”按钮的拉伸因子设置为 1,我们在窗口的左侧到右侧留出一定的空间。 窗口小部件不会扩展到分配给它的所有空间。 最后,Qt::AlignRight常数将小部件对齐到分配空间的右侧。

  1. vbox->addStretch(1);
  2. vbox->addLayout(hbox);

通过调用addStretch()方法,我们在垂直框中放入了一个可扩展的空白区域。 然后,将水平框布局添加到垂直框布局。

main.cpp

  1. #include <QApplication>
  2. #include "buttons.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Buttons window;
  6. window.resize(290, 170);
  7. window.setWindowTitle("Buttons");
  8. window.show();
  9. return app.exec();
  10. }

主文件。

Qt4 中的布局管理 - 图4

图:按钮

嵌套布局

以下示例的目的是说明可以合并布局管理器。 通过甚至简单布局的组合,我们可以创建复杂的对话框或窗口。 要嵌套布局,我们利用addLayout()方法。

layouts.h

  1. #pragma once
  2. #include <QWidget>
  3. class Layouts : public QWidget {
  4. public:
  5. Layouts(QWidget *parent = 0);
  6. };

头文件。

layouts.cpp

  1. #include <QVBoxLayout>
  2. #include <QPushButton>
  3. #include <QListWidget>
  4. #include "layouts.h"
  5. Layouts::Layouts(QWidget *parent)
  6. : QWidget(parent) {
  7. QVBoxLayout *vbox = new QVBoxLayout();
  8. QHBoxLayout *hbox = new QHBoxLayout(this);
  9. QListWidget *lw = new QListWidget(this);
  10. lw->addItem("The Omen");
  11. lw->addItem("The Exorcist");
  12. lw->addItem("Notes on a scandal");
  13. lw->addItem("Fargo");
  14. lw->addItem("Capote");
  15. QPushButton *add = new QPushButton("Add", this);
  16. QPushButton *rename = new QPushButton("Rename", this);
  17. QPushButton *remove = new QPushButton("Remove", this);
  18. QPushButton *removeall = new QPushButton("Remove All", this);
  19. vbox->setSpacing(3);
  20. vbox->addStretch(1);
  21. vbox->addWidget(add);
  22. vbox->addWidget(rename);
  23. vbox->addWidget(remove);
  24. vbox->addWidget(removeall);
  25. vbox->addStretch(1);
  26. hbox->addWidget(lw);
  27. hbox->addSpacing(15);
  28. hbox->addLayout(vbox);
  29. setLayout(hbox);
  30. }

在示例中,我们创建一个窗口,该窗口由四个按钮和一个列表小部件组成。 这些按钮被分组在一个垂直列中,并位于列表小部件的右侧。 如果我们调整窗口的大小,列表小部件也将被调整大小。

  1. QVBoxLayout *vbox = new QVBoxLayout();

QVBoxLayout将是按钮的列。

  1. QHBoxLayout *hbox = new QHBoxLayout(this);

QHBoxLayout将是小部件的基本布局。

  1. QListWidget *lw = new QListWidget(this);
  2. lw->addItem("The Omen");
  3. lw->addItem("The Exorcist");
  4. lw->addItem("Notes on a scandal");
  5. lw->addItem("Fargo");
  6. lw->addItem("Capote");

QListWidget已创建。

  1. QPushButton *add = new QPushButton("Add", this);
  2. QPushButton *rename = new QPushButton("Rename", this);
  3. QPushButton *remove = new QPushButton("Remove", this);
  4. QPushButton *removeall = new QPushButton("Remove All", this);

在这里,我们创建四个按钮。

  1. vbox->setSpacing(3);
  2. vbox->addStretch(1);
  3. vbox->addWidget(add);
  4. vbox->addWidget(rename);
  5. vbox->addWidget(remove);
  6. vbox->addWidget(removeall);
  7. vbox->addStretch(1);

创建带有四个按钮的垂直框。 我们在按钮之间留了一些空间。 注意,我们在垂直框的顶部和底部添加了一个拉伸因子。 这样,按钮可以垂直居中。

  1. hbox->addWidget(lw);
  2. hbox->addSpacing(15);
  3. hbox->addLayout(vbox);

列表小部件和按钮的垂直框放置在水平框布局中。 addLayout()方法用于将一个布局添加到另一个布局。

  1. setLayout(hbox);

我们为父窗口设置基本布局。

main.cpp

  1. #include <QApplication>
  2. #include "layouts.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Layouts window;
  6. window.setWindowTitle("Layouts");
  7. window.show();
  8. return app.exec();
  9. }

主文件。

Qt4 中的布局管理 - 图5

图:布局

QFormLayout

QFormLayout是一个简单的布局管理器,用于管理输入窗口小部件及其相关标签的形式。 它以两列的形式布置其子项。 左列包含标签,右列包含输入窗口小部件,例如QLineEditQSpinBox

form.h

  1. #pragma once
  2. #include <QWidget>
  3. class FormEx : public QWidget {
  4. public:
  5. FormEx(QWidget *parent = 0);
  6. };

这是标题文件管理器。

form.cpp

  1. #include "form.h"
  2. #include <QFormLayout>
  3. #include <QLabel>
  4. #include <QLineEdit>
  5. FormEx::FormEx(QWidget *parent)
  6. : QWidget(parent) {
  7. QLineEdit *nameEdit = new QLineEdit(this);
  8. QLineEdit *addrEdit = new QLineEdit(this);
  9. QLineEdit *occpEdit = new QLineEdit(this);
  10. QFormLayout *formLayout = new QFormLayout;
  11. formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
  12. formLayout->addRow("Name:", nameEdit);
  13. formLayout->addRow("Email:", addrEdit);
  14. formLayout->addRow("Age:", occpEdit);
  15. setLayout(formLayout);
  16. }

该示例创建一个包含三个标签和三个行编辑的表单。

  1. QFormLayout *formLayout = new QFormLayout;

创建QFormLayout的实例。

  1. formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);

使用setLabelAlignment()方法,我们设置标签小部件的对齐方式。

  1. formLayout->addRow("Name:", nameEdit);

addRow()方法将新行添加到表单布局的底部,并带有给定的标签和输入小部件。

main.cpp

  1. #include <QApplication>
  2. #include "form.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. FormEx window;
  6. window.setWindowTitle("Form example");
  7. window.show();
  8. return app.exec();
  9. }

主文件。

Qt4 中的布局管理 - 图6

图:简单 form

QGridLayout

QGridLayout将其小部件放置在网格中。 它是一个功能强大的布局管理器。

calculator.h

  1. #pragma once
  2. #include <QWidget>
  3. class Calculator : public QWidget {
  4. public:
  5. Calculator(QWidget *parent = 0);
  6. };

这是头文件。

calculator.cpp

  1. #include <QGridLayout>
  2. #include <QPushButton>
  3. #include "calculator.h"
  4. Calculator::Calculator(QWidget *parent)
  5. : QWidget(parent) {
  6. QGridLayout *grid = new QGridLayout(this);
  7. grid->setSpacing(2);
  8. QList<QString> values({ "7", "8", "9", "/",
  9. "4", "5", "6", "*",
  10. "1", "2", "3", "-",
  11. "0", ".", "=", "+"
  12. });
  13. int pos = 0;
  14. for (int i=0; i<4; i++) {
  15. for (int j=0; j<4; j++) {
  16. QPushButton *btn = new QPushButton(values[pos], this);
  17. btn->setFixedSize(40, 40);
  18. grid->addWidget(btn, i, j);
  19. pos++;
  20. }
  21. }
  22. setLayout(grid);
  23. }

我们创建计算器的骨架。

  1. QGridLayout *grid = new QGridLayout(this);
  2. grid->setSpacing(2);

我们创建网格布局,并在子小部件之间设置 2px 的空间。

  1. QList<QString> values({ "7", "8", "9", "/",
  2. "4", "5", "6", "*",
  3. "1", "2", "3", "-",
  4. "0", ".", "=", "+"
  5. });

这些是按钮上显示的字符。

  1. for (int i=0; i<4; i++) {
  2. for (int j=0; j<4; j++) {
  3. QPushButton *btn = new QPushButton(values[pos], this);
  4. btn->setFixedSize(40, 40);
  5. grid->addWidget(btn, i, j);
  6. pos++;
  7. }
  8. }

我们将十六个小部件放置到网格布局中。 每个按钮将具有固定的大小。

main.cpp

  1. #include <QApplication>
  2. #include "calculator.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Calculator window;
  6. window.move(300, 300);
  7. window.setWindowTitle("Calculator");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

Qt4 中的布局管理 - 图7

图:QGridLayout

回顾

在本章的下一个示例中,我们使用QGridLayout管理器创建一个更复杂的窗口。

review.h

  1. #pragma once
  2. #include <QWidget>
  3. class Review : public QWidget {
  4. public:
  5. Review(QWidget *parent = 0);
  6. };

头文件。

review.cpp

  1. #include "review.h"
  2. #include <QGridLayout>
  3. #include <QLabel>
  4. #include <QLineEdit>
  5. #include <QTextEdit>
  6. Review::Review(QWidget *parent)
  7. : QWidget(parent) {
  8. QGridLayout *grid = new QGridLayout(this);
  9. grid->setVerticalSpacing(15);
  10. grid->setHorizontalSpacing(10);
  11. QLabel *title = new QLabel("Title:", this);
  12. grid->addWidget(title, 0, 0, 1, 1);
  13. title->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
  14. QLineEdit *edt1 = new QLineEdit(this);
  15. grid->addWidget(edt1, 0, 1, 1, 1);
  16. QLabel *author = new QLabel("Author:", this);
  17. grid->addWidget(author, 1, 0, 1, 1);
  18. author->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
  19. QLineEdit *edt2 = new QLineEdit(this);
  20. grid->addWidget(edt2, 1, 1, 1, 1);
  21. QLabel *review = new QLabel("Review:", this);
  22. grid->addWidget(review, 2, 0, 1, 1);
  23. review->setAlignment(Qt::AlignRight | Qt::AlignTop);
  24. QTextEdit *te = new QTextEdit(this);
  25. grid->addWidget(te, 2, 1, 3, 1);
  26. setLayout(grid);
  27. }

该代码创建了一个窗口,可用于输入作者,书名和书评。

  1. QGridLayout *grid = new QGridLayout(this);

QGridLayout管理器已创建。

  1. grid->setVerticalSpacing(15);
  2. grid->setHorizontalSpacing(10);

我们使用setVerticalSpacing()方法添加垂直间距,并使用setHorizontalSpacing()方法添加水平间距。

  1. QLabel *title = new QLabel("Title", this);
  2. grid->addWidget(title, 0, 0, 1, 1);

这些代码行创建一个标签小部件,并将其放入网格布局中。 addWidget()方法具有五个参数。 第一个参数是子窗口小部件,在本例中为标签。 接下来的两个参数是放置标签的网格中的行和列。 最后,最后一个参数是rowspancolspan。 这些参数指定当前窗口小部件将跨越多少行。 在我们的情况下,标签将仅跨越一列和一行。

  1. title->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

setAlignment()方法将标题标签对准其单元格。 在水平方向上,它是右对齐的。 在垂直方向上,它居中。

  1. QTextEdit *te = new QTextEdit(this);
  2. grid->addWidget(te, 2, 1, 3, 1);

QTextEdit小部件位于第三行和第二列; 它跨越三行一列。

main.cpp

  1. #include <QApplication>
  2. #include "review.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Review window;
  6. window.setWindowTitle("Review");
  7. window.show();
  8. return app.exec();
  9. }

主文件。

Qt4 中的布局管理 - 图8

图:回顾

Qt4 教程的这一部分专门用于布局管理。