Model/View 结构将数据模型和用户界面分离开来,分别用不同的实现,是一种显示和编辑数据的有效结构,在处理大型数据时尤其明显。
可以将一个数据模型在不同的视图中显示,也可以在不修改数据模型的情况下,设计特殊的视图组件
MVC由三种对象组成:
把一个 model 注册给多个 view,为同一个数据提供不同的显示方式。Qt 会自动地对这些 view 保持同步,自动刷新所有的 view 以显示最新的数据。这样,我们就可以只对 model 进行修改,view 会自动更新。
但是对于很大的数据,我们则需要使用 Qt 的 view 类,比如 QListView,QTabelView 和 QTreeView,同时需要提供一个 model,可以是自定义 model,也可以是 Qt 预置的model。例如,如果数据来自数据库,那么你可以使用 QTabelView 和 QSqlTableModel 这两个类。(在少量数据的情形下,我们不需要动用 model 这样重量级的组件。Qt 为了方便起见也提供了 item view 类,分别是 QListWidget,QTableWidget 和 QTreeWidget,使用这些类可以直接对 item 进行操作。这种实现很像 Qt 早期版本,组件中包含了相应的 item,例如 QTableWidget 中包含有QTableWidgetItem 等。)
源数据由模型 (Model) 读取,然后在视图 (View) 组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。
通过数据模型存取的每个数据都有一个模型索引,视图组件和代理都通过索引来获取数据。
模型内部组织数据的结构可能随时改变,所以模型索引是临时的。例如,对于一个QTreeView组件,如果获取一个节点的模型索引后又修改了模型数据,则先前获得的模型索引或不再指向原节点。
通过行号、列号、父项的模型索引三个参数来获得需要的模型索引。
数据模型的基本形式是用行和列定义的表格数据,但这并不意味着底层的数据是用二维数组存储的。
为数据模型的一个项设置数据时,可以为项设置不同角色的数据。
一个项可以有不同角色的数据,对应不同的用途。
实际上是Qt的一个enum定义的,比较常见的有Qt::DisplayRole和Qt::EditRole,另外还有Qt::ToolTipRole, Qt::StatusTipRole, 和Qt::WhatsThisRole等。并且,还有一些属性是用来描述基本的展现属性的,比如Qt::FontRole, Qt::TextAlignmentRole, Qt::TextColorRole, Qt::BackgroundColorRole等。
// 创建一个QStringListModel的对象。然后创建一个QStringList对象,并且把这个对象设置为model的数据
QStringListModel *model = new QStringListModel(this);
QStringList data;
data << "a" << "b" << "c";
// QListView数据展示:创建一个QListView的对象,并把model设置为它的model
QListView *view = new QListView(this);
view->setModel(model);
数据插入:
int irow = view->currentIndex().row(); // 获取QListView当前行
model->insertRows(row, 1);
QModelIndex index = model->index(row);
model->setData(index, text);
view->setCurrentIndex(index);
view->edit(index); // 这一行可以被编辑
几乎所有操作都是针对model的,model侦测到数据发生了变化,会立刻通知view刷新。这样,我们就可以把精力集中到对数据的操作上,而不用担心view的同步等操作。
QStringListModel *model = new QStringListModel(QColor::colorNames(), this);
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); // 代理(QListView必须用QSortFilterProxyModel代理,否则不生效)
proxy->setSourceModel(model); // 对model代理
proxy->setFilterKeyColumn(0); // 过滤第0列
QListView *view = new QListView(this);
view->setModel(proxy);
QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(....);
modelProxy->setFilterRegExp(regExp); // 设置proxy过滤器的表达式
CurrencyModel::CurrencyModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
QMap<QString, double> map;
int CurrencyModel::rowCount(const QModelIndex & parent) const
{
return map.count();
}
int CurrencyModel::columnCount(const QModelIndex & parent) const
{
return map.count();
}
// 虚函数 在用户编辑数据时会自动调用: 用户新修改的数据被作为参数value传入
QVariant CurrencyModel::data(const QModelIndex &index, const QVariant &value, int role) const
{
...
}
QVariant CurrencyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
...
}
void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map)
{
...
}
// 调用
CurrencyModel *model = new CurrencyModel();
model->setCurrencyMap(data);
QTableView *view = new QTableView(this);
view->setModel(model);
代理在视图与模型之间交互操作时提供临时编辑组件的功能。(模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理功能会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又提交给模型:在QTableView组件上双击一个单元格编辑数据时,在单元格里就会出现一个QLineEdit组件,这个编辑框就是代理提供的临时编辑器。)
对于一些特殊的数据编辑需求,例如只允许输入整型数,使用一个QSpinBox作为代理组件更合适;从列表中选择一个数据,使用一个QComboBox作为代理组件更好。这时就需要从QStyledItemDelegate、QItemDelegate继承创建自定义代理类。
如果delegate没有支持为你的数据类型进行绘制,或者你希望自己绘制item,那么就可以继承 QStyledItemDelegate 类,并且重写 paint() 、sizeHint() 函数。paint() 函数会被每一个item独立调用,而sizeHint()函数则可以定义每一个item 的大小。
使用:
tableWidget->setItemDelegate(new TrackDelegate());