1.1 Qt中MVD的基本概念解析
模型视图委托(MVD)是Qt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同。不同的是委托不是独立存在,而是包含在视图里面。
模型视图委托设计模式中,模型负责存储和管理数据;视图负责显示数据,其中界面的框架和基础信息是视图负责,具体数据的显示是委托负责;委托不仅仅负责数据的显示,还有一个重要的功能是负责数据的编辑,如在视图中双击就可以编辑数据。
1.2Model:存储数据的模型
Qt中的Model有许多种,均继承至QAbstractItemModel,常用的Model有以下几种QStandardItemModel,QFileSystemModel,QDirModel,QSqlTableModel等等几种模型,本例使用QStandardItemModel。
1.2.1数据在Model中的存储方式以及如何索引
对于QStandardItemModel,数据的存储是比较灵活的,存储形式可以是表格/列表/树等形式,但受View的约束,比如,listView就不能显示列表和树,Model中数据的索引是根据row与column,对于只有一行的数据,他的列索引号就是0。因此,对于QStandardItemModel不仅可以存储线性的列表,也可以存储表格形式的数据和树形结构的数据,对于树形结构的索引相对比较麻烦。
1.2.2Model的存储模型示意图
需要注意的是,对于treeModel,row的定义与其余两个模型有所不同,因此在索引时需要一层一层索引。下边看如下代码。
void Widget::initData()
{
model = new QStandardItemModel;
for(int i = 0; i <50; i++){
QStandardItem *item1 = new QStandardItem;
QStandardItem *item2 = new QStandardItem;
item1->setData(QVariant::fromValue(i),Qt::DisplayRole);
item2->setData(QVariant::fromValue(i+5),Qt::DisplayRole);
model->setItem(i,0,item1);
model->setItem(i,1,item2);
qInfo()<<i<<endl;
}
ui->listView->setModel(model);
ui->tableView->setModel(model);
ui->treeView->setModel(model);
}
上述代码中,对于不同的View设置相同的Model,Model中的数据是一张表格形式的,可以看到,对于listView只显示了Item1的内容,对于treeView和TableView显示出了表格的形式。
QStandardItem *itemFromIndex(const QModelIndex &index) const;
QStandardItem *item(int row, int column = 0) const;
以QModelIndex方式索引:需要知道QModelIndex,QModelIndex类用于定位数据模型中的数据,与用row,column定位相同,QModelIndex中也包含row,column值可供访问,这个类用作从QAbstractItemModel派生的项目模型的索引。项目视图、委托和选择模型使用索引来定位模型中的项目。
比较重要的一点是:
可以使用QModelIndex构造函数构造无效的模型索引。当引用模型中的顶级项时,无效索引通常用作父索引,模型索引引用模型中的项,并包含指定它们在那些模型中的位置所需的所有信息。QModelIndex通常是作为信号参数传递,通过传递的QModelIndex对象可以访问对应的项。
例如,QAbstractItemView的信号函数
void activated(const QModelIndex &index)
void clicked(const QModelIndex &index)
void doubleClicked(const QModelIndex &index)
void entered(const QModelIndex &index)
void iconSizeChanged(const QSize &size)
void pressed(const QModelIndex &index)
void viewportEntered()
1.3 View的基本使用
常见的View有ListView/TableView/TreeView/QHeaderView/等,分别用于显示列表形式/表格形式/树形结构/表头部分栏目等。View显示Model中的数据,Model/View结构决定了当Model中的数据更新了时,View中的数据也会自动更新,View的细节设置参考帮助手册。
1.4Delegate的基本用法
对于Qt中的MVD架构来说,Model用于存储数据,View用来展示数据,大多数教程对于Delegate的解释是Delegaet可以编辑数据并将数据回写到Model并展示,其实Delegate的功能不全是提供一个自定义或Qt内置的Item编辑窗体,对于不需要编辑的项目,Delegate也可以通过重绘,提供一个展示Item内容的功能,这就类似于View是数据显示的一个框架,骨干部分,Delegate也可以作为骨干部分的Item项目的展示形式的自定义。
1.4.1 使用编辑交互窗体实现
对于自定义代理,一般是继承至QStyledItemDelegate,对于编辑类型的代理,需要重载一下四个函数。
virtual QWidget *
createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
virtual void
setEditorData(QWidget *editor, const QModelIndex &index) const override
virtual void
setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
virtual void
updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
createEditor()函数返回一个QWidget对象指针,该Widget就是当编辑数据时,展示的Widget。一般对于简单的项,可以是一个QComboBox项,可以是QSpinBox项等等,也可以是自定义窗体。
//创建代理编辑组件
QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBox
editor->setFrame(false); //设置为无边框
editor->setMinimum(0);
editor->setMaximum(10000);
return editor; //返回此编辑器
setEditorData()函数用于给编辑窗体中设置Model中的数据,通过ModelIndex索引。
//获取数据模型的模型索引指向的单元的数据
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
spinBox->setValue(value); //设置编辑器的数值
setModelData()函数用户将用户编辑好的数据回写到MOdel中去。
//将代理组件的数据,保存到数据模型中
QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号
int value = spinBox->value(); //获取spinBox的值
model->setData(index, value, Qt::EditRole); //更新到数据模型
updateEditorGeometry() 该函数可以根据指定的样式,更新当前的编辑器。
//设置组件大小
editor->setGeometry(option.rect);
1.4.1 使用重绘实现
当一个项目需要比较复杂的样式展示而不需要编辑功能时,可以使用重绘实现。
同样继承至QStyledItemDelegate,不需要重写上述四个函数,只需要重写以下两个函数。
virtual void
paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
virtual QSize
sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
sizeHint()定义绘制区域的大小。
paint()完成绘制,绘制好的项会自动更新到View中。
未完待续........