一. 概述
Qt的基本布局有水平布局(QHBoxLayout)、垂直布局(QVBoxLayout)、网格布局(QGridLayout)、表单布局(QFormLayout)等。QBoxWidget作为一个类似网格布局的窗口容器,将加入到容器中的窗口按照网格布局方式排列,根据窗口的数量自动计算行数与列数。与网格布局的不同之处在于,网格布局中的某个单元格中的窗口暂时隐藏之后,空余出来的空间不会被同一行的其他窗口延伸利用,QBoxWidget实现了某个网格暂时隐藏之后,同一行的其他窗口会重新分配横向上占用的空间,自动延伸,从而实现尽可能占用更多空间的功能。
二. 运行效果图
1. 所有窗口均显示的情形
2. 某些窗口暂时隐藏时的情形
3. 某个窗口最大化显示情形
三. 实现基本原理
根据容器中窗口的个数,先计算一行应该有多少列(nCol)“int nCol = 0.9999 + sqrt(m_vWidget.size());”,再计算应该有多少行nRow“int nRow = (m_vWidget.size() + nCol - 1) / nCol;”,然后创建nRow个水平布局(QHBoxLayout)容器,每个nCol水平布局(QHBoxLayout)容器中加入最多nCol个窗口,最后将这些水平布局(QHBoxLayout)容器加入到垂直布局(QVBoxLayout)容器中。
四. qboxwidget.h代码
#ifndef QBOXWIDGET_H
#define QBOXWIDGET_H
#include <QWidget>
#include <QVBoxLayout>
#include <vector>
class QPushButton;
class QCellWidget : public QWidget
{
Q_OBJECT
public:
explicit QCellWidget(QWidget *widget, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
enum ShowType
{
ST_HIDE,
ST_MAXIMIZED
};
protected:
virtual void resizeEvent(QResizeEvent *event);
virtual void paintEvent(QPaintEvent *event) override;
signals:
void showCell(int);
private slots :
void ShowCell();
private:
QPushButton *m_pBtnHide;
QPushButton *m_pBtnMaximize;
QVBoxLayout m_LayoutV;
};
class QBoxWidget : public QWidget
{
Q_OBJECT
public:
explicit QBoxWidget(QWidget *parent = nullptr, int nMaxCol = 0);
void AddWidget(QWidget *pWidget, bool bDoLayout = false);
void DoLayout();
int FindWidget(QWidget *pWidget);
inline const QVBoxLayout& GetLayout() const { return m_LayoutV; };
inline void MaximizeWidget(QWidget *pWidget = NULL) { MaximizeWidget2(FindWidget(pWidget)); };
inline void HideWidget(QWidget *pWidget = NULL) { HideWidget2(FindWidget(pWidget)); };
inline void ShowWidget(QWidget *pWidget = NULL) { ShowWidget2(FindWidget(pWidget)); };
void MaximizeWidget2(int nIndex = -1);
void HideWidget2(int nIndex = -1);
void ShowWidget2(int nIndex = -1);
private:
signals:
public slots :
void ShowCell(int);
private:
int m_nMaxCol;
QVBoxLayout m_LayoutV;
std::vector<QWidget*> m_vWidget;
};
#endif // QBOXWIDGET_H
五. qboxwidget.cpp代码
#include "qboxwidget.h"
#include <math.h>
#include <QPainter>
#include <QPushButton>
#include <QMouseEvent>
QCellWidget::QCellWidget(QWidget *widget, QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f), m_LayoutV(this)
{
widget->setParent(parentWidget());
m_LayoutV.setMargin(0);
m_LayoutV.addWidget(widget);
QPixmap pix;
pix.load("max.png");
QIcon icon(pix);
m_pBtnMaximize = new QPushButton(icon, "", this);
m_pBtnMaximize->setFlat(true);
m_pBtnMaximize->setStyleSheet("margin:0px");
m_pBtnMaximize->setFixedSize(13, 13);
m_pBtnHide = new QPushButton(QStringLiteral("×"), this);
m_pBtnHide->setFlat(true);
m_pBtnHide->setStyleSheet("margin:0px;color: rgb(160, 160, 160);");
m_pBtnHide->setFixedSize(12, 12);
connect(m_pBtnMaximize, SIGNAL(clicked()), this, SLOT(ShowCell()));
connect(m_pBtnHide, SIGNAL(clicked()), this, SLOT(ShowCell()));
}
void QCellWidget::resizeEvent(QResizeEvent *event)
{
QRect rc = rect();
m_pBtnHide->move(rc.right() - 12, 1);
m_pBtnMaximize->move(rc.right() - 28, 0);
}
void QCellWidget::paintEvent(QPaintEvent *event)
{
QRect rc = rect();
QPainter p(this);
p.setPen(QColor(224, 224, 224));
p.drawRect(rc.left(), rc.top(), rc.width() - 1, rc.height() - 1);
}
void QCellWidget::ShowCell()
{
if (sender() == m_pBtnMaximize)
{
emit showCell(ST_MAXIMIZED);
}
else
{
emit showCell(ST_HIDE);
}
}
QBoxWidget::QBoxWidget(QWidget *parent, int nMaxCol) : QWidget(parent), m_LayoutV(this)//此处必须加this
{
m_nMaxCol = nMaxCol;
m_LayoutV.setMargin(0);
m_LayoutV.setSpacing(1);
}
void QBoxWidget::AddWidget(QWidget *pWidget, bool bDoLayout)
{
if(pWidget != NULL)
{
//pWidget->setParent(parentWidget());
QCellWidget *pCW = new QCellWidget(pWidget, parentWidget());
connect(pCW, SIGNAL(showCell(int)), this, SLOT(ShowCell(int)));
m_vWidget.push_back(pCW);
}
if(bDoLayout)
{
DoLayout();
}
}
void QBoxWidget::DoLayout()
{
int nCol = 0.9999 + sqrt(m_vWidget.size());
if (m_nMaxCol > 0 && nCol > m_nMaxCol)
{
nCol = m_nMaxCol;
}
int nRow = (m_vWidget.size() + nCol - 1) / nCol;
while (m_LayoutV.count())
{
delete m_LayoutV.takeAt(0);
}
for (int i = 0; i < nRow; i++)
{
QHBoxLayout *pLayout = new QHBoxLayout();//this);//此处可以不加this
for (int j = 0; j < nCol; j++)
{
int nIndex = i*nCol + j;
if (nIndex < m_vWidget.size())
{
pLayout->addWidget(m_vWidget[nIndex]);
}
}
pLayout->setMargin(0);
pLayout->setSpacing(1);
m_LayoutV.addItem(pLayout);
}
}
int QBoxWidget::FindWidget(QWidget *pWidget)
{
if (pWidget != NULL)
{
for (int i = m_vWidget.size() - 1; i >= 0; i--)
{
if (m_vWidget[i] == pWidget)
{
return i;
}
}
}
return -1;
}
void QBoxWidget::MaximizeWidget2(int nIndex /*= -1*/)
{
int i;
if (nIndex < 0 || nIndex >= m_vWidget.size())
{
for (i = m_vWidget.size() - 1; i >= 0; i--)
{
m_vWidget[i]->show();
}
}
else
{
for (i = m_vWidget.size() - 1; i >= 0; i--)
{
m_vWidget[i]->setVisible(i == nIndex);
}
}
m_LayoutV.update();
}
void QBoxWidget::HideWidget2(int nIndex /*= -1*/)
{
if (nIndex >= 0 && nIndex < m_vWidget.size())
{
m_vWidget[nIndex]->hide();
m_LayoutV.update();
}
}
void QBoxWidget::ShowWidget2(int nIndex /*= -1*/)
{
if (nIndex >= 0 && nIndex < m_vWidget.size())
{
m_vWidget[nIndex]->show();
m_LayoutV.update();
}
}
void QBoxWidget::ShowCell(int nType)
{
QWidget *pWidget = (QWidget*)sender();
if (nType == QCellWidget::ST_MAXIMIZED)
{
MaximizeWidget(pWidget);
}
else if (nType == QCellWidget::ST_HIDE)
{
HideWidget(pWidget);
}
}