目录
1.前言
数据展示使用最多的就是table控件了,在QT编程中,使用比较多的无外乎两种QTableWidget和QTableView,前者是开箱即用的封装型控件,后者是灵活的模型视图架构核心,二者的选择直接决定表格的性能、扩展性和维护成本。
它们的关系如下:
| 控件 | 核心定位 | 继承关系 | 架构类型 |
|---|---|---|---|
QTableWidget |
封装了 QTableView + 私有默认模型,提供 Item 直接操作接口,开箱即用 |
QTableView → QAbstractItemView |
Item-Based(基于项) |
QTableView |
纯视图控件,无内置数据存储,需绑定自定义模型(如 QAbstractTableModel) |
QAbstractItemView |
Model-View(模型视图分离) |
简单来说:
QTableWidget = QTableView + 内置默认模型 + QTableWidgetItem 操作接口
QTableView 是 "空壳视图",必须搭配模型才能使用,灵活性更高。
2.QTableWidget快速插入数据方法
核心思路:关闭所有非必要 UI 开销,批量一次性插入,避免动态扩展和中间重绘。
- 禁用 UI 更新 + 屏蔽信号(避免每插一条数据触发重绘 / 事件);
- 提前设置总行 / 列数(替代
insertRow,避免动态扩容开销); - 批量创建 Item(减少
new调用和setText重复操作); - 禁用排序 / 自动滚动(插入后统一处理)。
示例代码:
cpp
#include <QTableWidget>
#include <QElapsedTimer>
#include <QDebug>
void efficientInsertMediumData(QTableWidget* table, int totalRows, int totalCols) {
QElapsedTimer timer;
timer.start();
// 1. 保存原始状态(插入后恢复,不影响后续使用)
const bool oldUpdate = table->updatesEnabled();
const bool oldSignal = table->signalsBlocked();
const bool oldSort = table->isSortingEnabled();
const bool oldScroll = table->verticalScrollBar()->isEnabled();
// 2. 关闭所有性能消耗项
table->setUpdatesEnabled(false); // 核心:禁止UI重绘
table->blockSignals(true); // 屏蔽 itemChanged 等信号
table->setSortingEnabled(false); // 关闭实时排序
table->verticalScrollBar()->setEnabled(false); // 禁用滚动条(避免滚动触发重绘)
// 3. 一次性预留行列空间(关键:避免动态扩展)
table->setRowCount(totalRows);
table->setColumnCount(totalCols);
// 4. 批量插入数据(循环内仅做必要操作)
for (int row = 0; row < totalRows; ++row) {
// 可选:复用Item模板(如果数据格式固定,减少new开销)
for (int col = 0; col < totalCols; ++col) {
// 直接构造时赋值,避免单独调用 setText
QTableWidgetItem* item = new QTableWidgetItem(
QString("R%1-C%2").arg(row).arg(col)
);
// 批量设置属性(如禁用编辑、对齐方式),避免循环外重复操作
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
item->setTextAlignment(Qt::AlignCenter);
table->setItem(row, col, item);
}
}
// 5. 统一恢复状态+一次性处理UI(仅触发1次重绘)
table->verticalScrollBar()->setEnabled(oldScroll);
table->setSortingEnabled(oldSort);
table->blockSignals(oldSignal);
table->setUpdatesEnabled(oldUpdate);
// 统一调整列宽(最后执行,避免中间重绘)
table->resizeColumnsToContents();
qDebug() << "插入" << totalRows << "行数据耗时:" << timer.elapsed() << "ms";
}
// 调用方式
// QTableWidget* table = new QTableWidget(this);
// efficientInsertMediumData(table, 100000, 5); // 10万行耗时≈30ms
3.QTableView高效方案
QTableWidget 是 Item-based 架构 (每个单元格对应一个 QTableWidgetItem),10 万行以上会占用大量内存(每行 5 列≈50MB+),且渲染压力大,此时必须切换到 Model-View 架构 (QTableView + 自定义模型),仅渲染可见区域,性能提升 100 倍以上。
- 内存占用骤降(仅缓存可见行数据,100 万行也只占几 MB);
- 渲染无延迟(只绘制当前屏幕可见的 10-20 行);
- 支持动态数据加载(分页、懒加载)。
示例代码:
cpp
#include <QTableView>
#include <QAbstractTableModel>
#include <QElapsedTimer>
#include <QDebug>
// 自定义模型:仅提供可见行数据,不预加载所有Item
class MassDataModel : public QAbstractTableModel {
Q_OBJECT
public:
explicit MassDataModel(int totalRows, int totalCols, QObject* parent = nullptr)
: QAbstractTableModel(parent), m_totalRows(totalRows), m_totalCols(totalCols) {}
// 重写必要接口(模型核心)
int rowCount(const QModelIndex& parent = QModelIndex()) const override {
return m_totalRows; // 总数据行数(实际不加载,只返回计数)
}
int columnCount(const QModelIndex& parent = QModelIndex()) const override {
return m_totalCols;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override {
if (!index.isValid()) return QVariant();
// 仅当需要显示时,才生成数据(懒加载)
if (role == Qt::DisplayRole) {
return QString("R%1-C%2").arg(index.row()).arg(index.column());
}
// 批量设置单元格属性(避免在UI层重复操作)
else if (role == Qt::TextAlignmentRole) {
return Qt::AlignCenter;
} else if (role == Qt::ItemIsEditable) {
return false;
}
return QVariant();
}
private:
int m_totalRows; // 总数据行数
int m_totalCols; // 总列数
};
// 调用方式(100万行无压力)
void efficientInsertMassData(QTableView* view, int totalRows, int totalCols) {
QElapsedTimer timer;
timer.start();
// 1. 创建自定义模型(数据不预加载,瞬间完成)
MassDataModel* model = new MassDataModel(totalRows, totalCols, view);
// 2. 绑定模型到QTableView(模型视图分离)
view->setModel(model);
// 3. 优化视图性能
view->setUniformRowHeights(true); // 固定行高,减少布局计算
view->setSortingEnabled(false); // 如需排序,在模型中实现(更高效)
view->resizeColumnsToContents();
qDebug() << "初始化" << totalRows << "行数据耗时:" << timer.elapsed() << "ms";
}
// 使用示例
// QTableView* view = new QTableView(this);
// efficientInsertMassData(view, 1000000, 5); // 100万行耗时≈5ms
两种插入数据方法性能比较:
电脑配置:

插入100w行数据输出信息:

4.核心维度对比(关键差异)
| 对比维度 | QTableWidget | QTableView |
|---|---|---|
| 数据存储 | 依赖 QTableWidgetItem 存储每个单元格数据 |
无内置存储,数据由绑定的模型(如自定义 QAbstractTableModel)管理 |
| 数据绑定 | 手动创建 QTableWidgetItem 并 setItem() |
绑定模型(setModel()),模型与视图解耦 |
| 大数据量支持 | 差(1 万行以上卡顿 / 内存溢出,需大量优化) | 优(百万行级无压力,懒加载仅渲染可见行) |
| 扩展性 | 弱(仅支持简单编辑 / 样式,自定义逻辑需绕开封装) | 强(重写模型接口支持任意数据源 / 自定义渲染 / 业务逻辑) |
| 多视图共享数据 | 不支持(需手动同步多个 QTableWidget 数据) |
支持(一个模型绑定多个视图,数据变化自动同步) |
| 自定义单元格渲染 | 依赖 setCellWidget()(效率低、内存占用高) |
模型 data() 函数通过角色(如 Qt::DecorationRole)实现(高效) |
| 使用复杂度 | 低(开箱即用,无需子类化) | 高(需子类化模型,重写核心接口) |
| 性能(10 万行) | 耗时≈30ms(需禁用重绘 / 信号等优化) | 耗时≈5ms(模型懒加载) |
| 内存占用(10 万行) | ≈50MB+(每个 Item 占内存) | ≈几 MB(仅缓存可见行数据) |
5.典型使用场景
✅ 选 QTableWidget 的场景
- 简单静态表格(1000 行以内),无需复杂逻辑;
- 快速开发原型、临时表格展示(如小工具的配置项表格);
- 仅需基础功能(展示 / 简单编辑 / 排序),无自定义渲染 / 多数据源需求。
✅ 选 QTableView 的场景
- 大数据量展示(1 万行以上,尤其是 10 万 / 百万行);
- 多数据源整合(如同时对接数据库 / 文件 / 网络数据);
- 需自定义业务逻辑(如单元格验证、权限控制、树形表格);
- 多视图共享同一数据源(如主窗口表格 + 子窗口详情页同步);
- 实时动态数据更新(如监控面板、股票行情)。