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

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

(0)

相关推荐

  • QT QML的元素布局的实现

    本文介绍QT QML跨平台移动APP开发中的元素布局的相关问题,先看一张图,我们来分析一下其中的问题: 这张图片中,有如下问题: 整体的布局没有居中显示 班级名称: 没有和 请输入班级名称输入框垂直对齐 和输入框的距离太远 班主任的提示也一样 最后的Button一行,需求要求右对齐,在QML的程序中没有实现 代码修改完以后的效果: 改变宽度试一下: 原代码说明: main.qml import QtQuick 2.12 import QtQuick.Window 2.12 Window { vi

  • QT布局管理详解QVBoxLayout与QHBoxLayout及QGridLayout的使用

    目录 main.cpp mainwindow.h mainwindow.cpp ui界面设计 登录界面为例 总结 main.cpp #include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; return a.exec(); } mainwindow.h #ifndef MAINWIND

  • Qt实现栅格布局效果

    Qt提供QGridLayout类来实现栅格布局,所谓栅格,就是网格,拥有规律的行和列,通过QGridLayout可以很方便的对多个控件进行布局. 如果在设计师中进行拖拽绘制,一旦需求有变,需要增加或者删除控件,就被迫打破原来的布局,重新进行调整,这是一件很耗时的事件, 所以通过代码画,还能做到复用,往往是首选. 效果: 代码: #include "mainwindow.h" #include "ui_mainwindow.h"   MainWindow::MainW

  • Qt实现自定义矩阵布局

    前言: 当界面需要同时展示多个项的时候,可能需要一个矩阵来填充数据,因为通常不知道数据项的多少,所以支持自定义行列就显得尤为重要, 比如可能需要在一台电脑同时显示多个报表的数据,如果一直切换,因为无法比较各个报表的数据,难免不够直观,这种时候,通过矩阵布局同步显示一般是首选方案. 效果展示: 本次采用的技术是qt,思路是通过在矩阵上布局对应的控件,以搭载数据的显示,这样子数据就可以放到对应的承载控件上显示. 通过行列号的设置来随时切换布局效果,矩阵同时支持随主界面大小的改变而改变,以适应不同的场

  • C/C++ QT实现自定义对话框的示例代码

    对话框分为多种,常见的有通用对话框,自定义对话框,模态对话框,非模态对话框等,其中通用对话框包括了,QFileDialog文件对话框,QColorDialog颜色对话框,QFontDialog字体对话框,QInputDialog输入对话框等,自定义对话框则主要是实现自己布局的简单页面,区别于窗体对话框则显得更加简单一些,除对话框外,多窗体设计也是最常用的,例如多窗体嵌入,MID窗体等,下面则是每种窗体的代码总结. 创建自定义窗体 1.首先使用两个控件,TableView主要是表格处理,TreeV

  • Qt实现自定义验证码输入框控件的方法

    前言 本文实现了自定义的验证码输入框控件.控件包括图标.输入框.获取验证码按钮.验证码获取倒计时标签.支持鼠标点击获取验证码按钮后开始显示倒计时功能,倒计时为0时,才可以在此点击获取验证码按钮.效果如图: 主要的编程思想还是实现自定义控件的封装性和共用性. UI布局: 控件1:QWidget,实现整个控件的封装,这个控件使用水平布局,实现控件的水平摆放,同时,当其中一个控件隐藏时,位置会重新更新,实现在控件的后面只显示获取验证码和倒计时两个控件中的一个. 控件2:QLabel,实现展示盾牌图标.

  • Android自定义listview布局实现上拉加载下拉刷新功能

    listview实现上拉加载以及下拉刷新的方式有很多.下面是我写的一种自定义的布局,复用性也比较的强.首先就是继承的listview的自定义view.   AutoListView.Java: package com.example.mic.testdemo.view; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Bu

  • Android Tablayout 自定义Tab布局的使用案例

    开发公司的项目中需要实现以下效果图,需要自定义TabLayout 中的Tab Tablayout xml <android.support.design.widget.TabLayout android:id="@+id/dialog_mod_icon_tablayout" android:layout_width="wrap_content" android:layout_height="wrap_content" app:tabIndi

  • Qt 信号自定义槽函数的实现

    目录 使用无参数信号与槽 使用有参信号传递 点击按钮触发信号 匿名函数与槽 Qt中实现自定义信号与槽函数,信号用于发送并触发槽函数,槽函数则是具体的功能实现,如下我们以老师学生为例子简单学习一下信号与槽函数的使用方法. 使用无参数信号与槽 首先定义一个teacher类,该类中用于发送一个信号,其次student类,定义用于接收该信号的槽函数,最后在widget中使用emit触发信号,当老师说下课时,学生请客吃饭. teacher.h 中只需要定义信号.定义一个 void hungry(); 信号

  • C/C++ Qt TableDelegate 自定义代理组件使用详解

    TableDelegate 自定义代理组件的主要作用是对原有表格进行调整,例如默认情况下Table中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重写编辑框实现选择的效果,代理组件常用于个性化定制Table表格中的字段类型. 在自定义代理中QAbstractItemDelegate是所有代理类的抽象基类,我们继承任何组件时都必须要包括如下4个函数: CreateEditor() 用于创建编辑模型数据的组件,例如(QSpinBox组件) SetE

  • 使用有趣的自定义标记布局页面

    今天我们来学习,如何使用有趣的自定义标记来布局页面.有的朋友可能有这样的疑问,自己随便定义的标记浏览器怎么能正确的认识呢? 这里我们就要用到文档的命名空间,那么命名空间又是指什么? 大家知道XML有一个很大的特点就是他的可扩展性.你可以创建你自己的标记或使用别人创建的标记,这里就存在了一个问题,即你所定义的标 记和别人定义的标识有可能相同,但他们各自所表示的意义却不同. 打一个形象的比喻,比如有两个人名字都叫蓝色,一个人在经典,一个人在天涯,如果你要找他们就可以这样说明,天涯:蓝色 . 经典:蓝

  • Android运用onTouchEvent自定义滑动布局

    写在自定义之前 我们也许会遇到,自定义控件的触屏事件处理,先来了解一下View类中的,onTouch事件和onTouchEvent事件. 1.boolean onTouch(View v, MotionVent event) 触摸事件发送到视图时调用(v:视图,event:触摸事件) 返回true:事件被完全消耗(即,从down事件开始,触发move,up所有的事件) 返回fasle:事件未被完全消耗(即,只会消耗掉down事件) 2.boolean onTouchEvent(MotionEve

  • Android自定义LinearLayout布局显示不完整的解决方法

    发现问题 原需求,在一个伸缩列表中,自定义LinearLayout继承LinearLayout动态添加布局. 然而实现的时候:一共遍历了30条数据,却只显示了一条 断点查看代码:遍历addView()这个过程是正常的30次循环.那是布局的问题? 感觉没毛病...试着在自定义布局外层再加一层LinearLayout垂直方向,wrap_content和match_parent?都试了依旧无效 毛发都被抓掉了好几根 . . 只能谷歌,找度娘了 终于翻到这个Android - 自定义View不显示,非常

随机推荐