Qt使用flowlayout,使控件两端间距始终固定,垂直和水平间距相等

先上代码:

flowlayout.h:

#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H

#include <QLayout>
#include <QRect>
#include <QStyle>

class FlowLayout : public QLayout
{
public:
    explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
    explicit FlowLayout(QWidget *parent, bool home,int margin = -1, int hSpacing = -1, int vSpacing = -1 );
    explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
    ~FlowLayout();

public:
    void addItem(QLayoutItem *item) Q_DECL_OVERRIDE;
    int horizontalSpacing() const;
    int verticalSpacing() const;
    Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE;
    bool hasHeightForWidth() const Q_DECL_OVERRIDE;
    int heightForWidth(int) const Q_DECL_OVERRIDE;
    int count() const Q_DECL_OVERRIDE;
    QLayoutItem *itemAt(int index) const Q_DECL_OVERRIDE;
    QSize minimumSize() const Q_DECL_OVERRIDE;
    void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
    QSize sizeHint() const Q_DECL_OVERRIDE;
    QLayoutItem *takeAt(int index) Q_DECL_OVERRIDE;
    int fillSpaceX(QWidget *wid) const;

private:
    int doLayout(const QRect &rect, bool testOnly) const;
    int smartSpacing(QStyle::PixelMetric pm) const;

private:
    QList<QLayoutItem *> itemList;
    int m_hSpace;
    int m_vSpace;
    bool m_home = false;


};

flowlayout.cpp:

#include "flowlayout.h"

#include <QWidget>
#include <QDebug>
#include <QtMath>

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

FlowLayout::FlowLayout(QWidget *parent, bool home, int margin, int hSpacing, int vSpacing):
    QLayout(parent),
    m_hSpace(hSpacing),
    m_vSpace(vSpacing),
    m_home(home)
{
    setContentsMargins(margin, margin, margin, margin);
}

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

FlowLayout::~FlowLayout(){
    QLayoutItem * item;
    while ((item = takeAt(0))) {

    }
}

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

int FlowLayout::horizontalSpacing() const{
    if (m_hSpace >= 0 || m_hSpace == -1) {
        return m_hSpace;
    } else {
        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
    }
}

int FlowLayout::verticalSpacing() const{
    if (m_vSpace >= 0 || m_vSpace == -1) {
        return m_vSpace;
    } else {
        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
    }
}

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

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

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

Qt::Orientations FlowLayout::expandingDirections() const{
    return 0;
}

bool FlowLayout::hasHeightForWidth() const{
    return true;
}

int FlowLayout::heightForWidth(int width) const{
    int height = doLayout(QRect(0, 0, width, 0), true);
    return height;
}

void FlowLayout::setGeometry(const QRect &rect){
    QLayout::setGeometry(rect);
    doLayout(rect, false);
}

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

QSize FlowLayout::minimumSize() const{
    QSize size;
    QLayoutItem *item;
    foreach (item, itemList)
        size = size.expandedTo(item->minimumSize());

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

int FlowLayout::doLayout(const QRect &rect, bool testOnly) const{
    int left, top, right, bottom;
    getContentsMargins(&left, &top, &right, &bottom);
    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
    int x = effectiveRect.x();
    int y = effectiveRect.y();
    int lineHeight = 0;

    int fillX = 0;
    bool bFillX = false;

    QLayoutItem *item;
    foreach (item, itemList) {
        QWidget *wid = item->widget();
        int spaceX = horizontalSpacing();
        if (spaceX == -1) {
            if (!bFillX) {
                bFillX = true;
                fillX = fillSpaceX(wid);
            }
            spaceX = fillX;
        }

        int spaceY = verticalSpacing();
        if (spaceY == -1 && fillX >= 0) {
            spaceY = fillX;
        } else {
            spaceY = wid->style()->layoutSpacing(
                        QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
        }
        if (m_home) {
            spaceY = 32;
        }


        int nextX = x + item->sizeHint().width() + spaceX;
        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
            x = effectiveRect.x();
            y = y + lineHeight + spaceY;
            nextX = x + item->sizeHint().width() + spaceX;
            lineHeight = 0;
        }

        if (!testOnly)
            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));

        x = nextX;
        lineHeight = qMax(lineHeight, item->sizeHint().height());
    }
    return y + lineHeight - rect.y() + bottom;
}

int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const{
    QObject *parent = this->parent();
    if (!parent) {
        return -1;
    } else if (parent->isWidgetType()) {
        QWidget *pw = static_cast<QWidget *>(parent);
        return pw->style()->pixelMetric(pm, 0, pw);
    } else {
        return static_cast<QLayout *>(parent)->spacing();
    }
}

int FlowLayout::fillSpaceX(QWidget *wid) const{
    int num = 0;
    int x = 0;
    int numH = 0;
    int space  = 4;
    if (m_home) {
        space = 24;
    }
    int len = this->parentWidget()->width() - this->contentsMargins().left() - this->contentsMargins().right();
    while (true) {
       num++;
       if (num * (wid->width() + space) - space >= len) {  //最小间距space
           break;
       }
    }

    num = num - 1;
    if (num <= 1) {
        numH = itemList.size();
        return 32;
    }
    int height = wid->height();
    numH = ceil(double(itemList.size()) / num);
    x = len + space - num * (wid->width() + space);
    x = ceil(double(x)/(num - 1)) +space;
    x = x - 1;   //考虑边框等因素影响

    int maxY = numH * (height + x) + 32 - x;
    if (m_home) {
        maxY = numH * (height + 24) + 32;
    }
    this->parentWidget()->setFixedHeight(maxY);
    return x;
}

使用效果一:

FlowLayout * flowLayout = new FlowLayout(ui->widget_2, true, 0);
flowLayout->setContentsMargins(70, 0, 70, 0);
/*.....*/
flowLayout->addWidget(baseWidget);

使用效果二:

picFlowLayout = new FlowLayout(ui->picFrame,16, -1, -1);
ui->picFrame->setLayout(picFlowLayout);
/*.....................*/
picFlowLayout->addWidget(picUnit);

 

 

注意,不能直接在滚动控件的widget里面去设置这个flowLayout,否则可能会导致在改变大小的时候出现段错误(一直循环在修改size)。例如在效果一中,如果直接设置父对象为scrollAreaWidgetContents_5,当在首页改变控制面板窗口大小时,就会出现闪退的情况,所以这里添加了一个widget。使用QT提供的flowlayout不会有此问题,猜测是因为新加的fillSpaceX()函数中会修改父窗口的高度导致。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值