一、基础知识
(以下基础知识来源于DeekSeek,我没它总结的好,就借用它的术语,只整理了文本格式,博主自己写的示例代码在第二章)
QStandardItemModel 是 Qt 框架中用于管理表格、列表或树形数据的通用模型类,基于 QAbstractItemModel 实现,提供了一种简单灵活的方式来处理结构化数据。
1. 基本特性
-
适用场景:适用于动态数据管理,支持表格、列表和树形结构。
-
核心优势:
-
提供预定义的
QStandardItem对象,封装单元格的数据、样式和角色。 -
支持动态增删行列、直接访问单元格内容。
-
自动处理数据更新和视图同步。
-
-
与
QAbstractItemModel的区别:-
QStandardItemModel是具体实现,可直接使用;QAbstractItemModel是抽象基类,需手动实现数据逻辑。 -
适合快速开发,但对超大数据量(如百万行)性能较低,此时建议自定义模型。
-
2. 核心用法
2.1 创建模型与填充数据
#include <QStandardItemModel>
#include <QTableView>
// 创建模型(3行3列)
QStandardItemModel *model = new QStandardItemModel(3, 3);
// 设置表头
model->setHorizontalHeaderLabels({"姓名", "年龄", "职业"});
model->setVerticalHeaderLabels({"行1", "行2", "行3"});
// 填充数据
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
QStandardItem *item = new QStandardItem(QString("数据%1-%2").arg(row).arg(col));
model->setItem(row, col, item);
}
}
// 关联到视图
QTableView tableView;
tableView.setModel(model);
tableView.show();
2.2 操作单元格
// 获取单元格项
QStandardItem *item = model->item(0, 0); // 第1行第1列
// 修改文本
item->setText("张三");
// 设置数据角色(如背景色)
item->setBackground(QBrush(Qt::yellow));
// 获取数据
QString text = item->text();
QVariant bgColor = item->data(Qt::BackgroundRole);
3. 动态增删行列
插入行/列
// 在第2行插入一行空数据
model->insertRow(1);
// 在第2行插入一行预填充数据
QList<QStandardItem*> rowItems;
rowItems << new QStandardItem("李四") << new QStandardItem("25") << new QStandardItem("工程师");
model->insertRow(1, rowItems);
// 插入列
model->insertColumn(1);
删除行/列
// 删除第2行
model->removeRow(1);
// 删除第2列
model->removeColumn(1);
4. 数据角色与样式
QStandardItem 支持通过 数据角色 控制显示属性:
| 角色 | 说明 | 示例代码 |
|---|---|---|
Qt::DisplayRole | 文本内容 | item->setText("数据") |
Qt::BackgroundRole | 背景色 | item->setBackground(Qt::yellow) |
Qt::ForegroundRole | 文本颜色 | item->setForeground(Qt::red) |
Qt::FontRole | 字体 | item->setFont(QFont("Arial", 12)) |
Qt::CheckStateRole | 复选框状态 | item->setCheckable(true) |
Qt::UserRole | 自定义数据 | item->setData(123, Qt::UserRole) |
示例:设置复杂样式
QStandardItem *item = new QStandardItem("重点数据");
item->setBackground(QBrush(QColor("#FFCC00"))); // 十六进制颜色
item->setForeground(QBrush(Qt::white));
item->setFont(QFont("微软雅黑", 10, QFont::Bold));
item->setTextAlignment(Qt::AlignCenter); // 居中对齐
item->setCheckable(true); // 启用复选框
item->setCheckState(Qt::Checked); // 默认勾选
5. 信号与槽:监听数据变化
模型提供信号以响应用户或程序的数据修改:
常用信号
-
itemChanged(QStandardItem*):单元格数据(文本、勾选状态等)变化时触发。 -
rowsInserted/rowsRemoved:行增删时触发。 -
columnsInserted/columnsRemoved:列增删时触发。
示例:实时打印修改内容
QObject::connect(model, &QStandardItemModel::itemChanged, [](QStandardItem *item) {
qDebug() << "修改位置: (" << item->row() << "," << item->column()
<< "), 新内容: " << item->text();
});
6. 树形结构支持
QStandardItemModel 可表示树形数据,通过父子关系组织:
构建树形数据
QStandardItem *rootItem = model->invisibleRootItem(); // 获取根节点
// 创建父节点
QStandardItem *parent = new QStandardItem("部门A");
rootItem->appendRow(parent);
// 添加子节点
QStandardItem *child1 = new QStandardItem("员工1");
QStandardItem *child2 = new QStandardItem("员工2");
parent->appendRow(child1);
parent->appendRow(child2);
在 QTreeView 中显示
QTreeView treeView;
treeView.setModel(model);
treeView.expandAll(); // 展开所有节点
treeView.show();
7. 性能优化
-
批量操作:使用
beginInsertRows()/endInsertRows()包裹多行插入,减少视图刷新次数。model->beginInsertRows(QModelIndex(), startRow, endRow); for (int i = startRow; i <= endRow; i++) { model->insertRow(i, new QStandardItem("数据")); } model->endInsertRows(); -
禁用视图刷新:在密集操作前禁用视图更新,操作完成后恢复。
tableView.setUpdatesEnabled(false); // 执行批量修改... tableView.setUpdatesEnabled(true); -
轻量级数据角色:避免在
data()中频繁计算复杂数据,尽量预存结果。
8. 与委托(Delegate)结合
通过自定义委托实现复杂渲染或编辑逻辑(如进度条、下拉框)。
示例:为特定列添加按钮
class ButtonDelegate : public QStyledItemDelegate {
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
if (index.column() == 2) { // 第3列显示按钮
QStyleOptionButton button;
button.rect = option.rect.adjusted(2, 2, -2, -2);
button.text = "点击";
button.state = QStyle::State_Enabled;
QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
};
// 应用委托
tableView.setItemDelegate(new ButtonDelegate);
9. 数据持久化
保存为 CSV
QFile file("data.csv");
if (file.open(QIODevice::WriteOnly)) {
QTextStream stream(&file);
for (int row = 0; row < model->rowCount(); ++row) {
QStringList rowData;
for (int col = 0; col < model->columnCount(); ++col) {
rowData << model->item(row, col)->text();
}
stream << rowData.join(",") << "\n";
}
file.close();
}
从数据库加载
QSqlQuery query("SELECT name, age, job FROM employees");
while (query.next()) {
QList<QStandardItem*> rowItems;
rowItems << new QStandardItem(query.value(0).toString())
<< new QStandardItem(query.value(1).toString())
<< new QStandardItem(query.value(2).toString());
model->appendRow(rowItems);
}
10. 常见问题
1. 如何清空模型?
model->clear(); // 清空所有数据(包括表头)
model->removeRows(0, model->rowCount()); // 仅清空行数据
2. 如何获取选中单元格?
通过视图的 selectionModel() 获取选中项:
QModelIndexList selected = tableView.selectionModel()->selectedIndexes();
for (const QModelIndex &index : selected) {
qDebug() << "选中单元格: " << index.data().toString();
}
3. 如何处理大数据性能问题?
-
使用
QAbstractTableModel自定义模型,避免QStandardItem内存开销。 -
分页加载数据,仅显示当前视图范围内的行。
-
使用
QSortFilterProxyModel进行排序和过滤,而非直接操作原始模型。
总结
QStandardItemModel 是 Qt 中快速实现表格/树形数据管理的利器,适合中小型数据集。通过灵活使用数据角色、信号槽机制和委托,可以轻松实现复杂交互和样式。对于超大数据量或高性能需求,建议结合自定义模型或代理模型优化架构。
二、示例代码
1、新建一个对话框,对话框dialog.h代码如下:
#ifndef DIALOG_H
#define DIALOG_H
#include <qdialog.h>
#include <qlabel.h>
#include <qtableview.h>
#include <qpushbutton.h>
#include <qstandarditemmodel.h>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
private:
void initUi();
void buildConnection();
void initLayout();
void initTableView();
void updateTableData();
void updateTableDataTest(int count);
enum {EN_Index = 0, EN_Name, EN_Address, EN_Hobby};
private:
int m_dataCount = 0;
QTableView *m_tableView = nullptr;
QStandardItemModel *m_itemModel = nullptr;
QPushButton *m_btnTest1 = nullptr;
QPushButton *m_btnTest2 = nullptr;
QPushButton *m_btnTest3 = nullptr;
QLabel *m_lbInfo = nullptr;
};
#endif // DIALOG_H
2、dialog.cpp代码如下:
#include "dialog.h"
#include <qboxlayout.h>
#include <QHeaderView>
#include <qdatetime.h>
const QString kTableViewStyleSheet1 =
"QHeaderView::section {background:#f8f8f8;color:#0a0a0a;border:1px solid #d5d9e0;}"
"QTableView {background:#ffffff;color:#0a0a0a;font-family:Microsoft YaHei;font:16px solid;font-weight:normal;"
"gridline-color:#ffffff;border:0px solid transparent;border-radius:0px;padding:0px 0px;}"
"QTableView:disabled {background-color:transparent;color:#868A92;}"
"QTableView::item {border-top:0px;border-bottom:1px;border-left:0px;border-right:0px;"
"border-style:solid;border-color:#d5d9e0;padding-left:8px;}"
"QTableView::item:selected:active {background:#E5F1FF;color:#0a0a0a;}"
"QTableView::item:selected:!active {background:#E5F1FF;color:#0a0a0a;}";
const QString kTableViewStyleSheet2 =
"QHeaderView::section {background:#f8f8f8;color:#0a0a0a;border:1px solid #d5d9e0;}"
"QTableView {background:#ffffff;color:#0a0a0a;font-family:Microsoft YaHei;font:16px solid;font-weight:normal;"
"gridline-color:#ffffff;border:0px solid transparent;border-radius:0px;padding:0px 0px;}"
"QTableView:disabled {background-color:transparent;color:#868A92;}";
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
initUi();
buildConnection();
initLayout();
}
Dialog::~Dialog()
{
}
void Dialog::initUi()
{
setWindowTitle("QTableView 测试");
setFixedSize(850, 800);
m_tableView = new QTableView(this);
m_itemModel = new QStandardItemModel(m_tableView);
m_btnTest1 = new QPushButton("测试1", this);
m_btnTest2 = new QPushButton("测试2", this);
m_btnTest3 = new QPushButton("测试3", this);
m_lbInfo = new QLabel(this);
m_btnTest1->setFixedSize(100, 32);
m_btnTest2->setFixedSize(100, 32);
m_btnTest3->setFixedSize(100, 32);
m_lbInfo->setStyleSheet("QLabel{color:#ee2032;font-size:14px;}");
m_lbInfo->setFixedHeight(32);
initTableView();
}
void Dialog::buildConnection()
{
connect(m_btnTest1, &QPushButton::clicked, this, [this] {
updateTableDataTest(1000);
});
connect(m_btnTest2, &QPushButton::clicked, this, [this] {
updateTableDataTest(10000);
});
connect(m_btnTest3, &QPushButton::clicked, this, [this] {
updateTableDataTest(100000);
});
}
void Dialog::initLayout()
{
QHBoxLayout *hLayBtn = new QHBoxLayout();
hLayBtn->setMargin(0);
hLayBtn->setSpacing(20);
hLayBtn->addWidget(m_btnTest1);
hLayBtn->addWidget(m_btnTest2);
hLayBtn->addWidget(m_btnTest3);
hLayBtn->addWidget(m_lbInfo, 0, Qt::AlignLeft |Qt::AlignVCenter);
hLayBtn->addStretch();
QVBoxLayout *mainLay = new QVBoxLayout(this);
mainLay->setMargin(15);
mainLay->setSpacing(0);
mainLay->addWidget(m_tableView, 0, Qt::AlignLeft |Qt::AlignTop);
mainLay->addStretch();
mainLay->addLayout(hLayBtn);
}
void Dialog::initTableView()
{
m_tableView->setStyleSheet(kTableViewStyleSheet1);
m_tableView->setModel(m_itemModel);
m_tableView->setFixedSize(820, 710);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
// 双击单元格可编辑
m_tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
// 隐藏序号列
m_tableView->verticalHeader()->hide();
QStringList colName;
colName << "序号" << "名字" << "地址" << "爱好";
m_itemModel->setHorizontalHeaderLabels(colName);
m_tableView->horizontalHeader()->setStretchLastSection(true); //最后一列自动占满
m_tableView->setColumnWidth(EN_Index, 90);
m_tableView->setColumnWidth(EN_Name, 120);
m_tableView->setColumnWidth(EN_Address, 200);
}
void Dialog::updateTableData()
{
m_itemModel->removeRows(0, m_itemModel->rowCount());
QStandardItem *pItem = nullptr;
for (int i = 0; i < m_dataCount; ++i) {
pItem = new QStandardItem(QString::number(i + 1));
pItem->setCheckable(true);
pItem->setCheckState(Qt::CheckState(i % 3));
m_itemModel->setItem(i, EN_Index, pItem);
pItem = new QStandardItem(QString("张三 %1").arg(i + 1));
m_itemModel->setItem(i, EN_Name, pItem);
pItem = new QStandardItem(QString("湖北省赤壁市第二中学"));
m_itemModel->setItem(i, EN_Address, pItem);
pItem = new QStandardItem("阅读,骑行,猫狗,跑步,冲浪,游泳,篮球");
//pItem->setBackground(QColor(0, 0, 96, 64));
m_itemModel->setItem(i, EN_Hobby, pItem);
}
}
void Dialog::updateTableDataTest(int count)
{
quint64 t1 = QDateTime::currentMSecsSinceEpoch();
m_dataCount = count;
updateTableData();
quint64 t2 = QDateTime::currentMSecsSinceEpoch();
m_lbInfo->setText(QString("添加%1条数据,耗时:%2ms").arg(m_dataCount).arg(t2 - t1));
}
3、界面如下:

6257

被折叠的 条评论
为什么被折叠?



