Qt data visualization performance optimization

1. Introduction to Data Visualization

1.1. Introduction to Data Visualization

Data visualization is to display the collected data by using graphics and charts, etc., so that the data collected by the sensor can be viewed very intuitively. This article will use Qt's standard component QTableWidget, standard model, and custom model to realize the tabular display of data.

1.2. System environment

Personal PC: ThinkPad T450
Operating System: RHEL7.3 WorkStation
Memory Capacity: 8G
Disk Capacity: SSD 100G
CPU: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz

2. Realization of standard interface components

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, C++ design pattern, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project actual combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

2.1, code implementation

MainWindow.h file:

#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 file:

#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 file:

#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.2. Performance analysis

The Student structure is as follows:

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

The size of the Student structure is 108 bytes. According to the generated data of different quantities, the memory occupied by the program is as follows:

 

According to the above data, under large-scale data volume, when using QTableWidget to display data, the actual memory occupied by each piece of data is 15 times the size of the data itself. The larger the data volume, the more time-consuming it is to insert, and the head insertion time is much longer than the tail. append insert.

3. Implementation of standard model

3.1, code implementation

StudentTableModel.h file:

#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 file:

#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 file:

#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 file:

#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 file:

#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();
}

3.2. Performance analysis

According to the data of different quantities and scales generated, the memory occupied by the program is as follows:

 

Use QStandardItemModel and QTableView to display data. The actual memory size of each piece of data is 15 times the size of the data itself. The larger the data volume, the more time-consuming it is to insert. The head insertion time is much longer than the tail append insertion. Its performance is comparable to that of QTableWidget .

4. Custom model implementation

4.1, code implementation

StudentTableModel.h file:

#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 file:

#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 file:

#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 file:

#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 file:

#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();
}

4.2. Performance analysis
According to the generated data of different quantities and scales, the memory occupied by the program is as follows:

Use QAbstractTableModel derived classes and QTableView to display data. The actual memory size of each piece of data is 1.5 times the size of the data itself. The larger the data volume, the more time-consuming it is to insert. Since the underlying data structure is implemented by a linked list, it takes time to insert the head and append to the end. The insertion time is equivalent, but the memory space usage is greatly reduced.
Replace the underlying data structure with QVector. According to the generated data of different sizes, the memory occupied by the program is as follows:

 

Using QVector as the underlying data structure of the model to store data, its memory usage is equivalent to that of QList, and the time-consuming of appending and inserting at the end is equivalent to that of QList, but the time-consuming of inserting at the head is more than that of QList.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, C++ design pattern, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project actual combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/m0_73443478/article/details/130132285