QStandardItemModel 和 QTableView
概念和方法
QStandardItemModel 是以项为基本数据单元的模型类,每个项是一个 QStandardItem 对象。
使用时需包含<QMainWindow>和<QStandardItem>头文件
这部分的方法和内容比较庞杂,快速过一遍,然后零帧起手做个案例,带着大家理解一下。
●设置行数和列数 QStandardItemModel 以二维数组的形式存储项数据,所以可以设置行数和列数。
cpp
void setRowCount(int rows) //设置数据模型的行数
void setColumnCount(int columns) //设置数据模型的列数
如果设置的列数大于 1,模型就是表格模型;如果设置的列数为 1,模型就可以看作列表模型。
- 设置项
cpp
void setItem(int row, int column, QStandardItem *item)
void setItem(int row, QStandardItem *item) //用于列表模型
- 获取项
cpp
QStandardItem *item(int row, int column = 0) //根据行号和列号返回项
QStandardItem *itemFromIndex(const QModelIndex &index) //根据模型索引返回项
//函数 indexFromItem()根据项返回其模型索引,其定义如下:
QModelIndex indexFromItem(const QStandardItem *item)
- 添加行或列
cpp
void appendRow(const QList<QStandardItem *> &items) //用于表格模型
void appendRow(QStandardItem *item) //用于列表模型
- 插入行或列
cpp
void insertRow(int row, const QList<QStandardItem *> &items) //用于表格模型
void insertRow(int row, QStandardItem *item) //用于列表模型
案例学习
实现表格数据展示 demo, 如下图所示

开发讲解
1 初步实现
为了实现QTableview和QStandarditemModel的组合,我们在MainWindow中添加成员tableView和model
cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
//用表格的方式展现数据
QTableView *tableView;
//用标准模型基类存储数据
QStandardItemModel *model;
};
实现构造函数
cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置窗口标题和大小
setWindowTitle("QStandardItemModel 与 QTableView 教学案例");
resize(600, 400);
// 创建表格视图
tableView = new QTableView(this);
// 创建模型
model = new QStandardItemModel(this);
// 设置表格视图为中心部件
this->setCentralWidget(tableView);
// 初始化模型
model->setHorizontalHeaderLabels({"姓名", "年龄", "职业"});
// 添加数据
QList<QStringList> data = {
{"张三", "25", "工程师"},
{"李四", "30", "设计师"},
{"王五", "28", "教师"},
{"赵六", "22", "学生"}
};
for (const QStringList &rowData : data) {
QList<QStandardItem*> items;
for (const QString &field : rowData) {
QStandardItem *item = new QStandardItem(field);
//设置文字居中
item->setTextAlignment(Qt::AlignCenter);
items.append(item);
}
model->appendRow(items);
}
// 设置模型到视图
tableView->setModel(model);
// 设置列宽自适应内容
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
// 允许编辑
tableView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
// 启用排序功能
tableView->setSortingEnabled(true);
}
运行后可以看到通过tableview和model组合,可以实现显示表格数据显示

2 实现增删
我们在MainWindow中增加槽函数的声明,以及新增两个按钮
cpp
private slots:
//增加行
void insertRow();
//删除行
void deleteRow();
private:
QPushButton *insertButton;
QPushButton *deleteButton;
完善MainWindow的构造函数列表,将model, tableview, 以及按钮的创建放在构造列表中,注意原来在构造函数中创建的要删除
cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
tableView(new QTableView(this)),
model(new QStandardItemModel(this)),
insertButton(new QPushButton("插入行",this)),
deleteButton(new QPushButton("删除行",this)){
//...省略
}
另外我们需要创建一个QWidget用来做中心部件的显示,将原来tableview设为中心部件的逻辑注释掉,
并且基于中心部件创建一个垂直布局,
垂直布局里放入tableview和一个水平布局,水平布局里再加入两个按钮。
cpp
// 创建一个中心widget
QWidget *centralWidget = new QWidget(this);
// 基于中心widget创建垂直布局作为主布局
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
// 向布局中加入tableview
mainLayout->addWidget(tableView);
// 创建按钮布局为水平布局
QHBoxLayout *buttonLayout = new QHBoxLayout();
// 加个伸缩因子,也就是弹簧,保证按钮被挤压到右侧
buttonLayout->addStretch(); // 右对齐按钮
//添加插入按钮
buttonLayout->addWidget(insertButton);
//添加删除按钮
buttonLayout->addWidget(deleteButton);
//将按钮布局加入到主布局里
mainLayout->addLayout(buttonLayout);
//最后设置中心部件为中心widget
setCentralWidget(centralWidget);
// 连接信号与槽
connect(insertButton, &QPushButton::clicked, this, &MainWindow::insertRow);
connect(deleteButton, &QPushButton::clicked, this, &MainWindow::deleteRow);
实现插入行的逻辑
cpp
void MainWindow::insertRow()
{
// 获取当前行数
int row = model->rowCount();
// 可以选择在特定位置插入,这里选择在末尾插入
QList<QStandardItem*> items;
// 提示用户输入新行的数据
bool ok;
//获取用户姓名
QString name = QInputDialog::getText(this, "插入行", "请输入姓名:", QLineEdit::Normal, "", &ok);
if (!ok || name.isEmpty()) return;
//获取用户年龄
QString age = QInputDialog::getText(this, "插入行", "请输入年龄:", QLineEdit::Normal, "", &ok);
if (!ok || age.isEmpty()) return;
//获取用户职业
QString profession = QInputDialog::getText(this, "插入行", "请输入职业:", QLineEdit::Normal, "", &ok);
if (!ok || profession.isEmpty()) return;
auto name_item = new QStandardItem(name);
name_item->setTextAlignment(Qt::AlignCenter);
auto age_item = new QStandardItem(age);
age_item->setTextAlignment(Qt::AlignCenter);
auto profession_item = new QStandardItem(profession);
profession_item->setTextAlignment(Qt::AlignCenter);
items.append(name_item);
items.append(age_item);
items.append(profession_item);
model->appendRow(items);
// 可选:自动调整列宽
tableView->resizeColumnsToContents();
}
实现删除逻辑
cpp
//删除行
void MainWindow::deleteRow()
{
// 获取当前选中的行
QModelIndexList selectedIndexes = tableView->selectionModel()->selectedRows();
if (selectedIndexes.isEmpty()) {
QMessageBox::warning(this, "删除行", "请先选择要删除的行!");
return;
}
// 确认删除
auto reply = QMessageBox::question(this, "删除行", "确定要删除选中的行吗?",
QMessageBox::Yes|QMessageBox::No);
// 取消则返回
if (reply != QMessageBox::Yes) {
return;
}
QList<int> rows;
//selectedIndexes存储的时索引列表,可以根据索引获取行号
for(const QModelIndex& index: selectedIndexes){
//根据ModelIndex获取行号, 放入列表
rows.append(index.row());
}
//对行号从大到小排序,因为删除要从下往上删除
//从行号大的向上删除,保证其他待删行号不会受影响
//greater为仿函数,接受两个参数实现从大到小排序
std::sort(rows.begin(),rows.end(),std::greater<int>());
// 删除选中的行,从后往前删
for(auto row: rows){
model->removeRow(row);
}
}