美文网首页Qt学习Qt
3.2 自定义布局管理器

3.2 自定义布局管理器

作者: 223480235e8e | 来源:发表于2018-04-26 13:26 被阅读26次

虽然标准布局可以帮助我们解决很多问题,但是有时候需要一些特殊的布局管理器来管理控件,所以我们需要制作一个自己的自定义布局管理器,本节小豆君就来给大家分享如何自定义布局管理器。

我们看到所有的标准布局都直接或间接继承于QLayout,那么我们的自定义布局也要继承QLayout。

QLayout继承于QObject和QLayoutItem,QLayoutItem是一个抽象类

一般的,在继承类中,我们重点要关注这个基类中的虚函数,下面列出了QLayout的虚函数:

//添加一个QLayoutItem,当调用addWidget时,也会调用此函数
//在布局中,每添加一个控件,就会相应的增加一个QLayoutItem,
//用来管理每个控件在布局中的大小策略
virtual void addItem(QLayoutItem *) = 0;

//用于计算每个子控件在父窗口中的位置和大小
virtual void setGeometry(const QRect&) Q_DECL_OVERRIDE;

//获取第index个QLayoutItem
virtual QLayoutItem *itemAt(int index) const = 0;

//出栈一个QLayoutItem,并且返回该QLayoutItem的指针
virtual QLayoutItem *takeAt(int index) = 0;

//返回QLayoutItem*的个数
virtual int count() const = 0;

//大小提示
//这个虚函数是属于QLayoutItem的接口
//关于大小提示的信息,可查看上一节标准布局管理器三中的布局原理
QSize   sizeHint() const;

3.2.1 金字塔布局

这个布局会在第一行放置一个控件,第二行放置两个控件。。。以此类推。

效果图:

image.png

好,直接上代码。

新建项目CustomLayout,类名为CustomLayout,基类为QWidget,不需要创建ui文件

customlayout.h

#ifndef CUSTOMLAYOUT_H
#define CUSTOMLAYOUT_H

#include <QWidget>

class CustomLayout : public QWidget
{
    Q_OBJECT

public:
    CustomLayout(QWidget *parent = 0);
    ~CustomLayout();
};

#endif // CUSTOMLAYOUT_H

customlayout.cpp

#include <QLabel>
#include "customlayout.h"
#include "pyramidlayout.h"

CustomLayout::CustomLayout(QWidget *parent)
    : QWidget(parent)
{
    PyramidLayout* layout = new PyramidLayout(this, 0, 0, 0);
    for (int i = 0; i < 15; ++i)
    {
        layout->addWidget(new QLabel(tr("       ")));
    }
    //再给我们的每个label加个颜色,看起来像一块儿砖
    setStyleSheet("QLabel{background-color:#ffc000;border:2px solid #ff3000}");
}

CustomLayout::~CustomLayout()
{
}

添加一个新C++类PyramidLayout

pyramidlayout.h

#ifndef PYRAMIDLAYOUT_H
#define PYRAMIDLAYOUT_H

#include <QLayout>
#include <QWidget>

class PyramidLayout: public QLayout
{
public:
    PyramidLayout(QWidget *parent, int margin = 9, 
                  int hSpacing = 9, int vSpacing = 9);
    PyramidLayout(int margin = 9, int hSpacing = 9, int vSpacing = 9);
    ~PyramidLayout();

    void addItem(QLayoutItem *item) override ;
    void setGeometry(const QRect &rect) override ;
    QLayoutItem *itemAt(int index) const override;
    QLayoutItem *takeAt(int index)  override;
    int count() const;

    QSize minimumSize() const override;
    QSize sizeHint() const override;
    int rowCount() const;

private:
    QList<QLayoutItem *> itemList;
    int m_hSpace;
    int m_vSpace;
};

#endif // PYRAMIDLAYOUT_H

pyramidlayout.cpp

#include <QtMath>
#include "pyramidlayout.h"

PyramidLayout::PyramidLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
    : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
    setContentsMargins(margin, margin, margin, margin);
}

PyramidLayout::PyramidLayout(int margin, int hSpacing, int vSpacing)
    : m_hSpace(hSpacing), m_vSpace(vSpacing)
{
    setContentsMargins(margin, margin, margin, margin);
}

PyramidLayout::~PyramidLayout()
{
    QLayoutItem *item;
    while ((item = takeAt(0)))
        delete item;
}

void PyramidLayout::addItem(QLayoutItem *item)
{
    itemList.append(item);
}

int PyramidLayout::count() const
{
    return itemList.size();
}

QSize PyramidLayout::minimumSize() const
{
    QLayoutItem *item = itemList.first();
    QSize size;
    if (item)
    {
        int r = rowCount();
        size.setWidth((item->sizeHint().width()+m_hSpace)*r- m_hSpace);
        size.setHeight((item->sizeHint().height()+m_vSpace)*r- m_vSpace);
    }

    size += QSize(2*margin(), 2*margin());
    return size;
}

QSize PyramidLayout::sizeHint() const
{
    return minimumSize();
}

//用于计算金字塔高度
int PyramidLayout::rowCount() const
{
    //(1+r)*r = cnt
    //r为金字塔高度,cnt为子控件总个数
    //求解一元二次方程
    double r = (qSqrt(1+8*count())-1)/2.;
    int ir = r;
    if (r > ir)
    {
        r = ir + 1;
    }
    return r;
}

QLayoutItem *PyramidLayout::itemAt(int index) const
{
    return itemList.value(index);
}

QLayoutItem *PyramidLayout::takeAt(int index)
{
    if (index >= 0 && index < itemList.size())
        return itemList.takeAt(index);
    else
        return 0;
}

void PyramidLayout::setGeometry(const QRect &rect)
{
    QLayout::setGeometry(rect);
    int left, top, right, bottom;
    getContentsMargins(&left, &top, &right, &bottom);
    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
    int x = 0;
    int y = 0;
    int cnt = count();

    //i表示行,j表示列,k表示第k个子控件
    for (int i = 0, k = 0; k < cnt; ++i)
    {
        for (int j = 0; j < i+1 && k < cnt; ++j, ++k)
        {
            QLayoutItem *item = itemList.at(k);
            QLayoutItem *aboveItem = 0;//每个控件的头上控件,用于定位item
            if (i == j)//最右侧控件
            {
                if (i == 0)//第一个控件,水平居中
                {
                    double r = rowCount();
                    x = effectiveRect.center().x() - item->sizeHint().width()/2;
                    y = effectiveRect.center().y()-r/2*item->sizeHint().height()-(r-1)/2*m_vSpace;
                }
                else//选择它头上的左侧控件作为头控件
                {
                    aboveItem = itemList.at(k-i-1);
                    x = aboveItem->geometry().right()-item->sizeHint().width()/2+m_hSpace/2;
                    y = aboveItem->geometry().bottom()+m_vSpace;
                }
            }
            else//选择它头上的右侧控件作为头控件
            {
                aboveItem = itemList.at(k-i);
                x = aboveItem->geometry().left()-item->sizeHint().width()/2-m_hSpace/2;
                y = aboveItem->geometry().bottom()+m_vSpace;
            }
            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
        }
    }
}

编译运行程序,如图:

image.png

改变窗口大小,金字塔也会居中显示。

总的来说,布局管理就是当父窗口的大小被改变时,将其中的每个子控件进行重新排放,至于如何排放,就在setGeometry中进行计算。你可以将每个子控件想象成积木,如何摆,那就看你心情了。

这个金字塔还可以使用Qt的绘图,第三方库制作,我们以后再讲。

好了,关于自定义布局的内容今天先讲到这里。

更多分享,请关注微信公众号:小豆君,只要关注,便可加入我的C++\Qt交流群一起学习。

相关文章

网友评论

    本文标题:3.2 自定义布局管理器

    本文链接:https://www.haomeiwen.com/subject/oqsuoxtx.html