当界面需要同时展示多个项的时候,可能需要一个矩阵来填充数据,因为通常不知道数据项的多少,所以支持自定义行列就显得尤为重要,
比如可能需要在一台电脑同时显示多个报表的数据,如果一直切换,因为无法比较各个报表的数据,难免不够直观,这种时候,通过矩阵布局同步显示一般是首选方案。
效果展示:本次采用的技术是qt,思路是通过在矩阵上布局对应的控件,以搭载数据的显示,这样子数据就可以放到对应的承载控件上显示。
通过行列号的设置来随时切换布局效果,矩阵同时支持随主界面大小的改变而改变,以适应不同的场景需求。
主要实现代码:
void MatrixLayoutWidget::onSetMatrixLayout(int row, int column, bool bIsResize)
{
QSize size = _displayWall->size();
int num = row*column;
if (num == 0)
{
num = 1;
row = 1;
column = 1;
}
_row = row;
_column = column;
int labelSpace = 5;
int labelWidth = size.width() / column;
int labelHeigt = size.height() / row;
if (!bIsResize)
removeLabel();
for (int i = 0; i < num; i++)
{
if (!bIsResize)
{
QLabel *label = new QLabel(this);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("background-color:rgb(243,243,147)");
label->resize(labelWidth - labelSpace, labelHeigt - labelSpace);
_labelMap[i] = label; //注册
}
else
{
QLabel *label = nullptr;
auto iter = _labelMap.begin();
for (; iter != _labelMap.end(); iter++)
{
label = iter.value();
label->resize(labelWidth - labelSpace, labelHeigt - labelSpace);
}
}
}
for (int j = 0; j < column; j++)
{
for (int k = 0; k < row; k++)
{
int index = (row*j + k);
QLabel *label = _labelMap[index];
label->hide();
label->move(labelWidth * j, labelHeigt * k+30);
label->show();
}
}
_displayWall->update();
}
以上采用QMap来注册管理对应的承载控件,因为后续需要对控件上的子控件进行操作,所以保存了一个索引,以表示鼠标点击的是哪个承载控件。
通过这个思路,可以拓展很多功能,比如在对应承载控件双击,弹出详细的报表数据,鼠标右键弹出菜单,对控件上展示的报表数据进行其他操作等。
每次扩大行列布局的时候,目前采用先去除之前的控件,再创建新的,不过对于非实时获取数据并展示的需求而已不是很友好,因为用户可能只想增加两个新的报表,却把之前的数据清除掉了,
所以根据业务的需求,如果增加了数据,可以在原先的map表基础上再注册新的label,然后重新遍历即可。
void MatrixLayoutWidget::removeLabel()
{
//清除
auto iter = _labelMap.begin();
for (; iter != _labelMap.end(); iter++)
{
if (iter.value()) {
delete iter.value();
iter.value() = nullptr;
}
}
_labelMap.clear();
}
void MatrixLayoutWidget::resizeEvent(QResizeEvent *event)
{
//重新计算QLable的大小
if (_labelMap.count() > 0) {
onSetMatrixLayout(_row, _column, true);
}
}
通过重写resizeEvent事件来实现矩阵上的控件随窗口的大小变化而变化。只有画布上有矩阵的时候,才去做处理,避免不必要的麻烦。
整个代码demo逻辑很简单,因为可扩展性比较强,后期准备增加点新的功能,同步到github上,比如可以在每个矩阵的label上都显示波形,或者视频,或者图表。
以下是所有代码:
//.h
#ifndef MATRIXLAYOUTWIDGET_H
#define MATRIXLAYOUTWIDGET_H
#include <QWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <QMap>
class MatrixLayoutWidget : public QWidget
{
Q_OBJECT
public:
MatrixLayoutWidget(QWidget *parent = nullptr);
~MatrixLayoutWidget();
private:
void removeLabel();
virtual void resizeEvent(QResizeEvent *event) override;
protected slots:
void onSetMatrixLayout(int row, int column, bool bIsResize=false);
void onMatrixLayoutDivision();
private:
QPushButton *_1x1Btn;
QPushButton *_2x2Btn;
QPushButton *_3x3Btn;
QPushButton *_editBtn;
QLineEdit *_rowEdit;
QLineEdit *_columnEdit;
QFrame *_displayWall;
QMap<quint16, QLabel *> _labelMap;
int _row; //行
int _column; //列
};
#endif // MATRIXLAYOUTWIDGET_H
//cpp
#include "matrixlayoutwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
MatrixLayoutWidget::MatrixLayoutWidget(QWidget *parent)
: QWidget(parent)
{
int controlW = 180;
int controlH = 25;
_1x1Btn = new QPushButton;
_2x2Btn = new QPushButton;
_3x3Btn = new QPushButton;
_rowEdit = new QLineEdit;
_columnEdit = new QLineEdit;
_editBtn = new QPushButton;
_1x1Btn->setObjectName("btn");
_2x2Btn->setObjectName("btn");
_3x3Btn->setObjectName("btn");
_editBtn->setObjectName("btn");
_1x1Btn->setFixedSize(controlW / 3, controlH);
_1x1Btn->setText("1X1");
_2x2Btn->setFixedSize(controlW/3, controlH);
_2x2Btn->setText("2X2");
_3x3Btn->setFixedSize(controlW / 3, controlH);
_3x3Btn->setText("3X3");
_rowEdit->setFixedSize(controlW / 2, controlH);
_columnEdit->setFixedSize(controlW / 2, controlH);
_editBtn->setFixedSize(controlW / 3, controlH);
_editBtn->setText("Custom");
//
QHBoxLayout *editLayout = new QHBoxLayout;
editLayout->setMargin(0);
editLayout->setSpacing(0);
editLayout->addWidget(new QLabel("Row:"));
editLayout->addWidget(_rowEdit);
editLayout->addWidget(new QLabel("X"));
editLayout->addWidget(new QLabel("Column:"));
editLayout->addWidget(_columnEdit);
editLayout->addWidget(_editBtn);
//
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setMargin(0);
hlayout->setSpacing(10);
hlayout->addWidget(_1x1Btn);
hlayout->addWidget(_2x2Btn);
hlayout->addWidget(_3x3Btn);
hlayout->addLayout(editLayout);
_displayWall = new QFrame;
_displayWall->setStyleSheet("background-color:rgb(122,125,118)");
_displayWall->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *vhlayout = new QVBoxLayout;
vhlayout->setMargin(0);
vhlayout->setSpacing(5);
vhlayout->addLayout(hlayout);
vhlayout->addWidget(_displayWall);
connect(_1x1Btn, SIGNAL(clicked()), this, SLOT(onMatrixLayoutDivision()));
connect(_2x2Btn, SIGNAL(clicked()), this, SLOT(onMatrixLayoutDivision()));
connect(_3x3Btn, SIGNAL(clicked()), this, SLOT(onMatrixLayoutDivision()));
connect(_editBtn, SIGNAL(clicked()), this, SLOT(onMatrixLayoutDivision()));
this->setLayout(vhlayout);
this->setMinimumSize(400,300);
onSetMatrixLayout(1, 1);
}
MatrixLayoutWidget::~MatrixLayoutWidget()
{
removeLabel();
}
void MatrixLayoutWidget::onSetMatrixLayout(int row, int column, bool bIsResize)
{
QSize size = _displayWall->size();
int num = row*column;
if (num == 0)
{
num = 1;
row = 1;
column = 1;
}
_row = row;
_column = column;
int labelSpace = 5;
int labelWidth = size.width() / column;
int labelHeigt = size.height() / row;
if (!bIsResize)
removeLabel();
for (int i = 0; i < num; i++)
{
if (!bIsResize)
{
QLabel *label = new QLabel(this);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("background-color:rgb(243,243,147)");
label->resize(labelWidth - labelSpace, labelHeigt - labelSpace);
_labelMap[i] = label; //注册
}
else
{
QLabel *label = nullptr;
auto iter = _labelMap.begin();
for (; iter != _labelMap.end(); iter++)
{
label = iter.value();
label->resize(labelWidth - labelSpace, labelHeigt - labelSpace);
}
}
}
for (int j = 0; j < column; j++)
{
for (int k = 0; k < row; k++)
{
int index = (k + row*j);
QLabel *label = _labelMap[index];
label->hide();
label->move(labelWidth * j, labelHeigt * k+30);
label->show();
}
}
_displayWall->update();
}
void MatrixLayoutWidget::onMatrixLayoutDivision()
{
if (sender() == _1x1Btn)
{
_row = 1;
_column = 1;
}
else if (sender() == _2x2Btn)
{
_row = 2;
_column = 2;
}
else if (sender() == _3x3Btn)
{
_row = 3;
_column = 3;
}
else if (sender() == _editBtn)
{
_row = _rowEdit->text().toInt();
_column = _columnEdit->text().toInt();
if (_row == 0 || _column == 0) {
QMessageBox::critical(this, tr("warn"), "Row or Column Is Zero", QMessageBox::Ok);
return;
}
}
onSetMatrixLayout(_row, _column, false);
}
void MatrixLayoutWidget::removeLabel()
{
//清除
auto iter = _labelMap.begin();
for (; iter != _labelMap.end(); iter++)
{
if (iter.value()) {
delete iter.value();
iter.value() = nullptr;
}
}
_labelMap.clear();
}
void MatrixLayoutWidget::resizeEvent(QResizeEvent *event)
{
//重新计算QLable的大小
if (_labelMap.count() > 0) {
onSetMatrixLayout(_row, _column, true);
}
}
界面采用代码绘制,比起qt设计师拖拽而已,比较方便调整,因为设计师一打破布局,再调整就很麻烦,而且也不方便复用,所以个人感觉还是代码绘制比较舒服。