Qt学习之路——Model/View

本文将针对Qt中的Model/View结构进行介绍。

Model/View结构

Qt通过Model/View实现了数据与接口的分离,降低了耦合性,在该结构中:

  • View是前端,是用于展示、编辑数据的可视化组件
  • Model是后端,是视图与原始数据的接口

Model/View的一个典型应用是数据库应用,例如将数据库中一个页在view中进行展示与编辑。Model/View基本结构如下图所示:

图中还有一个代理环节,可以方便用户定制数据的显示方式和编辑方式。

Model/View在Qt中的实现

Model

基于项数据(item data)的数据模型(Model)都是基于QAbstractItemModel类,该类定义了两个主要部件:

  • 视图组件:用于显示
  • 数据处理接口:用于数据存取

根据数据结构的不同,Qt提供了若干模型类,其层次结构如下图。

其中的抽象类不能直接使用,如果里面的模型类无法实现,我们可以基于抽象类构建自己的Model

View

自定义Model

QAbstractTableModel使用2

这里以QAbstractTableModel为例,介绍如何自定义Model

源文件

  • main.cpp
点击显/隐内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <QtGui>  

#include "currencymodel.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

//数据源
QMap<QString, double> currencyMap;
currencyMap.insert("AUD", 1.3259);
currencyMap.insert("CHF", 1.2970);
currencyMap.insert("CZK", 24.510);
currencyMap.insert("DKK", 6.2168);
currencyMap.insert("EUR", 0.8333);
currencyMap.insert("GBP", 0.5661);
currencyMap.insert("HKD", 7.7562);
currencyMap.insert("JPY", 112.92);
currencyMap.insert("NOK", 6.5200);
currencyMap.insert("NZD", 1.4697);
currencyMap.insert("SEK", 7.8180);
currencyMap.insert("SGD", 1.6901);
currencyMap.insert("USD", 1.0000);

//自定义表模型
CurrencyModel currencyModel;
currencyModel.setCurrencyMap(currencyMap);
//表视图
QTableView tableView;
//设置视图模型
//********************重点是这句话,用标准的QTableView载入我们自己定义的模型
tableView.setModel(&currencyModel);
//设置交替颜色
tableView.setAlternatingRowColors(true);

tableView.setWindowTitle(QObject::tr("Currencies"));
tableView.show();

return app.exec();
}
  • currencymodel.h
点击显/隐内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef CURRENCYMODEL_H  
#define CURRENCYMODEL_H

#include <QAbstractTableModel>
#include <QMap>

class CurrencyModel : public QAbstractTableModel
{
public:
CurrencyModel(QObject *parent = 0);

void setCurrencyMap(const QMap<QString, double> &map);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const;

private:
QString currencyAt(int offset) const;

QMap<QString, double> currencyMap;
};

#endif
  • currencymodel.cpp
点击显/隐内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <QtCore>  

#include "currencymodel.h"

CurrencyModel::CurrencyModel(QObject *parent)
: QAbstractTableModel(parent)
{
}

void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map)
{
currencyMap = map;
//重置模型至原始状态,告诉所有视图,他们数据都无效,强制刷新数据
reset();
}

//返回行数
int CurrencyModel::rowCount(const QModelIndex & /* parent */) const
{
return currencyMap.count();
}
//返回列数
int CurrencyModel::columnCount(const QModelIndex & /* parent */) const
{
return currencyMap.count();
}

//返回一个项的任意角色的值,这个项被指定为QModelIndex
QVariant CurrencyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();

if (role == Qt::TextAlignmentRole) {
return int(Qt::AlignRight | Qt::AlignVCenter);
} else if (role == Qt::DisplayRole) {
QString rowCurrency = currencyAt(index.row());
QString columnCurrency = currencyAt(index.column());

if (currencyMap.value(rowCurrency) == 0.0)
return "####";

double amount = currencyMap.value(columnCurrency)
/ currencyMap.value(rowCurrency);

return QString("%1").arg(amount, 0, 'f', 4);
}
return QVariant();
}
//返回表头名称,(行号或列号,水平或垂直,角色)
QVariant CurrencyModel::headerData(int section,
Qt::Orientation /* orientation */,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
return currencyAt(section);
}
//获取当前关键字
QString CurrencyModel::currencyAt(int offset) const
{
return (currencyMap.begin() + offset).key();
} }

注意:在上面的代码中,headerData函数是必须实质性实现的,否则运行时会抛出std::bad_alloc导致异常。具体原因在view的setModel函数中,这里不深究了,总之要将这个函数实质性实现

自定义代理

参考文献

0%