QTableView与QStandardItemModel的应用(含示例代码)

一、基础知识

     (以下基础知识来源于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、界面如下:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宏笋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值