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 
#include 
#include 
#include 
#include 
#include 

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 generateRow(const Student& item);
private:
    QTableWidget* m_table;
};

#endif // MAINWINDOW_H

MainWindow.cpp文件:

#include "MainWindow.h"
#include 

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 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 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 MainWindow::generateRow(const Student &item)
{
    QList 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 

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字节,根据生成的不同数量规模的数据,其程序占用的内存如下:
Qt高级——Qt数据可视化性能优化
根据上述数据,在大规模数据量下,使用QTableWidget展示数据时,每条数据实际占用的内存是数据本身大小的15倍,数据量越大插入越耗时,头部插入耗时远远大于尾部追加插入。

三、标准模型实现

1、代码实现

StudentTableModel.h文件:

#ifndef STUDENTTABLEMODEL_H
#define STUDENTTABLEMODEL_H

#include 
#include 

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 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 items1 = generateRow(*zhangsan);
        // 尾部追加
        m_root->appendRow(items1);
        // 头部插入
        m_root->insertRow(0, items1);
    }
    delete zhangsan;
}

QList StudentTableModel::generateRow(const Student &item)
{
    QList 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 
#include 
#include 
#include 
#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 

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

    return a.exec();
}

2、性能分析

根据生成的不同数量规模的数据,其程序占用的内存如下:
Qt高级——Qt数据可视化性能优化
使用QStandardItemModel与QTableView展示数据,每条数据实际占用内存的大小是数据本身大小的15倍,数据量越大插入越耗时,头部插入耗时远远大于尾部追加插入,其性能表现与QTableWidget相当。

四、自定义模型实现

1、代码实现

StudentTableModel.h文件:

#ifndef STUDENTTABLEMODEL_H
#define STUDENTTABLEMODEL_H

#include 
#include 
#include 
#include 
#include 

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 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 
#include 
#include 
#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 

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

    return a.exec();
}

2、性能分析
根据生成的不同数量规模的数据,其程序占用的内存如下:
Qt高级——Qt数据可视化性能优化
使用QAbstractTableModel派生类与QTableView展示数据,每条数据实际占用内存的大小是数据本身大小的1.5倍,数据量越大插入越耗时,由于底层数据结构采用链表实现,头部插入耗时与尾部追加插入耗时相当,但内存空间占用大幅下降。
将底层数据结构换成QVector,根据生成的不同数量规模的数据,其程序占用的内存如下:
Qt高级——Qt数据可视化性能优化
使用QVector作为模型的底层数据结构存储数据,其内存占用与QList相当,尾部追加插入耗时与QList相当,但头部插入比QList耗时较多。


文章名称:Qt高级——Qt数据可视化性能优化
当前地址:http://myzitong.com/article/igiisp.html