淘先锋技术网

首页 1 2 3 4 5 6 7

Border Layout 例子将展示如何创建自定义布局管理器。

Border Layout是通过设置自定义的West, North, South, East, Center的方位来设置布局。通过上一篇博客已经有过一个例子简单介绍了如何自定义布局管理器。通过本例子可以学到跟多的细节。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Window window;
#if defined(Q_OS_SYMBIAN)
    window.showMaximized();
#else
    window.show();
#endif
    return app.exec();
}
class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private:
    QLabel *createLabel(const QString &text);   // 创建标签函数
};
Window::Window()
{
    QTextBrowser *centralWidget = new QTextBrowser;
    centralWidget->setPlainText(tr("Central widget"));

    BorderLayout *layout = new BorderLayout;  // 使用自定义的BorderLayout
    layout->addWidget(centralWidget, BorderLayout::Center);         // 添加窗体项到布局
    layout->addWidget(createLabel("North"), BorderLayout::North);   // 创建label,并添加到布局
    layout->addWidget(createLabel("West"), BorderLayout::West);
    layout->addWidget(createLabel("East 1"), BorderLayout::East);
    layout->addWidget(createLabel("East 2") , BorderLayout::East);
    layout->addWidget(createLabel("South"), BorderLayout::South);
    setLayout(layout);      // 设置布局

    setWindowTitle(tr("Border Layout"));  // 窗体标题
}

QLabel *Window::createLabel(const QString &text)   // 创建标签label
{
    QLabel *label = new QLabel(text);
    label->setFrameStyle(QFrame::Box | QFrame::Raised);  // 定义标签风格
    return label;
}
// 自定义布局管理器,此BorderLayout是按照枚举的位置来摆放窗体
class BorderLayout : public QLayout
{
public:
    enum Position { West, North, South, East, Center }; // 枚举位置

    BorderLayout(QWidget *parent, int margin = 0, int spacing = -1); //
    BorderLayout(int spacing = -1);
    ~BorderLayout();

    void addItem(QLayoutItem *item);
    void addWidget(QWidget *widget, Position position);
    Qt::Orientations expandingDirections() const;
    bool hasHeightForWidth() const;
    int count() const;
    QLayoutItem *itemAt(int index) const;
    QSize minimumSize() const;
    void setGeometry(const QRect &rect);
    QSize sizeHint() const;
    QLayoutItem *takeAt(int index);

    void add(QLayoutItem *item, Position position);

private:
    struct ItemWrapper      // 定义项数据结构
    {
        ItemWrapper(QLayoutItem *i, Position p) {
            item = i;
            position = p;
        }
    QLayoutItem *item;   // 项指针
    Position position;   // 项位置
    };

    enum SizeType { MinimumSize, SizeHint };       // 定义枚举类SizeType
    QSize calculateSize(SizeType sizeType) const;  // 计算大小

    QList<ItemWrapper *> list;      // 保存所有加入布局管理器的项
};
BorderLayout::BorderLayout(QWidget *parent, int margin, int spacing)
    : QLayout(parent)
{
    // 设置边框宽度的,此函数被废弃,为了保存兼容,这里还能使用
    // 可以用setContentsMargins(),不过参数为QMargins类型
    setMargin(margin);
    // 设置空白,-1为默认间距
    setSpacing(spacing);
}

// 以特定空白大小构造
BorderLayout::BorderLayout(int spacing)
{
    setSpacing(spacing);
}


BorderLayout::~BorderLayout()
{
    QLayoutItem *l;
    while ((l = takeAt(0)))  // 析构时移除所有项
        delete l;
}

// 添加部件,以默认的西位置
void BorderLayout::addItem(QLayoutItem *item)
{
    add(item, West);
}

// 添加部件,根据位置
void BorderLayout::addWidget(QWidget *widget, Position position)
{
    add(new QWidgetItem(widget), position);
}

// 拓展方向
Qt::Orientations BorderLayout::expandingDirections() const
{
    return Qt::Horizontal | Qt::Vertical;
}

// 是否高有实际宽决定
bool BorderLayout::hasHeightForWidth() const
{
    return false;
}

// 返回项数目
int BorderLayout::count() const
{
    return list.size();
}

// 返回索引项
QLayoutItem *BorderLayout::itemAt(int index) const
{
    ItemWrapper *wrapper = list.value(index);
    if (wrapper)
        return wrapper->item;
    else
        return 0;
}

// 最小大小
QSize BorderLayout::minimumSize() const
{
    return calculateSize(MinimumSize);  // 设置计算大小
}

// 正式布置布局
void BorderLayout::setGeometry(const QRect &rect)
{
    ItemWrapper *center = 0;
    int eastWidth = 0;
    int westWidth = 0;
    int northHeight = 0;
    int southHeight = 0;
    int centerHeight = 0;
    int i;

    QLayout::setGeometry(rect);

    for (i = 0; i < list.size(); ++i) {        // 遍历所有项,找出高相关的项,并设置布局
        ItemWrapper *wrapper = list.at(i);
        QLayoutItem *item = wrapper->item;
        Position position = wrapper->position;

        // 根据当前项的位置去画布局
        // 画完一项后要将高累加
        if (position == North) {
            item->setGeometry(QRect(rect.x(), northHeight, rect.width(),
                                    item->sizeHint().height()));

            northHeight += item->geometry().height() + spacing();
        } else if (position == South) {
            item->setGeometry(QRect(item->geometry().x(),
                                    item->geometry().y(), rect.width(),
                                    item->sizeHint().height()));

            southHeight += item->geometry().height() + spacing();

            item->setGeometry(QRect(rect.x(),
                              rect.y() + rect.height() - southHeight + spacing(),
                              item->geometry().width(),
                              item->geometry().height()));
        } else if (position == Center) {
            center = wrapper;
        }
    }

    // 所剩下的中心位置的高
    centerHeight = rect.height() - northHeight - southHeight;

    for (i = 0; i < list.size(); ++i) {   // 遍历所有项,找出高相关的项,并设置布局
        ItemWrapper *wrapper = list.at(i);
        QLayoutItem *item = wrapper->item;
        Position position = wrapper->position;

        if (position == West) {
            item->setGeometry(QRect(rect.x() + westWidth, northHeight,
                                    item->sizeHint().width(), centerHeight));

            westWidth += item->geometry().width() + spacing();
        } else if (position == East) {
            item->setGeometry(QRect(item->geometry().x(), item->geometry().y(),
                                    item->sizeHint().width(), centerHeight));

            eastWidth += item->geometry().width() + spacing();

            item->setGeometry(QRect(
                              rect.x() + rect.width() - eastWidth + spacing(),
                              northHeight, item->geometry().width(),
                              item->geometry().height()));
        }
    }

    // 设置中心部件的值
    if (center)
        center->item->setGeometry(QRect(westWidth, northHeight,
                                        rect.width() - eastWidth - westWidth,  // 剩下的宽空间
                                        centerHeight));
}

QSize BorderLayout::sizeHint() const
{
    return calculateSize(SizeHint);
}

// 移除索引项
QLayoutItem *BorderLayout::takeAt(int index)
{
    if (index >= 0 && index < list.size()) {
        ItemWrapper *layoutStruct = list.takeAt(index);  // 从存储List中取出
        return layoutStruct->item;
    }
    return 0;
}

// 添加项
void BorderLayout::add(QLayoutItem *item, Position position)
{
    list.append(new ItemWrapper(item, position));   // 添加到存储容器中
}

// 计算大小
QSize BorderLayout::calculateSize(SizeType sizeType) const
{
    QSize totalSize;

    for (int i = 0; i < list.size(); ++i) {         // 遍历所有项
        ItemWrapper *wrapper = list.at(i);
        Position position = wrapper->position;      // 取出当前项的位置
        QSize itemSize;

        if (sizeType == MinimumSize)                // 大小类型为MinimumSize
            itemSize = wrapper->item->minimumSize();// 返回当前项的最小大小
        else // (sizeType == SizeHint)              // 类型为SizeHint
            itemSize = wrapper->item->sizeHint();   // 返回当前项sizeHint大小

        if (position == North || position == South || position == Center)
            totalSize.rheight() += itemSize.height();   // 统计高

        if (position == West || position == East || position == Center)
            totalSize.rwidth() += itemSize.width();     // 统计宽
    }
    return totalSize;
}