3.2 自定义布局管理器

简介: 3.2 自定义布局管理器

八天假期转眼就结束了,不知道大家玩儿爽没有啊。

今天小豆君继续分享干货。

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

我们看到所有的标准布局都直接或间接继承于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 金字塔布局

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

效果图:

01ebd755782e4c909dad0843d3544acf.jpeg

好,直接上代码。

新建项目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()));
        }
    }
}

编译运行程序,如图:

01ebd755782e4c909dad0843d3544acf.jpeg

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

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

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

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

想要第一时间看到小豆君的更新,请关注微信公众号:小豆君,只要关注,便可加入小豆君为大家创建的C++\Qt交流群,方便讨论学习。

相关文章
|
7月前
|
Ubuntu iOS开发 MacOS
Qt5标题栏自定义QHeaderView自定义
Qt5标题栏自定义QHeaderView自定义
168 0
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
593 0
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
PyQt5 技术篇-如何彻底删除控件?布局移除控件方法。
PyQt5 技术篇-如何彻底删除控件?布局移除控件方法。
949 0
PyQt5 技术篇-如何彻底删除控件?布局移除控件方法。
我的Qt作品(4)实现可折叠和伸缩的自定义Widget--抽屉控件
我的Qt作品(4)实现可折叠和伸缩的自定义Widget--抽屉控件
1221 0
我的Qt作品(4)实现可折叠和伸缩的自定义Widget--抽屉控件
|
虚拟化 容器
Xamarin自定义布局系列——ListView的一个自定义实现ItemsControl(横向列表)
原文:Xamarin自定义布局系列——ListView的一个自定义实现ItemsControl(横向列表) 在以前写UWP程序的时候,了解到在ListView或者ListBox这类的列表空间中,有一个叫做ItemsPannel的属性,它是所有列表中子元素实际的容器,如果要让列表进行横向排列,只需要在...
1118 0
|
前端开发 Android开发 数据格式
自定义圆形控件 RoundImageView
1、自定义圆形控件 RoundImageView package com.ronye.CustomView; import android.content.Context; import android.
1440 0
自定义QGraphicsItem选中样式
简述 在 Scene 中添加 QGraphicsItem 后,当选中该 item 时,会看到边缘区域出现虚线,感觉不太美观。下面,我们来讲解如何去掉虚线并自定义选中样式。 简述 默认样式 虚线的由来 去掉虚线 自定义选中样式 默认样式 以椭圆为例,其它如:矩形、多边形等 item 类似。 // 构建一个椭圆 QGraphicsEllipseItem
5266 0