数据项中引入模型/视图架构,可以方便的将数据与表现层分开。
-------------------------------------
模型Model:一般来说,Model里面并不真正存储数据(数据少的话也可以直接存储在Model里),只是负责从诸如磁盘文件,数据库,网络通讯等获得源数据,并提供给View,View对数据进行修改,然后再通过Model更新源数据。
Model 另一个重要工作时为源数据添加索引(ModelIndex)。列表形式采用row/colum编号,树形式为建立父子间的层次关系而多添加了一个ParentIndex索引。
当View需要显示数据或者需要View中的指定数据项时,通过模块索引(ModelIndex)从Model中获取数据,如果View中添加了自定义“委托”,要通过调用mapFromSource
返回和源Model相映射的“委托模块”的索引来获取数据。
-------------------------------------
为每个视图都提供了一个默认的委托——可以使用自定义的委托来替换显示每一项,并为可编辑项提供一个合适的编辑器。
-------------------------------------
模型:用于存储数据项(data item)。
纯粹的视图部件:QListView、QColumnView、QTableView、QColumnView和QTreeView,必须为视图提供一个模型与之配合。
-------------------------------------
便利的窗口部件:QListWidget、QTableview和QTreeWidget,都有自己的内置模型并能直接使用。
QComboBox:既是一个便利窗口部件也是一个视图部件,即既能直接使用(内置的模型),也可当做视图(外部提供合适模型)。
-------------------------------------
QStyledItemDelegate委托:所有的标准视图都提供了一个默认的QStyledItemDelegate委托(显示视图中各个项并为可编辑的项提供一个合适的编辑器)。可以自定义委托,控制视图中的各个项的显示和编辑。
-------------------------------------
对于有数百或数千个项的数据集,假设数据集在任何时候仅仅显示在一个部件中,那么选择便利窗口部件(内置模型视图部件)比较合适。
-------------------------------------
尽管有统一的应用程序编程接口,但仍有两种不同类型的模型:行列特性表格模型和父子关系的树模型。
-------------------------------------
QStandardItemModel:是一个可被当成列表模型、表格模型或树模型来使用的通用模型。它提供了一个基于项的API。对于能够直接把数据恰好填充入列表、表格或树模型的各个数据项中,并能够直接使用或仅需做微小改动即可的情况,使用QStandardItemModel是理想的选择。
QAbstractTableModel:不保存数据,用户需要从这些类派生出子类,并在子类中定义某种数据结构来保存数据。与此不同,类QStandardItemModel负责保存数据,每个数据项被表示为类QStandardItem的对象。
QStandardItem数据项:一个数据项由若干个『角色/*字体、颜色等信息*/,数据子项』对组成,该类负责保存、访问这些数据。该类的内部定义了一个类型为QVector的容器,每个容器元素本质上存放一个『角色,数据子项』对,且各个角色可能对应具有不同类型的数据子项。
void setData ( const QVariant & value, int role)//将『role, value』对存入QStandardItem对象
QVariant data ( int role = ) const //读取角色role对应的数据子项
QStandardItemModel将类QStandardItem表示的数据项组织起来,形成列表、表格、树甚至更复杂的数据结构。
QStandardItemModel提供了一组成员函数:添加数据项appendRow()/*数据集为列表形式*/,更改数据项或删除数据项。
void QStandardItemModel::appendRow(const QList<QStandardItem *> & items)
-------------------------------------
QSortFilterProxyModel代理:过滤数据,拥有“防火墙”的作用,限制输出到View中进行显示的数据。代理处理完数据,不是直接交给委托,而是交给View,View再负责传递给Item,Item再通过委托显示特定的样式。(代理->视图->委托->显示)
voidQSortFilterProxyModel::setSourceModel(QAbstractItemModel * sourceModel)
--------------------------------------
QStyledItemDelegate委托处理过程(Item默认使用textedit显示数据):
-------------------------------------
View通过ModelIndex从model中获取数据:
QVariant QAbstractProxyModel::data(const QModelIndex & proxyIndex, int role = Qt::DisplayRole) const
当Model中的data变化时,自动发dataChanged signal给所有的view以便它们更新。
View修改Model中对应ModelIndex的数据:
bool QAbstractProxyModel::setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)
View负责组织数据并显示,但真正的显示工作交给代理delegate,代理负责最终的显示数据和处理用户交互的工作。
------------------------------------
通过代理delegate编辑并显示数据项的过程:
当通过双击鼠标或回车等操作触发了View的edit trigger时,Delegate会创建一个合适的widget
(如line edit或combo box等)处理用户的输入,用户编辑完成以后发射commitData“编辑完成信号”,
delegate通过setModelData将更新Model中的数据。
1)void paint(QPainter *painter, const QStyleOptionViewItem &option,
//View通过delegate重绘Item、渲染内容
const QModelIndex &index) const;
------------------------------------
2)QWidget *createEditor(QWidget *parent,//创建修改数据编辑器
const QStyleOptionViewItem &option,
const QModelIndex &index) const;//QModelIndex选择需要进行渲染的列
void setEditorData(QWidget *editor, //为editor提供编辑的原始数据
const QModelIndex &index) const;
//当用户开始编辑时,视图首先调用 委托的createEditor( ) 创建一个编辑器,然后调用 setEditorData( )
并使用当前数据项 Item 中的数据初始化编辑器。
------------------------------------
//当用户点击回车,或者从 QTimeEidt 等控件上失去输入焦点后,editingFinished() 信号就发送出来,
并且 commitAndColseEditor() 槽函数被调用。
connect( ..., SIGNAL(editingFinished()), ..., SLOT(CommitAndCloseEditor()) );
3)void ItemDelegate::CommitAndCloseEditor()
{
//QObject::sender() Returns a pointer to the object that sent the signal
QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender()); //使用 sender() 来获得编辑器
emit commitData( editor );//发送commitData() 信号提醒视图有一个已编辑好的数据需要更新了
emit closeEditor( editor );//发送closeEditor( ) 信号通知视图编辑控件不需要了,模型删除该编辑器
}
--------------------------------------
//获取编辑器中的数据并存储到指定模块索引的Model中
4)void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
QT——模型/视图(model/view)