Qt 布局管理器

来源:互联网 发布:成都卧龙大数据创始人 编辑:程序博客网 时间:2024/05/02 21:27
关于布局管理器,它是Qt提供的自动安排子部件位置的东西,布局管理可以为我们节省很多对位置的调节工作。

基本的布局管理器有四种:Horizontal, Vertical, Grid, 和Form 布局管理器。这些在前面的例子都能看到是如何使用的。Qt的帮助文档里也有一个专门讲他们的例子:Basic Layouts QHBoxLayout, QVBoxLayout, QGridLayout,和QFormLayout 都继承自QLayoutLayouts通过调用addWidget或者addRow将窗口部件加入进去,QWidget及其子类都有一个setLayout的成员函数,可以将我们的布局管理器设置进去,这样就完成了布局。

 

 

widgets被加入到layout,它是如下工作的:

1. 所有的widgets将根据他们的QWidget::sizePolicy()QWidget::sizeHint()被初始化一定数量的空间。

2. 如果一个widgetstretch factors(值大于0)要设置,那么就会为他们分配这些空间。

3. 如果其他widgets都不在需要空间了,那么就会把剩余的空间都给那些stretch factors0widget。那些设置了QSizePolicy::Expanding的优先分配。

4. 那些设置了最小大小却还没有被分配到他们的最小值的widgets会被分配空间直到他们的最小值。

5. 那些设置了最大大小却分配空间多于了这个最大值的widgets会被分配空间直到他们的最大值。

 

当我们做自己的窗体部件类时,可能会需要和它的layout属性交流,如果这个部件有Qt的布局管理器中的一个,它就已经管理好了。如果这个部件没有任何子部件,或者使用的手动布局,你一通过下面的方法改变部件的行为:

1. 重新实现QWidget::sizeHint(),它返回一个设置的窗体大小。

2. 重新实现QWidget::minimumSizeHint(),它返回窗体最小的大小。

3. 调用QWidget::setSizePolicy(),指定需求的空间策略。

对于以上的那些改变,调用QWidget::updateGeometry(),它将会导致布局的重新计算,并更新窗体部件。连续调用多次这个函数,也只会导致一次布局重新计算。

如果你的窗体部件的高依赖他实际的宽(例如label的自动断句),在窗体部件的setSizePolicy()里设置setHeightForWidth(true),再重新实现QWidget::heightForWidth()

即使你实现了QWidget::heightForWidth(),提供一个合理的sizeHint()仍然是个好主意。

 

 

怎么写一个自定义的布局管理器?

一个供选择的方案是子类化QLayout,后面的两个例子:Border LayoutFlow Layout就为你展示了做法。

这里我们提供一个具体的例子:CardLayout类是受java布局管理器里同名类的灵感。

写自己的布局类,必须定义如下的内容:

1. 一个数据结构,存储处理要被布局管理的项,每个项都是QLayoutItem,在下面的例子里,我们将使用QList

2. addItem(),怎么增加一个项到布局管理器。

3. setGeometry(),怎么去实现布局管理器。

4. sizeHint(),布局管理器的首选大小。

5. itemAt(),怎么去遍历布局管理器。

6. takeAt(),怎么去从布局管理器中移除项。

在大多数情况你还需要实现minimumSize()。我们也可以参看QLayout(或其他子类)的源码来寻找灵感~


#ifndef CARD_H

#define CARD_H


#include <QtGui>

#include <QList>


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;

   QLayoutItem *count() const;

   QLayoutItem *itemAt(intconst;

   QLayoutItem *takeAt(int);

   void setGeometry(const QRect &rect);


private:

   QList<QLayoutItem*> list;

};

#endif

首先,我我们定义count()返回项的数量。

QLayoutItem *CardLayout::count() const

{

        // QList::size() returns the number of QLayoutItems in the list

    return list.size();

}

接下来定义两个函数,遍历布局管理器。itemAt(idx),返回第idx项,takeAt(idx)删除idx

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;

}

addItem()为布局管理器用默认放置策略添加项,必须被实现,它使用QLayout::add()QLayout的构造器使布局管理器作为父类。如果你的布局管理器需要使用其他策略,那不许提供其他的存取函数,例如使用行列号作为参数重载addItem(),addWidget(),addLayout()

void CardLayout::addItem(QLayoutItem *item)

{

    list.append(item);

}


析构时,将项都遍历删除掉。

CardLayout::~CardLayout()

{

     QLayoutItem *item;

     while ((item = takeAt(0)))

         delete item;

}

setGeometry()函数正式执行布局管理器。使用spacing()作为项之间的间距。

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;

    }

}

sizeHint()minimumSize() 的实现很类似,

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());

}