QT 实现 以名片列表的形式 显示 10万 条数据 - 如何流畅的显示十万条数据

解决Qt的列表加载大量数据卡顿的问题 本人在使用QListView插入大量数据时,界面卡顿十分严重。数据量大概只有上千左右,但是每个Item的内容比较多。当数据不停地插入一段时间后,卡顿到鼠标的移动都有点困难。 阅读详情

目录

零、附上最新版本

1、附上成果图:

2、代码位置:

一、常用实现方式 - 有多少就创建多少,就显示多少

1、MainWindow.h文件

2、MainWindow.cpp文件:

3、main.cpp文件:

4、性能分析

二、Model / View 标准模型实现

1、StudentTableModel.h文件

2、StudentTableModel.cpp文件:

3、MainWindow.h文件:

4、MainWindow.cpp文件:

5、main.cpp文件:

6、性能分析

三、Model / View 自定义模型实现

1、StudentTableModel.h文件:

2、StudentTableModel.cpp文件:

3、MainWindow.h文件

4、MainWindow.cpp文件:

5、main.cpp文件:

 6、性能分析

四、Delegate + Model + View 自定义模型实现

1、QtTabelWidget.cpp - 组织界面,定义数据模型、委托、代理

2、QtModelViewTabelDelegate.cpp - 委托:用于 绘制自定义 名片控件

3、StudentTableModel.cpp - 创建模型类,提供数据的调用接口

4、TabelSortFilterProxyModel.cpp - 代理:用于对数据过滤、排序


零、附上最新版本

1、附上成果图:( 四 代码对应的效果图)

1、使用 QTableView 显示 10万条数据

2、每一条用自定义界面(名片的形式) 来显示

3、提供搜索过滤功能

2、代码位置:

https://download.youkuaiyun.com/download/LearnLHC/15933659

一、常用实现方式 - 有多少就创建多少,就显示多少

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

2、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;
}

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

4、性能分析

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倍,数据量越大插入越耗时,头部插入耗时远远大于尾部追加插入。

二、Model / View 标准模型实现

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

2、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;
}

3、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

4、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);
}

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

6、性能分析

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

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

三、Model / View 自定义模型实现

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

2、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;
}

3、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

4、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;
}

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

 6、性能分析

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

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

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

四、Delegate + Model + View 自定义模型实现

1、QtTabelWidget.cpp - 组织界面,定义数据模型、委托、代理

#include <QTableView>
#include <QHeaderView>
#include <QVBoxLayout>
#include <QTime>
#include <qDebug>
#include <QLabel>
#include <QLineEdit>

#include "QtTabelWidget.h"

QtTabelWidget::QtTabelWidget(QWidget *parent)
	: QWidget(parent)
{
	setFixedSize(800, 800);

	QVBoxLayout * mainLayout = new QVBoxLayout();
	mainLayout->setSpacing(0);
	mainLayout->setContentsMargins(0, 10, 0, 30);

	// 创建表格显示控件;
	m_TableView = new QTableView(this);
	m_TableView->setObjectName("MemberManageTableView");
	m_TableView->setShowGrid(true);
	m_TableView->setSelectionBehavior(QAbstractItemView::SelectItems);
	m_TableView->setMouseTracking(true);
	//m_TableView->setEditTriggers(QAbstractItemView::AllEditTriggers);

	// 创建委托类,用于 绘制自定义 名片控件;
	m_TableDelegate = new QtModelViewTabelDelegate(m_TableView);
	m_TableView->setItemDelegate(m_TableDelegate);

	// 创建模型类;
	m_TableModel = new StudentTableModel(this);

	QStringList header;
	header << "Name";
	m_TableModel->setHorizontalHeaderLabels(header);

	// ① 不使用数据过滤器;
	//m_TableView->setModel(m_TableModel);

	// ② 使用数据过滤器;
	m_TSFProxyModel = new TabelSortFilterProxyModel();
	m_TSFProxyModel->setSourceModel(m_TableModel);  //将model放入代理中;
	m_TableView->setModel(m_TSFProxyModel);         //在视图中安装代理;
	m_TSFProxyModel->setFilterRegExp("zhangsan20"); //安装过滤器;

	m_TableView->setUpdatesEnabled(true);
	m_TableView->verticalHeader()->setDefaultSectionSize(600);
	m_TableView->resizeRowsToContents();
	m_TableView->horizontalHeader()->setDefaultSectionSize(290);
	m_TableView->horizontalHeader()->setVisible(true);// 水平不可见
	m_TableView->verticalHeader()->setVisible(false);// 垂直不可见
	m_TableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);

	// 添加 搜索框;
	QHBoxLayout* hLayout = new QHBoxLayout;
	QLabel * laber = new QLabel();
	laber->setText("Search");
	QLineEdit *lineEdit = new QLineEdit;
	connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onLineEditChangeSlot(const QString &)));

	hLayout->addWidget(laber);
	hLayout->addWidget(lineEdit);

	mainLayout->addLayout(hLayout);
	mainLayout->addWidget(m_TableView);
	setLayout(mainLayout);
}

QtTabelWidget::~QtTabelWidget()
{
}

//组织 十万条数据;
void QtTabelWidget::generateData(int size)
{
	QTime time;
	time.start();

	QList<StudentPtr> itemList;
	for (int i = 0; i < size; i++)
	{
		StudentPtr data = QSharedPointer<Student>(new Student);
		data->name = "zhangsan" + QString::number(i);
		data->id = "53302219861001xxxx";
		data->sex = "M";
		data->age = 33;
		data->phone = "18910108888";
		data->hobby = "BasketBall, Play";
		data->company = QString(":/pic/Resources/side%1.png").arg(i%3+1);
		itemList.push_back(data);
		
		//m_TableModel->appendRow(data);
	}
	
	m_TableModel->appendRows(itemList);

	//qDebug() << QStringLiteral("代码程序运行时间:") << time.elapsed();
	qDebug() << time.elapsed() / 1000.0 << "s";
}

// 搜索内容变更时,重设过滤器;
void QtTabelWidget::onLineEditChangeSlot(const QString & text)
{
	//安装过滤器;
	m_TSFProxyModel->setFilterRegExp(text);
	// 调用 invalidateFilter() 函数,使之前的过滤失效,激活当前过滤.
	//m_TSFProxyModel->invalidateFilter();
}

2、QtModelViewTabelDelegate.cpp - 委托:用于 绘制自定义 名片控件

#include <QAbstractItemView>
#include <QTableView>
#include <QLabel>
#include <QGridLayout>
#include <QLineEdit>

#include "QtModelViewTabelDelegate.h"
#include "StudentTableModel.h"

QtModelViewTabelDelegate::QtModelViewTabelDelegate(QAbstractItemView *itemView)
{
	m_itemView = itemView;
}

QtModelViewTabelDelegate::~QtModelViewTabelDelegate()
{
}

void QtModelViewTabelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	Q_UNUSED(painter);
	Q_UNUSED(option);

	QStyleOptionViewItem opt = option;
	QString NN = opt.widget->objectName();

	// 也可以直接用 m_itemView 代替 tV 来操作;
	QWidget * widget = const_cast<QWidget *>(opt.widget);
	QTableView * tV = dynamic_cast<QTableView *>(widget);

	if (tV)
	{
		if (tV->indexWidget(index) == nullptr)
		{
			QWidget *itemWidget = createItemWidget(index);
			if (itemWidget != nullptr)
			{
				tV->setIndexWidget(index, itemWidget);
			}
		}
		else
		{
			updateItemWidget(index);
			setRowHeight(index);
		}
	}
}

QWidget * QtModelViewTabelDelegate::createItemWidget(const QModelIndex &index) const
{
	StudentPtr adapter = index.data().value<StudentPtr>();
	if (!adapter)
		return nullptr;

	QWidget *wid = new QWidget();
	wid->setObjectName("QWidgetF");
	QGridLayout *glay = new QGridLayout();
	glay->setObjectName("QGridLayoutF");
	QLabel *nameItem = new QLabel();
	nameItem->setText(adapter->name);
	QLabel *idItem = new QLabel();
	idItem->setText(adapter->id);
	QLabel *sexItem = new QLabel();
	sexItem->setText(adapter->sex);

	QLineEdit *phoneItem = new QLineEdit();
	phoneItem->setText(adapter->phone);
	phoneItem->setObjectName("phoneItem");
	phoneItem->installEventFilter(const_cast<QtModelViewTabelDelegate*>(this));

	connect(phoneItem, SIGNAL(textChanged(const QString &)), this, SLOT(onLineEditChangeSlot(const QString &)));

	QLineEdit *hobbyItem = new QLineEdit();
	hobbyItem->setText(adapter->hobby);
	connect(hobbyItem, SIGNAL(textChanged(const QString &)), this, SLOT(onLineEditChangeSlot(const QString &)));

	QLabel *companyItem = new QLabel();
	companyItem->setText(adapter->company);
	companyItem->setPixmap(QPixmap(adapter->company).scaled(90,90));

	glay->addWidget(nameItem, 0, 0, 1, 1);
	glay->addWidget(idItem, 1, 0, 1, 1);
	glay->addWidget(sexItem, 2, 0, 1, 1);

	glay->addWidget(phoneItem, 0, 1);
	glay->addWidget(hobbyItem, 2, 1);

	glay->addWidget(companyItem, 0, 2, 3, 4);
	glay->setContentsMargins(5, 5, 5, 5);
	wid->setLayout(glay);

	return wid;
}

void QtModelViewTabelDelegate::setRowHeight(const QModelIndex &index) const
{
	StudentPtr adapter = index.data().value<StudentPtr>();
	if (!adapter)
		return;

	QTableView *tableView = dynamic_cast<QTableView*>(m_itemView);
	if (tableView == nullptr)
		return;

	int nHeight = 100;
	tableView->setRowHeight(index.row(), nHeight);
	tableView->setColumnWidth(0, 400);
}

void QtModelViewTabelDelegate::updateItemWidget(const QModelIndex &index) const
{

}

bool QtModelViewTabelDelegate::eventFilter(QObject *watched, QEvent *event)
{
	if (watched->objectName() == "phoneItem")
	{
		if (event->type() == QEvent::MouseButtonPress)
		{
			QTableView *tableView = dynamic_cast<QTableView*>(m_itemView);
			if (tableView == nullptr)
				return false;

			QModelIndex indexM = tableView->indexAt(tableView->mapFromGlobal(QCursor::pos()));
			tableView->setCurrentIndex(indexM);
		}
	}
	return false;
}

void QtModelViewTabelDelegate::onLineEditChangeSlot(const QString & text)
{
	StudentPtr adapter = m_itemView->currentIndex().data().value<StudentPtr>();
	if (!adapter)
		return;
	QString ob = adapter->name;
	int i = 0;
}

3、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();
		StudentPtr data = m_itemList.at(row);
		return QVariant::fromValue(data);

		/*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);

	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 StudentPtr item)
{
	beginInsertRows(QModelIndex(), m_itemList.size(), m_itemList.size() + 1);
	
	m_itemList.append(item);

	endInsertRows();
}

void StudentTableModel::appendRows(QList<StudentPtr> item)
{
	beginInsertRows(QModelIndex(), 0, item.size());

	m_itemList = item;

	endInsertRows();
}

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;
}

4、TabelSortFilterProxyModel.cpp - 代理:用于对数据过滤、排序

#include "TabelSortFilterProxyModel.h"
#include "StudentTableModel.h"

TabelSortFilterProxyModel::TabelSortFilterProxyModel(QObject *parent)
	: QSortFilterProxyModel(parent)
{
}

TabelSortFilterProxyModel::~TabelSortFilterProxyModel()
{
}

bool TabelSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
	//获取model中实际的数据;
	StudentPtr adapter = sourceModel()->index(source_row, 0, source_parent).data().value<StudentPtr>();
	if (!adapter)
		return false;

	if (adapter->name.contains(this->filterRegExp()))
	{
		return true;
	}
	return false;
}

bool TabelSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
	//通过当前视图中的index位置获取model中实际的数据;
	QVariant leftData = sourceModel()->data(source_left);
	QVariant rightData = sourceModel()->data(source_right);
	switch (source_left.column())
	{
	case 0:     //序号,需要判断数字;
	case 3:     //信号ID,需要判断数字;
		return leftData.toInt() < rightData.toInt();
		break;
	default:    //其它,只判断字符串;
		return leftData.toString() < rightData.toString();
		break;
	}

	return true;
}

 

 

QT tableview 实现千万数据加载,内存消耗小 因为自己之前开发的工具也有类似的功能,但是效果没有这个好,于是研究了一下工具的源码,按照源码中的方法自己实验了一下tableview的用法。原文链接:https://blog.youkuaiyun.com/cwei231/article/details/131947874。//返回指定位置表头。if (role == Qt::DisplayRole) { //角色为显示。 阅读详情

相关推荐

Qt 卡片控件 一个以卡片样式展示信息的自定义控件

Qt 自定义卡片控件

dododododoooo的博客 1610

Qt小例子学习52 - 卡片游戏

Qt小例子学习53 - 卡片游戏 Card.qml import QtQuick 2.0 import QtQuick.Layouts 1.3 Item { id: card Layout.fillWidth: true Layout.fillHeight: true property alias backImageSource: backImage.source property alias flipped: flipable.flipped prope

虾球 692

qt项目中需要实时显示上万条数据,如何设计才不会卡顿

2. **虚拟化**:对于大量的数据,可以使用QAbstractItemView的虚拟化功能,比如`QListView`、`QTableView`和`QTreeView`都支持虚拟化。6. **减少绘制开销**:尽量减少不必要的界面重绘,比如使用`QWidget::update()`而不是`QWidget::paintEvent()`,后者会引发更频繁的窗口重绘。9. **使用高效的数据存储**:如果数据来源于数据库或者其他存储介质,确保数据检索是高效的,可以使用适当的数据库查询优化。

qq_43517257的博客 1790

QTreeView使用总结13,自定义model示例,大大优化性能和内存

前面简单介绍过Qt的模型/视图框架,提到了Qt预定义的几个model类型:QStringListModel:存储简单的字符串列表QStandardItemModel:可以用于树结构的存储,提供了层次数据QFileSystemModel:本地系统的文件和目录信息QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel:存取数据库数据。

逆枫 -- C++/Qt工程师、创业者 2万+

Qt | QListView美化-卡片拖拽-动画效果

源码可以在Github上下载,后续会不断更新,并且提供C++版本组件,欢迎各位关注。 1. Github

m0_72760466的博客 1278

Qt—QStandardItemModel

QStandardItemModel是Qt库中的一个类,它是QAbstractItemModel的一个子类。它可以用来存储和管理数据,并且可以通过Qt视图类(如QListView、QTreeView等)来显示这些数据。它采用了树型结构存储数据,每一个节点对应一个QStandardItem对象。使用这个类可以简化数据的管理和显示过程。

QtCompany的博客 1541

QT界面设计

漂亮的界面+优雅的代码分离+灵活的皮肤设定 = Qt Style Sheets 哪里去找? help-->Qt5.9-->Qt Widgets-->Qt Style Sheets 或者 help-->索引-->搜索-->Qt Style Sheets

YongYu_IT的专栏 662

QT5 卡片布局(QLayout)

QT5实现的卡片布局,基于QLayout的CardStackLayout,实现动画效果。

QTreeView处理大量数据

如何使QTreeView快速显示1000万条数据,并且内存占用量少呢?这个问题困扰我很久,在网上找了好多相关资料,都没有找到合理的解决方案,今天在这里把我的解决方案提供给朋友们,供大家相互学习。 我开始使用的QTreeWidget 控件来显示我的数据,发现该控件在显示10000行以下的数据还可以应付的过来,但超过10000条,就明显感觉到屏幕刷新就会有卡的现象,而且占据内存很大,虽然操作起来简单

1万+

qt快速加载并动态显示大容量数据能够加载千万行量级的数据

本工程介绍了如何使用QT的tablewideget快速加载大容量的数据,快速加载千万行文本并且动态显示出来,同时介绍了如何根据鼠标事件而显示不同内容

【遥感数字图像处理(朱文泉)】第四章 变换域处理方法

变换域的处理方法

甜子yu的博客 4311

Qt 大数据列表展示

Qt中使用QListWidget, QTableWidget,QTreeWidget(只考虑最大3层)自定义子widget来展示数据的时候,通常子widget的个数达到了上千加载展示就会很慢,而且很耗内存。原因是new出来的widget太多了。下面的解决方案希望能帮助你。原理: 其实一个列表展示给用户看的高度是很有限的不会超过一个屏幕的高度,而这个高度只需要很少的子widget就可以填充满,所以

Keep It Simple, Stupid 7588

Twin Builder CFD 示例:构建、验证和评估静态 ROM

通过我们构建、验证和评估静态 ROM 的综合指南,释放 Twin Builder CFD 的潜力。

jkh920184196的博客 1102

Qt | 以卡片的形式展示内容,解决了增加、删除卡片后QScrollWidget大小无法自适应问题(QT的事件处理机制)

原本是想用python实现一个内容展示区,具体内容是通过一张张小卡片来呈现的,其中包括增加卡片和删除卡片的功能. 基本展示框架的实现有两种方案. 方案一:使用ListView+委托(复用委托的paint方法)实现. 方案二:使用一个QScrollWidget+frame实现,frame充当卡片. 其中,方案一的实现由于用到了paint,所以卡片上的小部件是无法交互的,这也阻碍了后面代码功能的实现,遂放弃.方案二的实现似乎更简单暴力,但是在实现卡片添加和删除的时候,遇到了ScrollWidget(滚动区

m0_72760466的博客 2240

拼多多Android面试题及参考答案

Android 系统采用分层架构设计,从底层到上层依次为Linux 内核层硬件抽象层(HAL)系统运行时库层应用框架层应用层,共五层。位置:Linux 内核层与系统运行时库层之间,是“硬件与系统的隔离层”。核心作用:屏蔽硬件差异,为上层提供统一的硬件访问接口,解决 Android 开源与硬件厂商闭源的矛盾,具体功能如下:硬件能力抽象:将硬件的具体实现(如不同厂商的摄像头芯片、音频芯片)封装为标准化接口(如Camera HALAudio HAL。

大模型大数据攻城狮的专栏 1284

第 9 篇:TMC2240 速度调节实战|STEP 信号生成 + 平滑调速算法(无抖动)

摘要:本文详细介绍了TMC2240电机调速方案,重点解决调速时电机抖动、丢步等问题。核心原理是通过STEP信号频率控制转速,结合平滑加减速算法避免电流冲击。文章从速度与STEP频率的数学关系入手,提供了定时器生成固定频率STEP信号的实现代码,并给出不同转速下的频率参数表。针对42步进电机+256微步细分场景,详细讲解了定时器初始化、中断处理及固定转速设置方法,帮助开发者实现稳定调速。全文包含可直接复用的代码片段,适合嵌入式开发者快速实现电机平滑调速功能。

BackCatK Chen的博客 1035

Qt扫盲-QTreeWidget理论总结

从 这个Tree 来看,一个tree 可以设置 多列,每一个列其实就是展示的一些属性。一个Tree 可以有多行,每一行之间有一些关系,要么是并列,要么就是父子的关系,这样才能形成树的样子嘛。每个行其实就是一个 QTreeWidgetItem 。QTreeWidget 其实就是一个管理 QTreeWidgetItem 的容器而已。像内部的 每行具体的表现形式都是由QTreeWidgetItem 来确定的。同时会有一个 表头,也就是 上面的 名称-时间-大小 这行来管理每列的宽度和排序的一些功能样式展开之类的。

洪源兄 3165

第六部分:VTK进阶(第182章 体素化与隐式建模)

本文系统介绍了基于点云构建有符号距离场(SDF)的核心原理与VTK实现方法。主要内容包括:1)SDF的基本概念与算法原理,通过体素网格隐式表达几何表面;2)关键参数配置指南,如体素分辨率、边界范围和影响半径的选择;3)点云法向估计、SDF计算与等值面提取的完整工作流程;4)常见问题解决方案与性能优化建议。文中提供了详细的C++代码示例,展示从随机点云生成SDF并重建曲面的完整过程,特别强调了法向估计、参数调优和后处理等关键环节。该方法适用于点云表面重建、几何处理等应用场景。

仰望星空的博客 528

QTreeWidget显示类似QTableWidget网格的实现

QTreeWidget显示类似QTableWidget网格的实现

qq_20711029的博客 1926

3D 结构光相机工作原理全解 | 从编码投射到三维重建,原理 + 代码 + 工业实战一站式掌握

本文从3D结构光相机的整体架构出发,层层拆解了编码投射、相位计算、相位解包裹、三维重建的核心原理,补充了工业实战所需的公式推导、代码实现、参数调优、设备选型和案例落地,核心总结如下:相位是3D结构光成像的“灵魂”,其精度、稳定性直接决定3D重建的效果,包裹相位与绝对相位的区分的是工业实战的基础;编码投射的核心是“正弦条纹+格雷码条纹”组合,正弦条纹负责高精度相位提取,格雷码条纹辅助解包裹,适配工业场景的精度与稳定性需求;

getusushu的博客 414

uniapp(全端兼容) - 最新详细实现瀑布流列表及布局设计,uni-app全平台兼容的瀑布流示例,view容器自适应瀑布流布局,图片瀑布流布局、图像+文字卡片式瀑布流,类似小红书(详细示例代码)

uni-app,瀑布流,安卓app,苹果app,微信小程序,移动端,H5网页,手机网站,微信小程序、支付宝小程序,手机app端,nvue,瀑布流布局,如何实现图片瀑布流,怎么做文字+图像瀑布流,uniapp仿小红书笔记瀑布流效果,uniAPP仿快手抖音布局列表,瀑布流列表详细教程,编译所有平台都能用的瀑布流列表,商品瀑布流列表,图文列表,小程序瀑布流,页面触底懒加载,瀑布流插件,详细示例代码教程,uni-app H5实现瀑布流效果,uniapp小程序做瀑布流,纯css瀑布流,用uniapp写瀑布流商品列表,

✅ 精选推荐文章 1万+

QListView数据显示优化速度

最近在为自己写的文件管理器做文件搜索功能,搜索过程中将搜索到的文件放入列表框中。考虑到搜索文件比较多没有使用QListWidget而是使用使用。搜到文件不多时大概几十个时显示速度很快,当搜索整个磁盘时这是有几百万个文件时,显示速度的特别慢,鼠标一直在转圈。,导致QListView内部对所有的项计算一遍,200多万条数据就需要计算200多万次,速度当然很慢了。后,QListView只计算可显示的几十行,速度很快,达到设计要求。经过调试发现QListView有一个属性。显示也就几十行,不应该很慢。

flysnow010的博客 863

SAP AI Core 高阶能力实战参考

文章摘要:本文探讨了在企业SAP架构中整合GenAI技术的工程化实践,重点分析了SAP AI Core的高阶能力应用。作者指出,企业级AI应用需解决多轮对话管理、Git同步工作流、提示词治理等核心问题,而非简单接入大模型API。技术方案展示了如何通过SAP AI Core的orchestration service管理对话历史,利用GitOps实现AI资产版本控制,并强调prompt template在审计合规中的治理价值。全文以SAP技术栈(ABAP/RAP/Fiori)为背景,阐释了AI能力与企业核心系

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 104

Qt 下快速读写Excel指南

Qt Windows 下快速读写Excel指南很多人搜如何读写excel都会看到用QAxObject来进行操作,很多人试了之后都会发现一个问题,就是慢,非常缓慢!因此很多人得出结论是QAxObject读写excel方法不可取,效率低。 后来我曾试过用ODBC等数据库类型的接口进行读写,遇到中文嗝屁不说,超大的excel还是会读取速度慢。 最后,看了一些开源的代码后发现,Windows下读取exc

尘中远的程序开发记录 6万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

上一篇: QT 带 进度条 解压缩文件方法 - QuaZip
下一篇: QT-功能点记录
LearnLHC
博客等级 码龄10年 183粉丝 149原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值