Qt高级——Qt数据可视化性能优化

Qt高级——Qt数据可视化性能优化

一、数据可视化简介

1、数据可视化简介

数据可视化即采用图形图表等对采集的数据进行展示,可以非常直观的查看传感器采集到的数据。本文将使用Qt的标准组件QTableWidget、标准模型、自定义模型分别实现对数据的表格展示。

2、系统环境

个人PC:ThinkPad T450
操作系统:RHEL7.3 WorkStation
内存容量:8G
磁盘容量:SSD 100G
CPU:Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz

二、标准界面组件实现

1、代码实现

MainWindow.h文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QTableWidget>
#include <QVBoxLayout>
#include <QTableWidgetItem>
#include <QList>
#include <QString>

struct Student
{
    char name[16];
    char id[24];
    char sex[8];
    int age;
    char phone[16];
    char hobby[24];
    char company[16];
};

class MainWindow : public QWidget
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    /**
     * @brief 生成数据
     * @param size,生成数据的规模
     */
    void generateData(int size);
private:
    /**
     * @brief 生成一行数据
     * @param item,数据项
     * @return 返回数据项链表
     */
    QList<QTableWidgetItem*> generateRow(const Student& item);
private:
    QTableWidget* m_table;
};

#endif // MAINWINDOW_H

MainWindow.cpp文件:

#include "MainWindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    m_table = new QTableWidget(this);
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(m_table);
    setLayout(layout);
    QStringList header;
    header << "Name" << "ID" << "Sex" << "Age" << "Phone" << "Hobby" << "Company";
    m_table->setHorizontalHeaderLabels(header);
    m_table->setColumnCount(7);
}

MainWindow::~MainWindow()
{

}
// 头部插入实现
void MainWindow::generateData(int size)
{
    Student* zhangsan = (Student*)malloc(sizeof(Student));
    memset(zhangsan, 0, sizeof(Student));
    strncpy(zhangsan->name, "zhangsan", strlen("zhangsan"));
    strncpy(zhangsan->id, "53302219861001xxxx", strlen("53302219861001xxxx"));
    strncpy(zhangsan->sex, "M", strlen("M"));
    zhangsan->age = 33;
    strncpy(zhangsan->phone, "18910108888", strlen("18910108888"));
    strncpy(zhangsan->hobby, "BasketBall, Play", strlen("BasketBall, Play"));
    strncpy(zhangsan->company, "Alibaba", strlen("Alibaba"));

    for(int i = 0; i < size; i++)
    {
        m_table->insertRow(0);
        QList<QTableWidgetItem*> items1 = generateRow(*zhangsan);
        for(int k = 0; k < items1.size(); k++)
        {
            m_table->setItem(0, k, items1.at(k));
        }
    }
    delete zhangsan;
    qDebug() << sizeof(Student);

}
// 尾部插入实现
void MainWindow::generateData(int size)
{
    Student* zhangsan = (Student*)malloc(sizeof(Student));
    memset(zhangsan, 0, sizeof(Student));
    strncpy(zhangsan->name, "zhangsan", strlen("zhangsan"));
    strncpy(zhangsan->id, "53302219861001xxxx", strlen("53302219861001xxxx"));
    strncpy(zhangsan->sex, "M", strlen("M"));
    zhangsan->age = 33;
    strncpy(zhangsan->phone, "18910108888", strlen("18910108888"));
    strncpy(zhangsan->hobby, "BasketBall, Play", strlen("BasketBall, Play"));
    strncpy(zhangsan->company, "Alibaba", strlen("Alibaba"));

    for(int i = 0; i < size; i++)
    {
        m_table->insertRow(i);
        QList<QTableWidgetItem*> items1 = generateRow(*zhangsan);
        for(int k = 0; k < items1.size(); k++)
        {
            m_table->setItem(i, k, items1.at(k));
        }
    }
    delete zhangsan;
    qDebug() << sizeof(Student);

}

QList<QTableWidgetItem*> MainWindow::generateRow(const Student &item)
{
    QList<QTableWidgetItem*> ret;
    QTableWidgetItem* name = new QTableWidgetItem();
    name->setData(Qt::DisplayRole, QString("%1").arg(item.name));
    ret.append(name);
    QTableWidgetItem* id = new QTableWidgetItem();
    id->setData(Qt::DisplayRole, QString("%1").arg(item.id));
    ret.append(id);
    QTableWidgetItem* sex = new QTableWidgetItem();
    sex->setData(Qt::DisplayRole, QString("%1").arg(item.sex));
    ret.append(sex);
    QTableWidgetItem* age = new QTableWidgetItem();
    age->setData(Qt::DisplayRole, QString("%1").arg(item.age));
    ret.append(age);
    QTableWidgetItem* phone = new QTableWidgetItem();
    phone->setData(Qt::DisplayRole, QString("%1").arg(item.phone));
    ret.append(phone);
    QTableWidgetItem* hobby = new QTableWidgetItem();
    hobby->setData(Qt::DisplayRole, QString("%1").arg(item.hobby));
    ret.append(hobby);
    QTableWidgetItem* company = new QTableWidgetItem();
    company->setData(Qt::DisplayRole, QString("%1").arg(item.company));
    ret.append(company);
    return ret;
}

main.cpp文件:

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.generateData(500000);
    w.show();

    return a.exec();
}

2、性能分析
Student结构体如下:

struct Student
{
    char name[16];
    char id[24];
    char sex[8];
    int age;
    char phone[16];
    char hobby[24];
    char company[16];
};

Student结构体大小为108字节,根据生成的不同数量规模的数据,其程序占用的内存如下:

根据上述数据,在大规模数据量下,使用QTableWidget展示数据时,每条数据实际占用的内存是数据本身大小的15倍,数据量越大插入越耗时,头部插入耗时远远大于尾部追加插入。

三、标准模型实现

1、代码实现

StudentTableModel.h文件:

#ifndef STUDENTTABLEMODEL_H
#define STUDENTTABLEMODEL_H

#include <QStandardItemModel>
#include <QStandardItem>

struct Student
{
    char name[16];
    char id[24];
    char sex[8];
    int age;
    char phone[16];
    char hobby[24];
    char company[16];
};

class StudentTableModel : public QStandardItemModel
{
    Q_OBJECT
public:
    StudentTableModel();
    /**
     * @brief 生成数据
     * @param size,数据规模
     */
    void generateData(int size);
    /**
     * @brief 生成一行数据
     * @param item,数据对象
     * @return 返回数据项链表
     */
    QList<QStandardItem*> generateRow(const Student& item);
    /**
     * @brief 追加一行
     * @param item,数据对象
     */
    void appendRow(const Student& item);

private:
    QStandardItem* m_root;//模型虚拟根节点
};

#endif // STUDENTTABLEMODEL_H

StudentTableModel.cpp文件:

#include "StudentTableModel.h"

StudentTableModel::StudentTableModel()
{
    m_root = invisibleRootItem();
}

void StudentTableModel::generateData(int size)
{
    Student* zhangsan = (Student*)malloc(sizeof(Student));
    memset(zhangsan, 0, sizeof(Student));
    strncpy(zhangsan->name, "zhangsan", strlen("zhangsan"));
    strncpy(zhangsan->id, "53302219861001xxxx", strlen("53302219861001xxxx"));
    strncpy(zhangsan->sex, "M", strlen("M"));
    zhangsan->age = 33;
    strncpy(zhangsan->phone, "18910108888", strlen("18910108888"));
    strncpy(zhangsan->hobby, "BasketBall, Play", strlen("BasketBall, Play"));
    strncpy(zhangsan->company, "Alibaba", strlen("Alibaba"));

    for(int i = 0; i < size; i++)
    {
        QList<QStandardItem*> items1 = generateRow(*zhangsan);
        // 尾部追加
        m_root->appendRow(items1);
        // 头部插入
        m_root->insertRow(0, items1);
    }
    delete zhangsan;
}

QList<QStandardItem*> StudentTableModel::generateRow(const Student &item)
{
    QList<QStandardItem*> ret;
    QStandardItem* name = new QStandardItem();
    name->setData(QString("%1").arg(item.name), Qt::DisplayRole);
    ret.append(name);
    QStandardItem* id = new QStandardItem();
    id->setData(QString("%1").arg(item.id), Qt::DisplayRole);
    ret.append(id);
    QStandardItem* sex = new QStandardItem();
    sex->setData(QString("%1").arg(item.sex), Qt::DisplayRole);
    ret.append(sex);
    QStandardItem* age = new QStandardItem();
    age->setData(QString("%1").arg(item.age), Qt::DisplayRole);
    ret.append(age);
    QStandardItem* phone = new QStandardItem();
    phone->setData(QString("%1").arg(item.phone), Qt::DisplayRole);
    ret.append(phone);
    QStandardItem* hobby = new QStandardItem();
    hobby->setData(QString("%1").arg(item.hobby), Qt::DisplayRole);
    ret.append(hobby);
    QStandardItem* company = new QStandardItem();
    company->setData(QString("%1").arg(item.company), Qt::DisplayRole);
    ret.append(company);
    return ret;
}

MainWindow.h文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include "StudentTableModel.h"

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void generateData(int size);
private:
    QTableView* m_tableView;
    StudentTableModel* m_model;
};

#endif // MAINWINDOW_H

MainWindow.cpp文件:

#include "MainWindow.h"

MainWindow::MainWindow(QWidget *parent): QWidget(parent)
{
    m_tableView = new QTableView(this);
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(m_tableView);
    setLayout(layout);
    m_model = new StudentTableModel();
    m_tableView->setModel(m_model);
    QStringList header;
    header << "Name" << "ID" << "Sex" << "Age" << "Phone" << "Hobby" << "Company";
    m_model->setHorizontalHeaderLabels(header);
}

MainWindow::~MainWindow()
{

}

void MainWindow::generateData(int size)
{
    m_model->generateData(size);
}

main.cpp文件:

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.generateData(500000);

    return a.exec();
}

2、性能分析

根据生成的不同数量规模的数据,其程序占用的内存如下:

使用QStandardItemModel与QTableView展示数据,每条数据实际占用内存的大小是数据本身大小的15倍,数据量越大插入越耗时,头部插入耗时远远大于尾部追加插入,其性能表现与QTableWidget相当。

四、自定义模型实现

1、代码实现

StudentTableModel.h文件:

#ifndef STUDENTTABLEMODEL_H
#define STUDENTTABLEMODEL_H

#include <QAbstractTableModel>
#include <QList>
#include <QStringList>
#include <QString>
#include <QVariant>

struct Student
{
    char name[16];
    char id[24];
    char sex[8];
    int age;
    char phone[16];
    char hobby[24];
    char company[16];
};

class StudentTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    StudentTableModel(QObject* parent = NULL);
    virtual int rowCount(const QModelIndex &parent) const;
    virtual int columnCount(const QModelIndex &parent) const;
    virtual QVariant data(const QModelIndex &index, int role) const;
    virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    virtual bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
    virtual bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
    void appendRow(const Student& item);
    void setHorizontalHeaderLabels(const QStringList& header);
private:
    QStringList m_headers;
    QList<Student*> m_itemList;
};

#endif // STUDENTTABLEMODEL_H

StudentTableModel.cpp文件:

#include "StudentTableModel.h"

StudentTableModel::StudentTableModel(QObject *parent): QAbstractTableModel(parent)
{
}

int StudentTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_itemList.size();
}

int StudentTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_headers.size();
}

QVariant StudentTableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= m_itemList.size() || index.row() < 0)
        return QVariant();

    if (role == Qt::DisplayRole)
    {
        int row = index.row();
        Student* data = m_itemList.at(row);
        int column = index.column();
        switch(column)
        {
        case 0:
            return QString("%1").arg(data->name);
        case 1:
            return QString("%1").arg(data->id);
        case 2:
            return QString("%1").arg(data->sex);
        case 3:
            return data->age;
        case 4:
            return QString("%1").arg(data->phone);
        case 5:
            return QString("%1").arg(data->hobby);
        case 6:
            return QString("%1").arg(data->company);
        }
    }
    return QVariant();
}

QVariant StudentTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal)
    {
        return m_headers.at(section);
    }
    return QVariant();
}

bool StudentTableModel::insertRows(int position, int rows, const QModelIndex &index)
{
    Q_UNUSED(index);
    beginInsertRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; ++row)
    {
//        m_itemList.insert(position, );
    }

    endInsertRows();
    return true;
}

bool StudentTableModel::removeRows(int position, int rows, const QModelIndex &index)
{
    Q_UNUSED(index);
    beginRemoveRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; ++row)
        m_itemList.removeAt(position);

    endRemoveRows();
    return true;
}

void StudentTableModel::appendRow(const Student &item)
{
    Student* data = (Student*)malloc(sizeof(Student));
    memset(data, 0, sizeof(Student));
    strncpy(data->name, item.name, strlen(item.name));
    strncpy(data->id, item.id, strlen(item.id));
    strncpy(data->sex, item.sex, strlen(item.sex));
    data->age = item.age;
    strncpy(data->phone, item.phone, strlen(item.phone));
    strncpy(data->hobby, item.hobby, strlen(item.hobby));
    strncpy(data->company, item.company, strlen(item.company));
    int row = m_itemList.size();
    insertRows(0, 1);
    m_itemList.append(data);
//    m_itemList.insert(0, data);
}

void StudentTableModel::setHorizontalHeaderLabels(const QStringList &header)
{
    m_headers = header;
}

bool StudentTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole)
    {
        int row = index.row();
        // mpdify data

        emit(dataChanged(index, index));
        return true;
    }
    return false;
}

Qt::ItemFlags StudentTableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;
    return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

MainWindow.h文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QTableView>
#include <QVBoxLayout>
#include "StudentTableModel.h"

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void generateData(int size);
private:
    StudentTableModel* m_model;
    QTableView* m_tableView;
};

#endif // MAINWINDOW_H

MainWindow.cpp文件:

#include "MainWindow.h"

MainWindow::MainWindow(QWidget *parent): QWidget(parent)
{
    m_tableView = new QTableView(this);
    m_model = new StudentTableModel();
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(m_tableView);
    setLayout(layout);

    QStringList header;
    header << "Name" << "ID" << "Sex" << "Age" << "Phone" << "Hobby" << "Company";
    m_model->setHorizontalHeaderLabels(header);
    m_tableView->setUpdatesEnabled(true);
    m_tableView->setModel(m_model);
}

MainWindow::~MainWindow()
{

}

void MainWindow::generateData(int size)
{
    Student* zhangsan = (Student*)malloc(sizeof(Student));
    memset(zhangsan, 0, sizeof(Student));
    strncpy(zhangsan->name, "zhangsan", strlen("zhangsan"));
    strncpy(zhangsan->id, "53302219861001xxxx", strlen("53302219861001xxxx"));
    strncpy(zhangsan->sex, "M", strlen("M"));
    zhangsan->age = 33;
    strncpy(zhangsan->phone, "18910108888", strlen("18910108888"));
    strncpy(zhangsan->hobby, "BasketBall, Play", strlen("BasketBall, Play"));
    strncpy(zhangsan->company, "Alibaba", strlen("Alibaba"));

    for(int i = 0; i < size; i++)
    {
        m_model->appendRow(*zhangsan);
    }

    delete zhangsan;
}

main.cpp文件:

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.generateData(1000000);

    return a.exec();
}

2、性能分析
根据生成的不同数量规模的数据,其程序占用的内存如下:

使用QAbstractTableModel派生类与QTableView展示数据,每条数据实际占用内存的大小是数据本身大小的1.5倍,数据量越大插入越耗时,由于底层数据结构采用链表实现,头部插入耗时与尾部追加插入耗时相当,但内存空间占用大幅下降。
将底层数据结构换成QVector,根据生成的不同数量规模的数据,其程序占用的内存如下:

使用QVector作为模型的底层数据结构存储数据,其内存占用与QList相当,尾部追加插入耗时与QList相当,但头部插入比QList耗时较多。

原文地址:https://blog.51cto.com/9291927/2399998

时间: 2024-10-07 17:20:01

Qt高级——Qt数据可视化性能优化的相关文章

Qt高级——Qt信号槽机制源码解析

Qt高级--Qt信号槽机制源码解析 基于Qt4.8.6版本 一.信号槽机制的原理 1.信号槽简介 信号槽是观察者模式的一种实现,特性如下:A.一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知:B.一个槽就是一个观察者,通常就是在被观察的对象发生改变的时候--也可以说是信号发出的时候--被调用的函数:C.信号与槽的连接,形成一种观察者-被观察者的关系:D.当事件或者状态发生改变的时候,信号就会被发出:同时,信号发出者有义务调用所有注册的对这个事件(信号)感兴趣的函数(槽).信号和

Qt高级——Qt插件开发

Qt高级--Qt插件开发 一.Qt插件机制 1.Qt插件简介 插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序. 2.Qt插件API Qt提供了两种API用于创建插件:一种是高阶API,用于扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编码,自定义样式等:一种是低阶API,用于扩展Qt应用程序. 3.通过插件扩展应用程序功能 A.定义一个接口集(只有纯虚函数的类),用来与插件交流.B.用宏Q_DECLARE_INTERFACE()将该接口告

Qt高级——Qt日志信息处理

Qt高级--Qt日志信息处理 一.Qt日志功能简介 Qt有Debug.Warning.Critical.Fatal四种级别的调试信息.qDebug:调试信息qWarning:警告信息qCritical:严重错误qFatal:致命错误Qt4提供了qInstallMsgHandler(Qt5:qInstallMessageHandler)对qDebug.qWarning.qCritical.qFatal等函数输出信息的重定向处理.qInstallMsgHandler是一个回调函数,由qDebug.q

数据库插入或者更新大批量数据的性能优化

对于一些数据量较大的系统,数据库面临的问题除了查询效率低下,还有就是数据入库时间长.特别像报表系统,每天花费在数据导入上的时间可能会长达几个小时或十几个小时之久.因此,优化数据库插入性能是很有意义的. 经过对MySQL InnoDB的一些性能测试,发现一些可以提高insert效率的方法,供大家参考参考. 1.一条SQL语句插入多条数据 常用的插入语句如: INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) VALUE

【转】mongodb可以通过profile来监控数据 (mongodb性能优化)

开启 Profiling  功能 ,对慢查询进行优化: mongodb可以通过profile来监控数据,进行优化. 查看当前是否开启profile功能用命令 db.getProfilingLevel()  返回level等级,值为0|1|2,分别代表意思:0代表关闭,1代表记录慢命令,2代表全部 db.setProfilingLevel(level);  #level等级,值同上 level为1的时候,慢命令默认值为100ms,更改为db.setProfilingLevel(level,slow

Sqlite大数据写入性能优化

众所周知,Sqlite是一个轻量级的数据库,仅仅需要一个exe文件就能运行起来.在处理本地数据上,我比较喜欢选择使用它,不仅是因为他与sql server有着比较相近的语法,还因为它不需要安装,仅需要通过命令行就能启动了,而且他在处理大数据时,性能比sql server好很多,好吧这里不继续争论性能优劣. 首先,这次的问题是在一次项目中遇到的,项目要求能大量导入数据,而且由于项目性质(轻便,本地化),所以我选择sqlite来存放数据. 第一版代码: 1 StringBuilder sql = n

Skyline Web 端数据浏览性能优化

三维数据的效率一直是个瓶颈,特别是在Web端浏览一直是个问题,在IE内存限制1G的条件下,对于三维数据动不动几十G的数据量,这1G显得多么微不足道.虽然现在三维平台都是分级加载,或者在程序中采用数据分不同片区加载来降低一次性加载的模型数据,但是在浏览器中浏览三维数据崩溃问题仍然一直存在.最近在维护一个老的Skyline的项目,客户提出了在看二维数据的同时也可以看三维数据,需求很简单,实现也很容易,唯一的问题是浏览效率,能不能浏览?浏览效率怎么样?而且政府单位的电脑配置都是很一般.毕竟也做了这么多

存储性能差?杉岩数据分布式存储性能优化方案

作为明星产品,杉岩海量对象存储(SandStone MOS)在企业级市场顺势推出后,即受到了广泛的认可和接纳,它在众多企业级用户的场景中具备独特的价值属性,例如:海量小文件的访问性能保障.分支总部间的信息高速分发.数据中心级的双活或多活容灾等等,都让用户在面临PB级甚至EB级规模的非结构化数据存储时,有了一个全新的兼顾成本和服务的选择. SandStone MOS 在接口兼容性.数据安全性.业务高可用.全局便捷运维.数据生命周期管理等场景上进行了全新的补充和完善,接下来将为您详细解读关键功能的价

关于数据库优化3——在数据库插入、更新大量数据的性能优化

在真实的业务场景中,我们肯定是遇到过有时候需要大批量的数据要进行入库或者是更新.这时候我们在执行这种插入,或者是更新的时候肯定会遇到数据库效率的问题,我们首先能想到的是让事物尽量保持一致,统一去提交事务,这样肯定会有一定的效率提示. ok.在我们在考虑了这个事情之后,我们可以在来看一下表里是否存在索引,如果存在索引,我们可以做一个这样的操作:首先在插入大量数据的时候,先把索引废除掉,等插入完成后在把索引重新建立.我在这里做了一个简单的实验,整理了一下实验的结果,有了下面的一个执行效率的图. 实验