八天假期转眼就结束了,不知道大家玩儿爽没有啊。
今天小豆君继续分享干货。
虽然标准布局可以帮助我们解决很多问题,但是有时候需要一些特殊的布局管理器来管理控件,所以我们需要制作一个自己的自定义布局管理器,本节小豆君就来给大家分享如何自定义布局管理器。
我们看到所有的标准布局都直接或间接继承于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 金字塔布局
这个布局会在第一行放置一个控件,第二行放置两个控件。。。以此类推。
效果图:
好,直接上代码。
新建项目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())); } } }
编译运行程序,如图:
改变窗口大小,金字塔也会居中显示。
总的来说,布局管理就是当父窗口的大小被改变时,将其中的每个子控件进行重新排放,至于如何排放,就在setGeometry中进行计算。你可以将每个子控件想象成积木,如何摆,那就看你心情了。
这个金字塔还可以使用Qt的绘图,第三方库制作,我们以后再讲。
好了,关于自定义布局的内容今天先讲到这里。
想要第一时间看到小豆君的更新,请关注微信公众号:小豆君,只要关注,便可加入小豆君为大家创建的C++\Qt交流群,方便讨论学习。