Qt实现 自定义矩阵布局

2023-11-12

前言:

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

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

效果展示:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt实现 自定义矩阵布局 的相关文章

随机推荐

  • yolov5训练PASCAL VOC数据集调试报错记录

    1 UserWarning torch meshgrid in an upcoming release it will be required to pass the indexing argument 解决方法 找到目录D Users J
  • h5开发网站-css实现页面的背景固定定位

    一 需求 在页面滚动时 背景图片保持不变 而不是跟随滚动 二 解决方式 使用背景固定定位 只需要在CSS中增加一个background attachment fixed 属性即可 具体代码 div class item right img
  • Merkle Patricia Tree (MPT) 以太坊merkle技术分析

    一 传统merkle树缺陷 我的这篇博客merkle tree 分析 详细解释了merkle树的原理和作用 然而传统的merkle树有他的局限性 以下对Vitalik blog原文的翻译可以很好的阐述 传统merkle树的一个特别的限制是
  • 创业公司如何提高管理能力?十条法则教你!

    1 明确的架构 接手任何一个部门的最重要的事情 是明确或者重新调整组织架构 架构的关键是 谁在什么位置 负责什么内容 一定要明确 所谓 明确 的意思是 不允许两个人交叉负责 也不允许集体领导 不允许有模糊的领域 出了问题 大家都清楚谁应该出
  • ubuntu的pycharm或程序卡主,强制关闭方法

    首先打开终端 输入 ps ef 查看正在运行的所有程序 找到想要关闭的程序如pycharm 3078和3192 输入 kill s 9 3078 kill s 9 3192 即可关闭pycharm 或者关闭别的程序
  • ssm整合redis

    1 配置文件 redis properties redis host 127 0 0 1 redis port 6379 redis password redis maxIdle 50 redis maxTotal 100 redis ma
  • ARCode和ARKit官网

    ARCode官网 https developers google cn ar ARCode支持机型 https developers google cn ar discover supported devices ARKit官网 https
  • Mysql中的not in和null

    给定一个表 tree id 是树节点的编号 p id 是它父节点的 id tree id p id 1 null 2 1 3 1 4 2 5 2 树中每个节点属于以下三种类型之一 叶子 如果这个节点没有任何孩子节点 根 如果这个节点是整棵树
  • OCR测试对比

    文字密度 测试一 测试二 测试三 测试四 颜色 测试一 测试二 测试三 测试四 阴影 测试一 测试二 测试三 测试四
  • Java调用python打包的程序.exe,包括获取exec()中打印的日志,亲测有效

    python写了一个方法 windows平台上怎么被Java服务调用呢 最简单的办法 python利用pyinstaller打包成 exe程序 Java程序通过Process调用 想同时获取 exe执行过程中打印出的日志用ProcessBu
  • Java网络编程之Socket编程

    Socket编程 下图是基于TCP协议的客户机与服务器通信逻辑 客户机使用Socket类型的套接字与服务器会话 服务器则需要使用ServerSocket类型的套接字侦听网络连接 再使用Socket类型的套接字与客户机会话 会话都是通过基于套
  • python3 ==和is的区别

    如图 详细说明了等号和is的区别 可以通过查看地址来解释报错的原因
  • 基于ZelinAI的周报小助手

    ZelinAI是一家领先的人工智能公司 专注于创新的AI解决方案 ZelinAI团队由科学家 工程师和领域专家组成 拥有丰富的经验和专业知识 提供自然语言处理 计算机视觉 数据分析和智能助理等领域的解决方案 帮助客户实现业务增长和效率提升
  • Nacos使用详解

    一 部署nacos 1 方式一 一般的windows和linux部署 需要的nacos server 1 4 1文件 https download csdn net download yueyue763184 87822434 spm 10
  • 入职外包一个月,我离职了。

    点击上方 程序员黑叔 选择 置顶或者星标 你的关注意义重大 前言 由于最近面试了几个人 印象最深刻的求职者是world 匿名 一个普普通通在深圳打拼的小年轻 来深圳两年 见过深圳粤海街道凌晨5点的夜晚 经历过公司从裁员到倒闭 为了生活 也去
  • 国际阿里云/腾讯云:定向流量包范围

    阿里系定向流量包规模 阿里系定向流量包一般都包括有手机天猫 手机淘宝 支付宝 优酷视频 钉钉 口碑 闲鱼 聚划算 蚂蚁财富等主流阿里系手机APP运用 但详细免流APP会依据不同的流量套餐以及业务协作状况发生变动 以流量包订购页面或许号卡处理
  • 关于小程序鉴权那点事——oauth2.0

    刚接触小程序那会 一个接一个web的方法 APi不支持 难受的只能敲着代码 流着眼泪 oauth2 0鉴权 一个access token 一个refresh token 一个expires in 在pc端我们可以使用cookie来轻松处理a
  • 使用C语言设计一个学生信息管理系统,系统功能:系统能实现学员信息的管理的基本功能,如信息录入,删除,修改,查找,成绩排序等功能

    项目思路 使用结构体 struct 来表示一个学生的信息 包括学号 姓名 性别和分数 使用数组来存储多个学生的信息 并用变量count记录当前已有学生数量 提供菜单界面供用户选择功能 根据用户选择调用功能函数实现功能 使用switch分支控
  • Go timer 是如何被调度的?

    hi 大家好 我是 haohongfan 本篇文章剖析下 Go 定时器的相关内容 定时器不管是业务开发 还是基础架构开发 都是绕不过去的存在 由此可见定时器的重要程度 我们不管用 NewTimer timer After 还是 timer
  • Qt实现 自定义矩阵布局

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