Qt學習之路(46): 自定義model之二

原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://devbean.blog.51cto.com/448512/267972

前面的例子已經比較清楚的給出了自定義model的方法,就是要覆蓋我們所需要的那幾個函數就可以了。但是,前面的例子僅僅是簡單的展示數據,也就是說數據時只讀的。那麼,如何能做到讀寫數據呢?那就要來看進來的例子了。這個例子也是來自C++GUI Programming with Qt 4, 2nd Edition這本書的。

 

還是先來看代碼吧:

 

citymodel.h

 

InBlock.gifclass CityModel : public QAbstractTableModel 
InBlock.gif
InBlock.gif        Q_OBJECT 
InBlock.gif 
InBlock.gifpublic
InBlock.gif        CityModel(QObject *parent = 0); 
InBlock.gif 
InBlock.gif        void setCities(const QStringList &cityNames); 
InBlock.gif        int rowCount(const QModelIndex &parent) const
InBlock.gif        int columnCount(const QModelIndex &parent) const
InBlock.gif        QVariant data(const QModelIndex &index, int role) const
InBlock.gif        bool setData(const QModelIndex &index, const QVariant &value, int role); 
InBlock.gif        QVariant headerData(int section, Qt::Orientation orientation, int role) const
InBlock.gif        Qt::ItemFlags flags(const QModelIndex &index) const
InBlock.gif 
InBlock.gifprivate
InBlock.gif        int offsetOf(int row, int column) const
InBlock.gif 
InBlock.gif        QStringList cities; 
InBlock.gif        QVector<int> distances; 
InBlock.gif};

 

 

citymodel.cpp

InBlock.gifCityModel::CityModel(QObject *parent) 
InBlock.gif        : QAbstractTableModel(parent) 
InBlock.gif
InBlock.gif
InBlock.gif 
InBlock.gifint CityModel::rowCount(const QModelIndex & parent) const 
InBlock.gif
InBlock.gif        return cities.count(); 
InBlock.gif
InBlock.gif 
InBlock.gifint CityModel::columnCount(const QModelIndex & parent) const 
InBlock.gif
InBlock.gif        return cities.count(); 
InBlock.gif
InBlock.gif 
InBlock.gifQVariant CityModel::data(const QModelIndex &index, int role) const 
InBlock.gif
InBlock.gif        if (!index.isValid()) { 
InBlock.gif                return QVariant(); 
InBlock.gif        } 
InBlock.gif 
InBlock.gif        if (role == Qt::TextAlignmentRole) { 
InBlock.gif                return int(Qt::AlignRight | Qt::AlignVCenter); 
InBlock.gif        } else if (role == Qt::DisplayRole) { 
InBlock.gif                if (index.row() == index.column()) { 
InBlock.gif                        return 0; 
InBlock.gif                } 
InBlock.gif                int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                return distances[offset]; 
InBlock.gif        } 
InBlock.gif        return QVariant(); 
InBlock.gif
InBlock.gif 
InBlock.gifQVariant CityModel::headerData(int section, Qt::Orientation orientation, int role) const 
InBlock.gif
InBlock.gif        if (role == Qt::DisplayRole) { 
InBlock.gif                return cities[section]; 
InBlock.gif        } 
InBlock.gif        return QVariant(); 
InBlock.gif
InBlock.gif 
InBlock.gifbool CityModel::setData(const QModelIndex &index, const QVariant &value, int role) 
InBlock.gif
InBlock.gif        if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) { 
InBlock.gif                int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                distances[offset] = value.toInt(); 
InBlock.gif 
InBlock.gif                QModelIndex transposedIndex = createIndex(index.column(), index.row()); 
InBlock.gif                emit dataChanged(index, index); 
InBlock.gif                emit dataChanged(transposedIndex, transposedIndex); 
InBlock.gif                return true
InBlock.gif        } 
InBlock.gif        return false
InBlock.gif
InBlock.gif 
InBlock.gifQt::ItemFlags CityModel::flags(const QModelIndex &index) const 
InBlock.gif
InBlock.gif        Qt::ItemFlags flags = QAbstractItemModel::flags(index); 
InBlock.gif        if (index.row() != index.column()) { 
InBlock.gif                flags |= Qt::ItemIsEditable; 
InBlock.gif        } 
InBlock.gif        return flags; 
InBlock.gif
InBlock.gif 
InBlock.gifvoid CityModel::setCities(const QStringList &cityNames) 
InBlock.gif
InBlock.gif        cities = cityNames; 
InBlock.gif        distances.resize(cities.count() * (cities.count() - 1) / 2); 
InBlock.gif        distances.fill(0); 
InBlock.gif        reset(); 
InBlock.gif
InBlock.gif 
InBlock.gifint CityModel::offsetOf(int row, int column) const 
InBlock.gif
InBlock.gif        if (row < column) { 
InBlock.gif                qSwap(row, column); 
InBlock.gif        } 
InBlock.gif        return (row * (row - 1) / 2) + column; 
InBlock.gif

 

代碼很長,但實際上和前面我們的那個例子非常相似。這個model也是用於table的,因此還是繼承了QAbstractTableModel。CityModel內部有兩個數據源:一個QStringList類型的對象,一個QVector&lt;int>類型的對象。前者用於保存城市的名字,需要用戶顯示的給出;後者是model內部維護的一個存放int的向量。這個CityModel就是要在table中顯示兩個城市之間的距離。同前面的例子一樣,如果我們要把所有的數據都保存下來,顯然會造成數據的冗餘:城市A到城市B的距離同城市B到城市A的距離是一樣的!因此我們還是自定義一個model。同樣這個CityModel有個簡單的空構造函數,rowCount()和columnCount()函數也是返回list的長度。data()函數根據role的不同返回不同的值。由於在table中座標是由row和column給出的,因此需要有一個二維座標到一維座標的轉換,這就是offsetOf()函數的作用。我們把主要精力放在setData()函數上面。

 

InBlock.gifbool CityModel::setData(const QModelIndex &index, const QVariant &value, int role) 
InBlock.gif
InBlock.gif        if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) { 
InBlock.gif                int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                distances[offset] = value.toInt(); 
InBlock.gif 
InBlock.gif                QModelIndex transposedIndex = createIndex(index.column(), index.row()); 
InBlock.gif                emit dataChanged(index, index); 
InBlock.gif                emit dataChanged(transposedIndex, transposedIndex); 
InBlock.gif                return true
InBlock.gif        } 
InBlock.gif        return false
InBlock.gif}

 

這個函數在用戶編輯數據時會自動調用。也就是說,這時我們的數據已經不是隻讀的了。函數開始是一個長長的判斷:index要是合法的;index的row和column不相等,也就是說兩個城市是不同的;數據想的role是Qt::EditRole。如果滿足了這三個條件,纔會執行下面的操作。首先,由row和column座標定位到表中的數據項在vector中的位置。然後用戶新修改的數據被作爲參數value傳入,所以我們要把這個參數賦值給distances。createIndex()函數根據column和row值生成一個QModelIndex對象。請注意這裏的順序:row和column是顛倒的!這就把座標爲(row, column)的點關於主對角線對稱的那個點(column, row)的index找到了。還記得我們的需求嗎?當我們修改了一個數據時,對應的數據也要被修改,這就是這個功能的實現。我們需要emit dataChanged()信號,這個信號接收兩個參數:一個是被修改的數據的左上角的座標,一個是被修改的數據的右下角的座標。爲什麼會有兩個座標呢?因此我們修改的數據不一定只是一個。像這裏,我們只修改了一個數據,因此這兩個值是相同的。數據更新了,我們用這個信號通知view刷新,這樣就可以顯示新的數據了。最後,如果函數數據修改成功就返回true,否則返回false。

 

最後,我們在main()函數中顯示出來這個model:

 

InBlock.gifint main(int argc, char *argv[]) 
InBlock.gif
InBlock.gif        QApplication a(argc, argv); 
InBlock.gif        QStringList cities; 
InBlock.gif        cities << "Arvika" << "Boden" << "Eskilstuna" << "Falun"
InBlock.gif 
InBlock.gif        CityModel cityModel; 
InBlock.gif        cityModel.setCities(cities); 
InBlock.gif 
InBlock.gif        QTableView tableView; 
InBlock.gif        tableView.setModel(&cityModel); 
InBlock.gif        tableView.setAlternatingRowColors(true); 
InBlock.gif        tableView.setWindowTitle(QObject::tr("Cities")); 
InBlock.gif        tableView.show(); 
InBlock.gif        return a.exec(); 
InBlock.gif}

 

這樣,我們就把這個model做完了。最後來看看效果吧!

 

2010-1-19

本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/267972


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章