Qt模型類

在模型/視圖架構中,模型提供一個標準的接口供視圖和委託(view/delegate)用來訪問數據。在Qt中,這個標準的接口是由類QAbstractItemModel定義的。無論底層數據結構中的數據是如何存儲的,類QAbstractItemModel的所有子類都將數據表示爲一個包含由數據項組成的表格的層次型結構。視圖按照這個約定來訪問模型中的數據項,但是它們將信息展示給用戶的時候不需要這樣來做。

模型也使用信號和信號槽機制來向關聯的視圖通知關於數據的變化。

模型索引

爲了確保數據的表示與數據被訪問的方式是分開獨立的,引入了模型索引(modelindex)的概念。每一個可以通過模型來獲取的信息都被一個模型索引來表示。視圖和委託使用這些模型索引來獲取要顯示的數據項(視圖/委託操作的不是數據項,實際上操作的是保存了數據項內容的模型索引)。

結果是,只有模型需要知道如何獲取數據,並且被該模型管理的數據的種類可以進行很廣泛的定義。模型索引包含一個指向創建它們(模型索引)的模型的指針,這一點避免了在使用不止一個模型來工作時所產生的混亂。

1 QAbstractItemModel *model = index.model();

模型索引提供對信息片段的臨時引用,可以被用來通過模型獲取或者修改數據。由於模型可能會重新組織它們內部的數據結構,因此模型索引可能會失效,所 以不應當被保存。 如果你需要對一個信息片段的長期的引用,那麼你必須創建一個持久模型索引。這樣就提供了一個指向模型所維護的最新數據的引用。臨時模型索引由 QModelIndex類提供,持久模型索引由QPersistentModelIndex類提供。

要獲得對應於某一個數據項的模型索引,需要告訴模型3個屬性:1個行號,1個列號,和父數據項的模型索引。接下來的小節描述及詳細解釋這些屬性。

行與列

在最基本的形式下,一個模型可以被當作一個簡單的表格來訪問,在這個表格中數據項是通過行號和列號來定位的。這並不是說底層的數據片段存儲在一個 矩陣結構中;行號和列號的使用只是一個用來讓部件之間進行通信的約定。我們可以通過向模型提供任意指定的項目的行號和列號來獲取它的信息,我們會獲得一個 表示該數據項的模型索引:

1 QModelIndex index = model->index(row, column, …);

那些爲簡單、單級的數據結構,例如列表和表格提供接口的模型不需要任何其它的信息,但是,正如上面的代碼所顯示的,我們在獲取一個模型索引時需要提供更多的信息。

這個示意圖展示了一個基本的表格模型,其中每個數據項都是通過一對行號和列號來定位的。我們通過向模型傳遞一個數據項的對應的行號和列號來獲取一個指向該項目的索引。

1 QModelIndex indexA = model->index(0, 0, QModelIndex());
2  
3 QModelIndex indexB = model->index(1, 1, QModelIndex());
4  
5 QModelIndex indexC = model->index(2, 1, QModelIndex());

模型中的頂層數據項都是通過以QModelIndex()作爲它們的父對象項目來引用的。這一點是在下一節描述的。


數據項的父對象

模型針對數據項提供的類似表格的接口完美適用於在表格或者列表視圖中使用數據;行號和列號系統準確地與視圖顯示項目的方式對應。然而,像樹視圖這樣的結構要求模型針對它管理的項目展現出一個更靈活的接口。結果是,每個數據項也可以作爲另一個數據項組成的表格的父對象,就像樹視圖中的一個頂層數據項可以包含另一個由數據項組成的列表一樣。

當我請求一個模型數據項的模型索引時,我也必須提供一些關於它的父對象的信息。在模型之外,指向一個項目的唯一的方法就是使用一個模型索引,所以一個父對象模型索引也必須被給出:

1 QModelIndex index = model->index(row, column, parent);

父對象,行和列

這個圖顯示的是一個樹模型的表示方式,其中,每個數據項是使用一個父對象、一個行號和一個列號來索引的。

項目“A”和“C”在模型中是被以頂級的兄弟節點的方式來表示的:

1 QModelIndex indexA = model->index(0, 0, QModelIndex());
2  
3 QModelIndex indexC = model->index(2, 1, QModelIndex());


項目“A”有一些子對象。項目“B”的模型索引是以下面的代碼來獲取的:

1 QModelIndex indexB = model->index(1, 0, indexA);


數據項角色

一個模型中的數據項可以爲其它部件扮演各種各樣的角色,以便爲不同的情況來使用不同種類的數據。例如,Qt::DisplayRole用來在一個視圖中獲取一個可以被當成文本顯示的字符串。一般地,數據項都包含了用於很多不同角色的數據,而標準的角色是被Qt::ItemDataRole定義的。

我們可以通過向模型傳遞對應於一個數據項的模型索引來獲取它的數據,並且通過指定一個角色來獲取我們想要的類型的數據:

QVariant value = model->data(index, role);

數據項角色

角色指示模型哪種類型的數據是我們想要的。視圖可以以不同的方式來顯示不同的角色,所以爲每個角色提供合適的信息是很重要的。

創建新的模型一節更加具體地講述了關於角色的一些特殊用法。

數據項數據的最常用的用法是被Qt::ItemDataRole中定義的標準角色描述的。通過爲每個角色提供適當的數據項數據,模型可以爲視圖和委託提供關於數據項該如何顯示給用戶的提示。不同的視圖可以自主選擇是按照要求來忽略這個提示還是遵從這個提示。也可以定義附加的角色,以用於實現應用程序特定的目的。

概念的總結

  • •.模型索引以一種不依賴於任何底層數據結構的方式來向視圖和委託提供關於模型所提供的數據項的位置的信息。
  • •.數據項是通過行號、列號和它們的父對象的模型索引來引用的。
  • •.模型索引是模型在其它組件,例如視圖和委託的請求下構造的。
  • •.如果在使用index()來請求模型索引時,一個有效的模型索引被當作父項目的索引來指定,那麼返回的索引將會指向模型中在該父數據項下方的一個子數據項。獲取的模型索引指向那個父數據項的一個子數據項。
  • •.如果在使用index()來請求索引時,一個無效的模型索引被當作父數據項的模型索引來指定,那麼返回的索引將會指向模型中的一個頂層數據項。
  • •.角色區分與一個數據項結合的不同類型的數據。

使用模型索引

爲了演示如何使用模型索引來從模型中取得數據,我們設置一個沒有視圖的QFileSystemModel,並且在一個部件中顯示文件和目錄的名字。儘管這沒有展示出一個使用模型的標準方式,但是它展示了使用模型索引時在模型中使用的一些約定。

我們以下面的方式來構造一個文件系統模型:

1 QFileSystemModel *model = new QFileSystemModel;
2 QModelIndex parentIndex = model->index(QDir::currentPath());
3 int numRows = model->rowCount(parentIndex);


在這種情況下,我們建立一個默認的QFileSystemModel,使用該模型提供的特定的index()實現來獲取一個父對象索引,再使用函數rowCount()來統計該模型中的行數。

簡便起見,我們只關心該模型中第一列的項目。我們按順序檢查每一行,獲取每一行中第一個項目的模型索引,再讀取模型中爲該項目儲存的數據。

1 for (int row = 0; row < numRows; ++row) {
2 QModelIndex index = model->index(row, 0, parentIndex);}

爲了獲得一個模型索引,我們指定行號、列號(爲第一列指定爲0),以及針對所有我們想要獲取的項目的父項目的合適的模型索引。保存在每個項目中的文本是使用模型的data()函數來獲取的。我們指定模型索引和DisplayRole以便以字符串的形式來獲取該項目的數據。

1 QString text = model->data(index, Qt::DisplayRole).toString();
2 // Display the text in a widget.

上面的例子展示了從模型中獲取數據時所用的基本原則:

  • •.一個模型的維度可以使用rowCount()和columnCount()來獲取。這些函數通常要求指定一個父模型索引。
  • •.模型索引用來訪問模型中的項目。要指定一個項目,需要行號、列號和父模型索引。
  • •.要訪問模型中的頂級項目,需要使用QModelIndex()來指定一個空的模型索引作爲父索引。
  • •.項目都包含針對不同角色的數據。爲了獲取一個指定角色的數據,必須同時向模型提供模型索引和角色。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章