一、数据可视化简介
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耗时较多。