应使用自定义QAbstractTableModel配合懒加载与分页,避免QStandardItemModel全量驻留;模型只响应data()等调用并按需提供可视数据,配合关闭自动列宽、启用uniformRowHeights等性能优化。

QTableView直接setModel()加载百万行一定会卡死
Qt默认的 QStandardItemModel 是全量内存驻留模型,插入100万行 QStandardItem 时,不仅触发百万次内存分配,还会在每次插入时触发视图重排、信号发射、尺寸计算。实测在普通笔记本上,50万行就可能卡住5秒以上,UI线程完全冻结。
根本问题不在QTableView本身,而在于你让模型承担了不该承担的事:它本不该存全部数据,只该按需提供当前可视区的数据。
- 别用
QStandardItemModel做大数据载体 —— 它不是为这个设计的 - 别在主线程里循环调用
appendRow()或setItem() - 别试图“优化插入速度”,方向错了,再快也救不了内存和信号风暴
必须用自定义QAbstractTableModel + 懒加载 + 数据分页
核心是把真实数据(比如 std::vector 或数据库游标)和视图解耦,模型只响应 data()、rowCount() 等虚函数调用,且只查当前需要的几行(通常最多几十行)。
示例关键逻辑:
class LargeDataModel : public QAbstractTableModel {
std::vector<mydata> *m_data; // 只存指针或索引,不复制
int m_totalCount;
public:
QVariant data(const QModelIndex &index, int role) const override {
if (!index.isValid() || role != Qt::DisplayRole)
return {};
int row = index.row();
if (row = m_totalCount) return {};
// 这里只取单行数据,不做遍历、不建对象
return QString::number(m_data->at(row).id);
}
int rowCount(const QModelIndex &) const override { return m_totalCount; }
int columnCount(const QModelIndex &) const override { return 3; }
};
</mydata>
-
m_data推荐用std::vector或 mmap 文件映射,避免拷贝 - 如果数据来自数据库,
data()内部应走预编译语句 + offset/limit 查询,或使用游标缓存最近几百行 - 务必重写
headerData(),否则表头可能异常 - 不要在
data()里做耗时操作(如磁盘读、网络请求),可配合异步加载+占位符
必须开启QTableView的性能开关
即使模型写对了,缺几个设置仍会拖慢滚动和渲染:
- 关闭自动列宽:
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive)(别用ResizeToContents) - 禁用整行选中高亮(如果不需要):
tableView->setSelectionBehavior(QAbstractItemView::SelectItems) - 关闭动画:
tableView->verticalScrollBar()->setSingleStep(20)配合setAnimated(false) - 启用缓存策略:
tableView->setUniformRowHeights(true)(所有行等高,跳过逐行测量) - 慎用委托(
QStyledItemDelegate):自定义paint()若含复杂计算或字体测量,会显著降低帧率
滚动时卡顿?大概率是data()被反复调用且没缓存
QTableView 在滚动过程中会高频调用 data() 查几十个坐标(包括即将进入视口的行)。如果每次调用都触发一次 vector 查找或 DB 查询,立刻变卡。
解决方法:
- 对
std::vector数据,at()是 O(1),没问题;但若底层是 map/set 或需要字符串解析,就要加行级缓存(例如QCache) - 对数据库场景,缓存「当前页 ±2 页」的数据块(比如每页 500 行),
data()直接查缓存,后台线程按需预取 - 用
QElapsedTimer在data()开头结尾打点,确认单次调用是否超 0.1ms —— 超了就得优化或缓存 - 注意:
QVariant构造开销不小,避免在data()里频繁构造QPixmap、QDateTime等重型对象
真正跑得顺的百万级表格,模型里没有一行“加载中”,也没有一毫秒等待,只有指针偏移和缓存命中。细节都在 data() 的实现里,而不是框架选型上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











