Qt实现自定义矩阵布局

2022-08-22 11:12:47

前言:当界面需要同时展示多个项的时候,可能需要一个矩阵来填充数据,因为通常不知道数据项的多少,所以支持自定义行列就显得尤为重要,比如可能需要在一台电脑同时显示多个报表的数据,如果一直切换,因为无法比较...

前言:

当界面需要同时展示多个项的时候,可能需要一个矩阵来填充数据,因为通常不知道数据项的多少,所以支持自定义行列就显得尤为重要,

比如可能需要在一台电脑同时显示多个报表的数据,如果一直切换,因为无法比较各个报表的数据,难免不够直观,这种时候,通过矩阵布局同步显示一般是首选方案。

效果展示:

Qt实现自定义矩阵布局

本次采用的技术是qt,思路是通过在矩阵上布局对应的控件,以搭载数据的显示,这样子数据就可以放到对应的承载控件上显示。

通过行列号的设置来随时切换布局效果,矩阵同时支持随主界面大小的改变而改变,以适应不同的场景需求。

Qt实现自定义矩阵布局

主要实现代码:

void MatrixLayoutWidget::onSetMatrixLayout(int row, int column, bool bIsResize)
{
  QSize size = _displayWall->size();
  int njsum = 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.cjslear();
}

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 |android| _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设计师拖拽而已,比较方便调整,因为设计师一打破布局,再调整就很麻烦,而且也不方便复用,所以个人感觉还是代码绘制比较舒服。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。