前面的例子已經(jīng)比較清楚的給出了自定義 model 的方法,就是要覆蓋我們所需要的那幾個函數(shù)就可以了。但是,前面的例子僅僅是簡單的展示數(shù)據(jù),也就是說數(shù)據(jù)時只讀的。那么,如何能做到讀寫數(shù)據(jù)呢?那就要來看進(jìn)來的例子了。這個例子也是來自 C++GUI Programming with Qt 4, 2nd Edition這本書的。
還是先來看代碼吧:
citymodel.h
class CityModel : public QAbstractTableModel
{
Q_OBJECT
public:
CityModel(QObject *parent = 0);
void setCities(const QStringList &cityNames);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
private:
int offsetOf(int row, int column) const;
QStringList cities;
QVector<int> distances;
};
citymodel.cpp
CityModel::CityModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
int CityModel::rowCount(const QModelIndex & parent) const
{
return cities.count();
}
int CityModel::columnCount(const QModelIndex & parent) const
{
return cities.count();
}
QVariant CityModel::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) {
if (index.row() == index.column()) {
return 0;
}
int offset = offsetOf(index.row(), index.column());
return distances[offset];
}
return QVariant();
}
QVariant CityModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole) {
return cities[section];
}
return QVariant();
}
bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) {
int offset = offsetOf(index.row(), index.column());
distances[offset] = value.toInt();
QModelIndex transposedIndex = createIndex(index.column(), index.row());
emit dataChanged(index, index);
emit dataChanged(transposedIndex, transposedIndex);
return true;
}
return false;
}
Qt::ItemFlags CityModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.row() != index.column()) {
flags |= Qt::ItemIsEditable;
}
return flags;
}
void CityModel::setCities(const QStringList &cityNames)
{
cities = cityNames;
distances.resize(cities.count() * (cities.count() - 1) / 2);
distances.fill(0);
reset();
}
int CityModel::offsetOf(int row, int column) const
{
if (row < column) {
qSwap(row, column);
}
return (row * (row - 1) / 2) + column;
}
代碼很長,但實(shí)際上和前面我們的那個例子非常相似。這個 model 也是用于 table 的,因此還是繼承了QAbstractTableModel。CityModel 內(nèi)部有兩個數(shù)據(jù)源:一個 QStringList 類型的對象,一個QVector<int>類型的對象。前者用于保存城市的名字,需要用戶顯示的給出;后者是 model 內(nèi)部維護(hù)的一個存放 int 的向量。這個 CityModel 就是要在 table 中顯示兩個城市之間的距離。同前面的例子一樣,如果我們要把所有的數(shù)據(jù)都保存下來,顯然會造成數(shù)據(jù)的冗余:城市 A 到城市 B 的距離同城市 B 到城市 A 的距離是一樣的!因此我們還是自定義一個 model。同樣這個 CityModel 有個簡單的空構(gòu)造函數(shù),rowCount()和 columnCount()函數(shù)也是返回 list 的長度。data()函數(shù)根據(jù) role 的不同返回不同的值。由于在 table 中坐標(biāo)是由 row 和 column 給出的,因此需要有一個二維坐標(biāo)到一維坐標(biāo)的轉(zhuǎn)換,這就是 offsetOf()函數(shù)的作用。我們把主要精力放在 setData()函數(shù)上面。
bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) {
int offset = offsetOf(index.row(), index.column());
distances[offset] = value.toInt();
QModelIndex transposedIndex = createIndex(index.column(), index.row());
emit dataChanged(index, index);
emit dataChanged(transposedIndex, transposedIndex);
return true;
}
return false;
}
這個函數(shù)在用戶編輯數(shù)據(jù)時會自動調(diào)用。也就是說,這時我們的數(shù)據(jù)已經(jīng)不是只讀的了。函數(shù)開始是一個長長的判斷:index 要是合法的;index 的 row 和 column 不相等,也就是說兩個城市是不同的;數(shù)據(jù)想的 role是 Qt::EditRole。如果滿足了這三個條件,才會執(zhí)行下面的操作。首先,由 row 和 column 坐標(biāo)定位到表中的數(shù)據(jù)項(xiàng)在 vector 中的位置。然后用戶新修改的數(shù)據(jù)被作為參數(shù) value 傳入,所以我們要把這個參數(shù)賦值給 distances。createIndex()函數(shù)根據(jù) column 和 row 值生成一個 QModelIndex 對象。請注意這里的順序:row 和 column 是顛倒的!這就把坐標(biāo)為(row, column)的點(diǎn)關(guān)于主對角線對稱的那個點(diǎn)(column, row)的 index 找到了。還記得我們的需求嗎?當(dāng)我們修改了一個數(shù)據(jù)時,對應(yīng)的數(shù)據(jù)也要被修改,這就是這個功能的實(shí)現(xiàn)。我們需要 emit dataChanged()信號,這個信號接收兩個參數(shù):一個是被修改的數(shù)據(jù)的左上角的坐標(biāo),一個是被修改的數(shù)據(jù)的右下角的坐標(biāo)。為什么會有兩個坐標(biāo)呢?因此我們修改的數(shù)據(jù)不一定只是一個。像這里,我們只修改了一個數(shù)據(jù),因此這兩個值是相同的。數(shù)據(jù)更新了,我們用這個信號通知 view 刷新,這樣就可以顯示新的數(shù)據(jù)了。最后,如果函數(shù)數(shù)據(jù)修改成功就返回 true,否則返回 false。
最后,我們在 main()函數(shù)中顯示出來這個 model:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList cities;
cities << "Arvika" << "Boden" << "Eskilstuna" << "Falun";
CityModel cityModel;
cityModel.setCities(cities);
QTableView tableView;
tableView.setModel(&cityModel);
tableView.setAlternatingRowColors(true);
tableView.setWindowTitle(QObject::tr("Cities"));
tableView.show();
return a.exec();
}
這樣,我們就把這個 model 做完了。最后來看看效果吧!
本文出自 “豆子空間” 博客,請務(wù)必保留此出處 http://devbean.blog.51cto.com/448512/193918
更多建議: