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的存储模型示意图

ModelIndex.png

需要注意的是,对于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中。

未完待续........