布局类

Qt 3.0.5

主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数

布局类

Qt的布局系统提供了一个规定子窗口部件布局的简单的和强有力的方式。

当你一旦规定了合理的布局,你就会获得如下利益:

  • 布置子窗口部件。
  • 最高层窗口部件可感知的默认大小。
  • 最高层窗口部件可感知的最小大小。
  • 调整大小的处理。
  • 当内容改变的时候自动更新:
    • 字体大小、文本或者子窗口部件的其它内容。
    • 隐藏或者显示子窗口部件。
    • 移去一些子窗口部件。

Qt的布局类使用手写的C++代码设计的,所以它们很容易被理解和使用。

手写的布局代码的不利是当你在试验设计一个视窗并且你不得不为每一个改变进行编译、连接和运行这样一个循环的时候它不是非常方便的。我们的解决方案是Qt设计器,一种可以快速建立和很容易地试验布局和为你生成C++布局代码地图形用户界面可视化设计工具。

布局窗口部件

给你的窗口部件一个好的布局的最好的方法是使用这些布局窗口部件:QHBoxQVBoxQGrid。一个布局窗口部件自已自动地把它们的子窗口部件按照它们被构造地顺序进行布局。为了生成更复杂的布局,你可以嵌入一个布局窗口部件到其它的。

  • 一个QHBox把它的子窗口部件从左到右排列在一个水平的行中。

    Horizontal box with five child widgets

  • 一个QVBox把它的子窗口部件从上到下排列在一个竖直的列中。

    Vertical box with five child widgets

  • 一个QGrid把它的子窗口部件排列在一个二维的网格中。 你可以规定网格可以有多少列,当前面的一行已经排满,它将会从左到右填充新的一行。网格是被确定的,当这个窗口部件被改变大小的时候,它的子窗口部件不会跑到其它的行。

Two-column grid with five child widgets

上述演示的表格是由下面这些代码生成的:

    QGrid *mainGrid = new QGrid( 2 ); // 一个2*n的网格
    new QLabel( "One", mainGrid );
    new QLabel( "Two", mainGrid );
    new QLabel( "Three", mainGrid );
    new QLabel( "Four", mainGrid );
    new QLabel( "Five", mainGrid );

你可以通过对子窗口部件调用QWidget::setMinimumSize()或QWidget::setFixedSize()来调节布局。

QLayout

当你添加一个窗口部件到一个布局中,布局过程工作如下:

  1. 所有的窗口部件将最初根据它们的QWidget::sizePolicy()而被分配到一定空间中。
  2. 如果任何一个窗口部件有一个伸展因素设置,而且数值大于零,那么它们就会被根据它们的伸展因素的比例分配空间。
  3. 如果任何一个窗口部件有一个伸展因素设置而且数值为零,那么只有当其它窗口部件不再需要空间的时候才会得到更多的空间。在这当中,空间会首先被根据延展大小策略分配给窗口部件。
  4. 任何窗口部件被分配的空间的大小如果小于它们的最小大小(或者是在没有规定最小大小时的最小大小的提示),它们就会被按它们所需要的最小大小分配空间。(如果窗口部件的伸展因素是它们的决定因素的情况下,它们不必有最小大小或者最小大小的提示。)
  5. 任何窗口部件被分配的空间的大小如果大于它们的最大大小,它们就会被按它们所需要的最大大小分配空间。(如果窗口部件的伸展因素是它们的决定因素的情况下,它们不必有最大大小。)

伸展因素

窗口部件通常是在没有伸展因素设置的情况下被生成的。当它们被布置到一个布局中时,窗口部件会被根据它们的QWidget::sizePolicy()或者它们的最小大小的提示中大的那一个分配给整个空间的一部分。伸展因素是用来根据窗口部件互相的比例来改变它们所被分配的空间。

如果你使用一个QHBox来布置没有伸展参数设置的三个窗口部件,我们就会得到像下面这样的布局:

3 widgets in a row

如果我们给每个窗口部件设置一个伸展因素,它们就会被按比例布置(但是不能小于最小大小的提示),举例来说:

3 stretch factored widgets in a row

QLayout

如果你需要控制布局,可以使用QLayout的子类。Qt中包括的布局类有QGridLayoutQBoxLayout。(QHBoxLayoutQVBoxLayoutQBoxLayout很简单的子类,它们可以更简单地使用并且使得代码更容易阅读。)

当你使用一个布局,你将不得不把每个子窗口部件不仅放到它们的父窗口部件中(这个已经在构造函数中完成),而且要把它们放到它们的布局中(典型地通过调用addWidget()完成)。这个方法中,你可以对每个窗口部件给出布局参数,规定像对齐方式、伸展因素和位置这样的属性。

下面的代码生成一个和上面的那个网格类似,但有一些改进的网格:

    QWidget *main = new QWidget;

    // 生成一个1*1的网格;它可以自动展开
    QGridLayout *grid = new QGridLayout( main, 1, 1 );

    // 根据(行, 列)编址来添加前四个窗口部件
    grid->addWidget( new QLabel( "One", main ), 0, 0 );
    grid->addWidget( new QLabel( "Two", main ), 0, 1 );
    grid->addWidget( new QLabel( "Three", main ), 1, 0 );
    grid->addWidget( new QLabel( "Four", main ), 1, 1 );

    // 添加最后一个窗口部件到第2行,并占用第0列和第1列,中间对齐
    grid->addMultiCellWidget( new QLabel( "Five", main ), 2, 2, 0, 1,
                              Qt::AlignCenter );

    // 设置第0列和第1列的宽度比例为2:3
    grid->setColStretch( 0, 2 );
    grid->setColStretch( 1, 3 );

你可以通过把父布局作为子布局构造函数的一个参数来把布局添加到另一个布局中。

    QWidget *main = new QWidget;
    QLineEdit *field = new QLineEdit( main );
    QPushButton *ok = new QPushButton( "OK", main );
    QPushButton *cancel = new QPushButton( "Cancel", main );
    QLabel *label = new QLabel( "Write once, compile everywhere.", main );

    // 一个窗口部件中的布局
    QVBoxLayout *vbox = new QVBoxLayout( main );
    vbox->addWidget( label );
    vbox->addWidget( field );

    // 一个布局中的布局
    QHBoxLayout *buttons = new QHBoxLayout( vbox );
    buttons->addWidget( ok );
    buttons->addWidget( cancel );
如果你对默认位置感到不满意,你可以不使用父布局参数来生成一个布局然后通过调用addLayout()把这个布局插入到其它布局中。

自定义布局

如果内置的布局类不够充分的话,你可以定义你自己的布局类。你必须创建QLayout的一个子类来处理重新定义大小和大小的计算,也可以使用QGLayoutIterator的子类来迭代你的布局类。

如果需要更深的描述,请看Custom Layout页。

在布局中自定义窗口部件

当你创建自己的窗口部件类的时候,你也应该传递它的布局属性。如果这个窗口部件有一个QLayout,这样的话就已经被处理了。如果这个窗口部件不包括任何子窗口部件,或者使用自定义布局,你需要重新实现下面这些QWidget的成员函数:

只要大小提示、最小大小提示或者大小策略发生改变,都要调用QWidget::updateGeometry()。这会引起布局的重新计算。对updateGeometry()的多重调用只会引起一次重新计算。

如果你的窗口部件的优先选用的高度依赖于它的实际宽度(比如一个自动断词的标签),在sizePolicy()中设置hasHeightForWidth()标记,并且重新实现QWidget::heightForWidth()。

即使你实现了heightForWidth(),提供一个好的sizeHint()仍然是必需的。sizeHint()提供了窗口部件的优先选用的宽度,并且它经常被不支持heightForWidth()的QLayout的子类使用(QGridLayoutQBoxLayout都支持)。

当你实现这些函数并想获得更多的制导的时候,请查看和你的新窗口部件所需要的布局存在于Qt类中最接近的布局的实现。

人工布局

如果你要生成一种特殊的布局,你也可以按上面的描述来生成一个自定义窗口部件。重新实现QWidget::resizeEvent()来计算所需要分配的大小并且给每一个子窗口部件调用setGeometry()。

当布局需要重新计算的时候,窗口部件会得到一个类型LayoutHint的事件。重新实现被通知LayoutHint事件的QWidget::event()。


Copyright © 2002 Trolltech Trademarks 译者:Cavendish
Qt 3.0.5版