Qt——功能:流式布局

测试环境:win10、Qt Creator 4.11.1、5.14.2版本SDK

github:https://github.com/coldfirehello/QtDemo.git

 

项目需求:拉伸窗口后,自动记录每行子窗口个数,而且可以使用筛选功能,隐藏窗口。此时显示内容自动刷新。

 

初选方案1:QListWidget流式布局

经过调查,流式布局可以实现上述功能,后续发现QListWidget有流式布局功能。

上代码:

flowWidget.h
#ifndef FLOWWIDGET_H
#define FLOWWIDGET_H

#include <QListWidget>
#include <QPushButton>

class FlowWidget : public QWidget
{
    Q_OBJECT
public:
    explicit FlowWidget(QWidget *parent = nullptr);

signals:

private slots:
    void onBtn();

private:
    QListWidget m_list;

    QListWidgetItem* m_Item1;
    QListWidgetItem* m_Item2;
    QListWidgetItem* m_Item3;
    QListWidgetItem* m_Item4;
    QListWidgetItem* m_Item5;
    QListWidgetItem* m_Item6;
    QListWidgetItem* m_Item7;
    QListWidgetItem* m_Item8;
    QListWidgetItem* m_Item9;
    QListWidgetItem* m_ItemA;

    QPushButton* m_btn1;
    QPushButton* m_btn2;
    QPushButton* m_btn3;
    QPushButton* m_btn4;
    QPushButton* m_btn5;
    QPushButton* m_btn6;
    QPushButton* m_btn7;
    QPushButton* m_btn8;
    QPushButton* m_btn9;
    QPushButton* m_btnA;
};

#endif // FLOWWIDGET_H

flowWidget.cpp
#include "flowwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDebug>

#define LISTWIDGET

FlowWidget::FlowWidget(QWidget *parent) : QWidget(parent)
{
    QPushButton* btn = new QPushButton("btn");
    btn->setObjectName("btn");
    connect(btn, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn1 = new QPushButton("btn1");
    btn1->setObjectName("btn1");
    connect(btn1, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn2 = new QPushButton("btn2");
    btn2->setObjectName("btn2");
    connect(btn2, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn3 = new QPushButton("btn3");
    btn3->setObjectName("btn3");
    connect(btn3, &QPushButton::clicked, this, &FlowWidget::onBtn);

    QHBoxLayout* hLayout = new QHBoxLayout();
    hLayout->addWidget(btn);
    hLayout->addWidget(btn1);
    hLayout->addWidget(btn2);
    hLayout->addWidget(btn3);

    m_list.setViewMode(QListView::IconMode);
    m_list.setResizeMode(QListView::Adjust);    //流式布局关键
    m_list.setMovement(QListView::Static);
    m_list.setSpacing(10);

    m_Item1 = new QListWidgetItem();
    m_btn1 = new QPushButton("1");
    m_Item1->setSizeHint(QSize(100, 100));
    m_list.addItem(m_Item1);
    m_list.setItemWidget(m_Item1, m_btn1);

    m_Item2 = new QListWidgetItem();
    m_btn2 = new QPushButton("2");
    m_Item2->setSizeHint(QSize(100, 100));
    m_list.addItem(m_Item2);
    m_list.setItemWidget(m_Item2, m_btn2);

    m_Item3 = new QListWidgetItem();
    m_btn3 = new QPushButton("3");
    m_list.addItem(m_Item3);
    m_Item3->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item3, m_btn3);

    m_Item4 = new QListWidgetItem();
    m_btn4 = new QPushButton("4");
    m_list.addItem(m_Item4);
    m_Item4->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item4, m_btn4);

    m_Item5 = new QListWidgetItem();
    m_btn5 = new QPushButton("5");
    m_list.addItem(m_Item5);
    m_Item5->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item5, m_btn5);

    m_Item6 = new QListWidgetItem();
    m_btn6 = new QPushButton("6");
    m_list.addItem(m_Item6);
    m_Item6->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item6, m_btn6);

    m_Item7 = new QListWidgetItem();
    m_btn7 = new QPushButton("7");
    m_list.addItem(m_Item7);
    m_Item7->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item7, m_btn7);

    m_Item8 = new QListWidgetItem();
    m_btn8 = new QPushButton("8");
    m_list.addItem(m_Item8);
    m_Item8->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item8, m_btn8);

    m_Item9 = new QListWidgetItem();
    m_btn9 = new QPushButton("9");
    m_list.addItem(m_Item9);
    m_Item9->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_Item9, m_btn9);

    m_ItemA = new QListWidgetItem();
    m_btnA = new QPushButton("A");
    m_list.addItem(m_ItemA);
    m_ItemA->setSizeHint(QSize(100, 100));
    m_list.setItemWidget(m_ItemA, m_btnA);

    QVBoxLayout* layout = new QVBoxLayout();
    layout->addLayout(hLayout);
    layout->addWidget(&m_list);
    setLayout(layout);
}

void FlowWidget::onBtn()
{
    QPushButton* btn = qobject_cast<QPushButton*>(sender());
    if(btn->objectName() == "btn")
    {
        m_Item1->setHidden(false);
        m_Item2->setHidden(false);
        m_Item3->setHidden(false);
        m_Item4->setHidden(false);
        m_Item5->setHidden(false);
        m_Item6->setHidden(false);
        m_Item7->setHidden(false);
        m_Item8->setHidden(false);
        m_Item9->setHidden(false);
        m_ItemA->setHidden(false);
    }
    else if(btn->objectName() == "btn1")
    {
        m_Item1->setHidden(true);
        m_Item2->setHidden(true);
        m_Item3->setHidden(false);
        m_Item4->setHidden(false);
        m_Item5->setHidden(true);
        m_Item6->setHidden(true);
        m_Item7->setHidden(true);
        m_Item8->setHidden(false);
        m_Item9->setHidden(false);
        m_ItemA->setHidden(false);
    }
    else if(btn->objectName() == "btn2")
    {
        m_Item1->setHidden(false);
        m_Item2->setHidden(false);
        m_Item3->setHidden(true);
        m_Item4->setHidden(true);
        m_Item5->setHidden(false);
        m_Item6->setHidden(false);
        m_Item7->setHidden(true);
        m_Item8->setHidden(false);
        m_Item9->setHidden(true);
        m_ItemA->setHidden(false);
    }
    else
    {
        m_Item1->setHidden(false);
        m_Item2->setHidden(false);
        m_Item3->setHidden(false);
        m_Item4->setHidden(false);
        m_Item5->setHidden(true);
        m_Item6->setHidden(true);
        m_Item7->setHidden(true);
        m_Item8->setHidden(false);
        m_Item9->setHidden(false);
        m_ItemA->setHidden(false);
    }
}

main.cpp
#include <QApplication>
#include "flowwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    FlowWidget widget;
    widget.show();

    return a.exec();
}

但效果不如意:

上述内容可以发现:

1、点击btn1按钮有时显示5个(代码要求显示五个),有时显示4个。

2、QListWidget水平和行间距公用setSpcaing()函数设置的参数。(可以使用子窗口setContentsMargins()函数增加底部距离来解决水平和行间距相同,但是方法比较笨)。

 

2、项目中使用方案:Qt示例有流式布局demo,但是需要做些修改

上代码:

flowLayout.h
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H

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

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

class FlowLayout : public QLayout
{
    Q_OBJECT
public:
    //hSpacint——水平间距    vSpacint——行间距
    explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
    FlowLayout(int margin, int hSpacing, int vSpacing);
    ~FlowLayout();

    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;

signals:
//    void sglHeightChange(int height);

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

private:
    QList<QLayoutItem *> x_itemList;
    int x_hSpace;
    int x_vSpace;
    int m_lastHeight;
};
#endif

flowLayout.cpp
#include "flowlayout.h"

#include <algorithm>
#include <QWidget>

FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : QLayout(parent), x_hSpace(hSpacing), x_vSpace(vSpacing)
{
    m_lastHeight = -1;
    setContentsMargins(margin, margin, margin, margin);
}

FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : x_hSpace(hSpacing), x_vSpace(vSpacing)
{
    m_lastHeight = -1;
    setContentsMargins(margin, margin, margin, margin);
}

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

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

int FlowLayout::horizontalSpacing() const
{
    if (x_hSpace >= 0)
    {
        return x_hSpace;
    }
    else
    {
        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
    }
}

int FlowLayout::verticalSpacing() const
{
    if (x_vSpace >= 0) {
        return x_vSpace;
    }
    else {
        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
    }
}

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

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

QLayoutItem *FlowLayout::takeAt(int index)
{
    QLayoutItem* item = nullptr;

    if (index >= 0 && index < x_itemList.size())
    {
        item = x_itemList.takeAt(index);
        invalidate();
    }

    return item;
}

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

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

int FlowLayout::heightForWidth(int width) const
{
    Q_UNUSED(width)
    return -1;
}

void FlowLayout::setGeometry(const QRect &rect)
{
    QLayout::setGeometry(rect);

    int height = doLayout(rect, false);

    if(height != m_lastHeight)
    {
//        emit sglHeightChange(height);
    }
}

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

QSize FlowLayout::minimumSize() const
{
    QSize size;
    QLayoutItem *item;
    foreach(item, x_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;

    QLayoutItem *item;
    foreach(item, x_itemList)
    {
        QWidget *wid = item->widget();

        //如果控件隐藏就不计算位置(Qt官方示例没有)
        if(!wid->isHidden())    
        {
            int spaceX = horizontalSpacing();
            if (spaceX == -1)
                spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
            int spaceY = verticalSpacing();
            if (spaceY == -1)
                spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);

            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 = qobject_cast<QWidget *>(parent);
        return pw->style()->pixelMetric(pm, 0, pw);
    }
    else {
        return static_cast<QLayout *>(parent)->spacing();
    }
}

//以下为测试代码
flowWidget.h
#ifndef FLOWWIDGET_H
#define FLOWWIDGET_H

#include <QListWidget>
#include <QPushButton>

class FlowWidget : public QWidget
{
    Q_OBJECT
public:
    explicit FlowWidget(QWidget *parent = nullptr);

signals:

private slots:
    void onBtn();

private:
    QListWidget m_list;

    QListWidgetItem* m_Item1;
    QListWidgetItem* m_Item2;
    QListWidgetItem* m_Item3;
    QListWidgetItem* m_Item4;
    QListWidgetItem* m_Item5;
    QListWidgetItem* m_Item6;
    QListWidgetItem* m_Item7;
    QListWidgetItem* m_Item8;
    QListWidgetItem* m_Item9;
    QListWidgetItem* m_ItemA;

    QPushButton* m_btn1;
    QPushButton* m_btn2;
    QPushButton* m_btn3;
    QPushButton* m_btn4;
    QPushButton* m_btn5;
    QPushButton* m_btn6;
    QPushButton* m_btn7;
    QPushButton* m_btn8;
    QPushButton* m_btn9;
    QPushButton* m_btnA;
};

#endif // FLOWWIDGET_H


flowWidget.cpp
#include "flowwidget.h"
#include <QPushButton>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QScrollBar>
#include <QDebug>
#include "flowlayout.h"

//#define FLOWLAYOUT
#define LISTWIDGET

FlowWidget::FlowWidget(QWidget *parent) : QWidget(parent)
{
    QPushButton* btn = new QPushButton("btn");
    btn->setObjectName("btn");
    connect(btn, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn1 = new QPushButton("btn1");
    btn1->setObjectName("btn1");
    connect(btn1, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn2 = new QPushButton("btn2");
    btn2->setObjectName("btn2");
    connect(btn2, &QPushButton::clicked, this, &FlowWidget::onBtn);
    QPushButton* btn3 = new QPushButton("btn3");
    btn3->setObjectName("btn3");
    connect(btn3, &QPushButton::clicked, this, &FlowWidget::onBtn);

    QHBoxLayout* hLayout = new QHBoxLayout();
    hLayout->addWidget(btn);
    hLayout->addWidget(btn1);
    hLayout->addWidget(btn2);
    hLayout->addWidget(btn3);

    FlowLayout* flowLayout = new FlowLayout(-1, 12, 20);

    m_Item1 = new QListWidgetItem();
    m_btn1 = new QPushButton("1");
    flowLayout->addWidget(m_btn1);

    m_Item2 = new QListWidgetItem();
    m_btn2 = new QPushButton("2");
    flowLayout->addWidget(m_btn2);

    m_Item3 = new QListWidgetItem();
    m_btn3 = new QPushButton("3");
    flowLayout->addWidget(m_btn3);

    m_Item4 = new QListWidgetItem();
    m_btn4 = new QPushButton("4");
    flowLayout->addWidget(m_btn4);

    m_Item5 = new QListWidgetItem();
    m_btn5 = new QPushButton("5");
    flowLayout->addWidget(m_btn5);

    m_Item6 = new QListWidgetItem();
    m_btn6 = new QPushButton("6");
    flowLayout->addWidget(m_btn6);

    m_Item7 = new QListWidgetItem();
    m_btn7 = new QPushButton("7");
    flowLayout->addWidget(m_btn7);

    m_Item8 = new QListWidgetItem();
    m_btn8 = new QPushButton("8");
    flowLayout->addWidget(m_btn8);

    m_Item9 = new QListWidgetItem();
    m_btn9 = new QPushButton("9");
    flowLayout->addWidget(m_btn9);

    m_ItemA = new QListWidgetItem();
    m_btnA = new QPushButton("A");
    flowLayout->addWidget(m_btnA);

    QWidget* flowWidget = new QWidget();
    flowWidget->setLayout(flowLayout);

    QVBoxLayout* layout = new QVBoxLayout();
    layout->addLayout(hLayout);
    layout->addWidget(flowWidget);
    setLayout(layout);
}

void FlowWidget::onBtn()
{
    QPushButton* btn = qobject_cast<QPushButton*>(sender());
    if(btn->objectName() == "btn")
    {
        m_btn1->setHidden(false);
        m_btn2->setHidden(false);
        m_btn3->setHidden(false);
        m_btn4->setHidden(false);
        m_btn5->setHidden(false);
        m_btn6->setHidden(false);
        m_btn7->setHidden(false);
        m_btn8->setHidden(false);
        m_btn9->setHidden(false);
        m_btnA->setHidden(false);
    }
    else if(btn->objectName() == "btn1")
    {
        m_btn1->setHidden(true);
        m_btn2->setHidden(true);
        m_btn3->setHidden(false);
        m_btn4->setHidden(false);
        m_btn5->setHidden(true);
        m_btn6->setHidden(true);
        m_btn7->setHidden(true);
        m_btn8->setHidden(false);
        m_btn9->setHidden(false);
        m_btnA->setHidden(false);
    }
    else if(btn->objectName() == "btn2")
    {
        m_btn1->setHidden(false);
        m_btn2->setHidden(false);
        m_btn3->setHidden(true);
        m_btn4->setHidden(true);
        m_btn5->setHidden(false);
        m_btn6->setHidden(false);
        m_btn7->setHidden(true);
        m_btn8->setHidden(false);
        m_btn9->setHidden(true);
        m_btnA->setHidden(false);
    }
    else
    {
        m_btn1->setHidden(false);
        m_btn2->setHidden(false);
        m_btn3->setHidden(false);
        m_btn4->setHidden(false);
        m_btn5->setHidden(true);
        m_btn6->setHidden(true);
        m_btn7->setHidden(true);
        m_btn8->setHidden(false);
        m_btn9->setHidden(false);
        m_btnA->setHidden(false);
    }
}

main.cpp
#include <QApplication>
#include "flowwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    FlowWidget widget;
    widget.show();

    return a.exec();
}

运行结果:

从上图中可以看到,水平间距和行间距都可以修改,而且隐藏显示按钮个数也都正常。

 

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值