概述
关于视图模型,Qt已经封装了几个方便我们使用的类:QListWidget、QTreeWidget和QTabWidget,这几个类特点是使用起来很方便,适合显示比较简单的数据,若是涉及到大量的数据要显示、以及对性能要求严格就得用到视图模型了。
模型
简单的说,模型是跟数据打交道的。以QAbstractListModel为例,我们需要子类化QAbstractListModel,并重写它提供的这几个函数:
int QAbstractItemModel::rowCount(const QModelIndex & parent = QModelIndex()) const
QVariant QAbstractItemModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const
重写的rowCount函数功能是返回总行数,这里我们返回数据集(m_data)的长度
int MyModels::rowCount(const QModelIndex & parent) const {
return m_data.count();
}
data函数功能是给外部提供获取数据的接口,不过这个函数是系统自动调用的,我们只需要实现其功能即可
QVariant MyModels::data(const QModelIndex & index, int role) const {
if (!index.isValid())
{
return QVariant();
}
int row = index.row();
if (role == Qt::DisplayRole)
{
QString fileName = m_data[row];
return fileName;
}
else if (role == Qt::DecorationRole)
{
return QPixmap("resources/music_icon.png"); // 为了方便,固定显示某张图片
}
return QVariant();
}
每个item它可能包含不同的内容(图片、文字等),role的意义是为了区分这些不同的内容,一般来说,Qt::DisplayRole代表文字,Qt::DecorationRole代表图片,更多关于角色的内容大家可以去查看文档。假设数据集是歌曲列表,那么我们在Qt::DisplayRole里返回歌曲名,在Qt::DecorationRole里面返回歌曲的图片。
注意:
当数据发生变化时(添加、删除等),需要手动发送dataChanged信号通知视图更新界面。
emit dataChanged(topLeft,bottomRight)
视图
视图负责把数据展示到界面上,这些数据是通过从模型中获取的。最简单的视图操作,我们只需把模型设置到视图上来就可以了。
QListView myListView;
myModels = new MyModels();
myListView.setModel(myModels);
至此,我们就可以从界面上看到展示的数据了,但是我们无法操作这些数据,需要添加委托才能操作数据。
委托
只有模型和视图的话,用户是不能对视图上显示的内容做操作的,如果需要对视图内容进行操作或者是要在视图上显示比较复杂的item时就需要子类化委托(Delegate),并重写它的一些方法。下面列出常用的需要重写的几个方法:
QWidget * QAbstractItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
void QAbstractItemDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
void QAbstractItemDelegate::destroyEditor(QWidget * editor, const QModelIndex & index) const
createEditor创建一个编辑器对象,这个对象是我们展现给用户去操作视图内容的对象,这里我创建了一个MyWidget对象,在MyWidget类添加要显示的控件以及操作的业务逻辑。
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const{
QWidget *myWidget = new MyWidget(parent); // MusicOperation为自己创建的QWidget类
return myWidget;
}
我们重写委托的目的是为了操作视图上显示的数据的,而数据是由模型进行管理的,这里委托提供了一个方便的接口setEditorData,通过这个接口可以把模型里的数据设置到编辑器上,有了数据,我们就可以通过编辑器(界面)去修改数据了。
void MyDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const{
editor->setItem(index.model()->m_data[index.row()], index);
}
参数一为createEditor返回的widget,参数二为模型索引,通过这个参数可以取到数据集中的数据
我们在destroyEditor删除先前创建的编辑器widget。
void MyDelegate::destroyEditor(QWidget * editor, const QModelIndex & index) const{
editor->deleteLater();
}
最后,把委托关联到listview。
MyDelegate *delegate = new MyDelegate(parent);
myListView.setItemDelegate(delegate);
我们需要显式地调用视图的openPersistentEditor方法才能把编辑器加载出来
myListView.openPersistentEditor(index)
它会自动调用我们重写的createEditor方法、setEditorData方法。同理,关闭编辑器调用closePersistentEditor就可以了,它会自动调用我们重写的destroyEditor方法。