Qt实现自定义矩阵布局
前言:
当界面需要同时展示多个项的时候,可能需要一个矩阵来填充数据,因为通常不知道数据项的多少,所以支持自定义行列就显得尤为重要,
比如可能需要在一台电脑同时显示多个报表的数据,如果一直切换,因为无法比较各个报表的数据,难免不够直观,这种时候,通过矩阵布局同步显示一般是首选方案。
效果展示:
本次采用的技术是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设计师拖拽而已,比较方便调整,因为设计师一打破布局,再调整就很麻烦,而且也不方便复用,所以个人感觉还是代码绘制比较舒服。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。