在上一篇文章中,已经对Qt模型视图框架有了一个大致的了解,这篇文章我们主要来看一下模型基类QStandardItemModel的用法。
一般地,使用QStandardItemModel作为数据模型已经足够满足我们的需求了。这是因为它实现了QAbstractItemModel接口,这意味着该模型可以为任何支持该接口的视图中提供数据(例如QListView、QTableView和QTreeView,以及您自己的自定义视图)。
对于性能和灵活性,您可能想要子类化QAbstractItemModel来为不同类型的数据存储库提供支持。例如,QDirModel为底层文件系统提供了一个模型接口。
QStandardItemModel使用QStandardItem作为数据支撑。
1 QStandardItemModel常用方法介绍:
对于数据的操作,无非就是增删改查,我们就以这四个方面来对QStandardItemModel的方法进行介绍,因为QStandardItem是单个数据项的载体,所以这里我们只要能获取QStandardItem就可以很容易的获取数据。以下的数据操作,均指操作QStandardItem。
1.1 查询数据
1.1.1 item(int row, int column = 0) const
该接口可根据行号和列号返回QStandardItem指针。如果是一个树视图,通过这个接口,只能获取顶层item。想要获取下一分支的item可以使用QStandardItem的child接口。
1.1.2 invisibleRootItem() const
对于每个顶层item,其实它都是一个不可见item的子item,如果想要便利所有顶层item,可以使用如下方法。
QStandardItemModel* model = new QStandardItemModel; QStandardItem* rootItem = model->invisibleRootItem(); for (int i = 0; i < rootItem->rowCount(); ++i) { for (int j = 0; j < rootItem->columnCount(); ++j) { QStandardItem* item = rootItem->child(i, j); //do somting ... } }
当然,你也可以使用QStandardItemModel::rowCount和QStandardItemModel::columnCount来遍历顶层item,但如果你是递归遍历一棵树时,应该使用invisibleRootItem()的方法来遍历,因为这会为你遍历树提供一个统一的方法。否则你必须编写两个方法。
1.1.3 itemFromIndex(const QModelIndex &index) const
通过QModelIndex来获取item,QModelIndex 是一个用于定位item的类,关于这个类我会在后面的文章专门介绍。而且这个接口也是经常使用的,所以你最好记住它。
1.1.4 rowCount和columnCount可分别获取行数和列数
1.1.5 data(const QModelIndex &index, int role = Qt::DisplayRole) const
这个接口非常重要,它可以根据QModelIndex获取item中保存的数据,这对于快速检索出item所代表的数据是非常有用的。
因为一个item可以代表很多不同类型的数据,那么我们可以根据role这个参数来获取该item对应的某类数据,这里的默认参数是DisplayRole,即表示获取item其本身显示的数据。当然,如果你传递一个Qt::ToolTipRole,那么它将返回一个提示信息对应的数据。如果你想获取用户自定义类型,那么可以传递一个int类型,但这个类型要不小于Qt::UserRole。
data接口返回一个QVariant类型,这表明在item中可以存储QString,int,bool,Point,QRect,QVector3D等,也支持用户自定义类型,但你必须要先使用Q_DECLARE_METATYPE宏来向元对象系统进行注册声明。
与data接口相对应的是setData,用来设置数据。
1.1.6 findItems(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly, int column = 0) const
返回在给定列中使用给定标志匹配给定文本的item列表。
1.2 添加数据
1.2.1 appendRow(QStandardItem *item)
添加数据到模型的最后一行,与其相同的有insertRow(),insertColumn()。
该接口的另一个重载形式是appendRow(const QList&items) 其参数是一个QList列表,如果所使用的视图是一个table或者是一个tree,则该列表表示一行数据中的多列数据。
1.2.2 setRowCount(int) ,setColumnCount(int)
设置行数, 设置列数
1.3 修改数据
1.3.1 setItem(int row, int column, QStandardItem *item)
将item放置到表中
1.3.2 setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
设置index对应的item数据
1.4 删除数据
1.4.1 clear()
从模型中删除所有项。
1.4.2 removeRow()或removeColumn()
删除行,删除列。 在删除多行数据时,一定要注意索引问题。 请看一个删除文本为“apple”的程序
for (int i = 0; i < model->rowCount(); ++i) { QStandardItem* item = model->item(i); if (item->text() == "apple") { model->removeRow(i); } }
如果,这个列表中包含有多个有apple的item,则执行这部分代码时,程序不能正确的删除所有apple项。 不知道你有没有看出来,当从model中第一次删除了文本为"apple"的item后,model的行数就少了一行,且其后一项的索引自动的减一,当下一次再删除apple项时,很可能就跳过了apple项。
为了解决这个问题,可以使用倒叙的方法,即从列表的最后一项起,向前删除item,这是一个很不错的想法,而且也很实用。
你也可以使用findItme找出所有符合条件的item,然后删除它们。
2 示例程序
standarditemmodelwidget.h
#ifndef STANDARDITEMMODELWIDGET_H #define STANDARDITEMMODELWIDGET_H #include <QWidget> class StandardItemModelWidget : public QWidget { Q_OBJECT public: StandardItemModelWidget(QWidget *parent = 0); }; #endif // STANDARDITEMMODELWIDGET_H
standarditemmodelwidget.cpp
#include "standarditemmodelwidget.h" #include <QDebug> #include <QPushButton> #include <QStandardItemModel> #include <QVBoxLayout> #include <QListView> #include <QWidget> StandardItemModelWidget::StandardItemModelWidget(QWidget *parent) : QWidget(parent) { //创建界面 QVBoxLayout* layout = new QVBoxLayout(this); QStandardItemModel* model = new QStandardItemModel; QListView* view = new QListView; view->setModel(model); layout->addWidget(view); //向model中添加数据 QStringList textList; textList << "apple 1" << "apple 2" << "orange" << "banana" << "apple 3" << "red apple"; for(auto& text: textList) { QStandardItem *item = new QStandardItem(text); model->appendRow(item); item->setEditable(false);//设置每个item为不可编辑 } //检索所有的包含apple的数据项 const int customRole = Qt::UserRole; QList<QStandardItem*> appleItems = model->findItems("apple", Qt::MatchContains); foreach (QStandardItem* appleItem, appleItems) { //为apple项设置一个随机生成的颜色图标 QPixmap pixmap(25,25); QColor clr(qrand()%255, qrand()%255, qrand()%255); pixmap.fill(clr); appleItem->setIcon(QIcon(pixmap)); appleItem->setToolTip(clr.name()); appleItem->setData(clr.name(), customRole); } //当点击item时,在控制台输出其保存的颜色值 connect(view, &QListView::clicked, [=](const QModelIndex &index){ QStandardItem* item = model->itemFromIndex(index); qDebug() << item->data(customRole).toString(); }); //点击按钮删除所有apple项 QPushButton* btn = new QPushButton("删除以apple开头的项"); layout->addWidget(btn); connect(btn, &QPushButton::clicked, [=](){ QList<QStandardItem*> appleItems = model->findItems("apple", Qt::MatchContains); for (int i = appleItems.size()-1; i >= 0; --i) { model->removeRow(appleItems.at(i)->row()); } }); resize(200, 400); }
运行程序:
好了,今天的分享就到这里了,QStandardItemModel在模型中非常重要的,本篇文章只是带你了解了其比较重要的几个接口,关于它的更多用法,还需要你再深入理解。
最后也希望大家多多支持小豆君的创作,关注小豆君的公众号“小豆君Qt分享”,最新文章都会在公众号第一时间发布,也可加入C++\Qt交流群,一起学习。