1. 程式人生 > >Model/View框架總體架構

Model/View框架總體架構

原文:http://book.2cto.com/201209/4214.html

Model/View框架中,所有模型類具有共同的抽象基類QAbstractItemModel,所有檢視類具有共同的抽象基類QAbstractItemView,所有委託類具有共同的抽象基類QabstractItem Delegate。這些類之間的協作關係如圖13-4所示。


 

Model/View框架涉及的類如圖13-5所示。初看起來這個框架非常複雜,但是由於所有的模型類、檢視類、委託類都遵循上述協作關係,我們其實只需關注QAbstractItemModel的各個子類的區別、QAbstractItemView的各個子類的區別以及QAbstractItemDelegate的各個子類的區別。


 

(1)模型類。作為所有模型類的抽象基類,QAbstractItemModel定義了模型訪問介面。這個介面是一組純虛擬函式,負責訪問資料集中的資料項。該類將資料集看作一棵樹,但是該類並沒有定義任何資料成員來存放資料集。類似地,除了具有灰色背景的類Qstandard ItemModel外,其他模型類也都如此。由於QAbstractItemModel是一個抽象基類,我們並不能直接定義該類的物件。使用模型類的一個簡單做法是瞭解QAbstractItemModel各個派生類的功能,尋找一個與應用程式資料集特徵最接近的派生類,定義該類的物件或者派生該類的子類。如果QAbstractItemModel的所有派生類都無法滿足我們的要求(比如效能方面的),我們才需要直接派生QAbstractItemModel的子類,但這需要實現模型訪問介面中的幾個純虛擬函式,

程式設計工作量較大。

QAbstractListModel負責處理具有列表結構的資料集。它已經實現了部分模型訪問介面,我們只需派生該類的子類,實現模型訪問介面中的其他虛擬函式,因而比直接派生QAbstractItemModel的子類要簡單。如果應用程式只是企圖顯示、編輯一些字串,可以使用QAbstractListModel的子類QStringListModel。程式設計師所需做的只是將這些字串寫入一個QStringList物件中,然後將這個物件傳遞給一個QStringListModel物件。後者實現了整個模型訪問介面,因而程式設計師不需要過載它的任何成員函式。

QAbstractTableModel負責處理具有表格結構的資料集。它已經實現了部分模型訪問介面,我們只需派生該類的子類,實現模型訪問介面中的其他虛擬函式。它的派生類QsqlQueryModel,QsqlTableModel,QSqlRelationalTableModel和關係型

資料庫相關,限於篇幅,本書不予討論。

QStandardItemModel負責處理具有樹狀層次結構的資料集。當然,由於列表、表格也可以被看作特殊的樹,該類實際上也可以處理具有列表、表格結構的資料集。和前面幾個模型類不同,該類在其內部定義了一個數據結構,用來存放樹狀結構的資料集。它使用類QStandardItem表示一個樹節點,QStandardItem定義了一些資料成員,用來存放資料集的資料。另外,它還定義了一些指標,每個指標指向另外一個QStandardItem物件,形成當前節點的子節點。通過這種方式,QStandardItemModel可以儲存具有任意多層的樹狀資料集。

QFileSystemModel專門負責處理本地檔案系統。它實現了整個模型訪問介面,其他檢視類通過該類可以立即顯示本地檔案系統。

和前面的模型類都不同,QAbstractProxyModel並不直接處理資料集,它對其他模型中的資料項進行一些處理,再將處理結果呈現給框架中的其他物件。該類的子類QsortFilterProxy Model能夠做排序、篩選處理。

(2)檢視類。作為所有檢視類的抽象基類,QAbstractItemView負責繪製總體外觀並處理使用者的互動命令。由於它是一個抽象類,我們不能直接定義該類的物件,而是應該在該類的派生類中,選擇一個和資料集拓撲結構相似的類,定義其物件來顯示資料集。QListView適合顯示具有列表結構的資料集,QTableView適合顯示錶格結構的資料集,QTreeView以及QColumnView適合顯示樹狀結構的資料集。QHeaderView僅被用來顯示標頭部分,與資料項無關。

以上幾個檢視類實現普通檢視的功能,在它們的內部沒有定義任何模型物件。使用這些檢視類時,程式設計師必須自行構造模型物件。為了方便對Model/View框架的使用,Qt還提供了一組便利檢視類。這些類在其內部定義了模型物件,並定義了一組簡潔、易用的成員函式來操作模型物件中的資料項。其中,QListWidget儲存並顯示一個列表,QTableWidget儲存並顯示一個表格,QTreeWidget儲存並顯示一棵樹。在圖中這些類的背景被顯示為灰色,表示它們包含模型物件。這些檢視類分別使用類QlistWidgetItem,QTableWidgetItem以及QTreeWidgetItem表示模型物件中的資料項。

對於QListWidget表示的列表或者QTableWidget表示的表格,依據其行數、列數即可遍歷整個資料集。對於QTreeWidget表示的樹狀資料集,遍歷所有樹節點並非易事。為此,Model/View框架提供了迭代器QTreeWidgetItemIterator,對QTreeWidget表示的樹進行前序遍歷。

3)委託類。抽象基類QAbstractItemDelegate及其子類負責檢視中每個資料項的繪製與編輯。檢視類的內部定義了一個指標,指向一個委託物件。當檢視需要繪製每個資料項時,會將該資料項的螢幕位置等資訊傳遞給委託物件進行繪製。

QAbstractItemDelegate的子類QItemDelegate總是以一種預設的風格繪製資料項,而它的另外一個子類QStyledItemDelegate使用應用程式當前的介面風格進行繪製,使得整個介面具有協調一致的風格,因而我們應該儘可能地使用後者。當用戶需要編輯某個資料項時,委託物件會將目標資料項的型別傳遞給類QItemEditorFactory,後者負責建立一個能夠編輯該型別的編輯器控制元件,對目標資料項進行編輯。編輯完畢後,委託物件呼叫模型訪問介面,將編輯結果寫回模型。

(4)索引。在檢視類或者委託類看來,模型類中的資料集總是一棵樹,該樹可能具有多層樹節點。由於模型的訪問者對資料集的存放方式一無所知,為了表示要訪問哪個節點,訪問者會通知模型類目標節點的父節點,以及在這個父節點所包含的所有子節點中,目標節點所在的行號、列號。模型類收到這些資訊後,在資料集中找到目標節點,建立一個QModelIndex物件,邏輯上指向這個節點,這個QModelIndex物件就被稱為目標節點的索引。模型的訪問者此後就可以使用這個索引來訪問目標節點,提高了訪問效率。另外,QModelIndex內部還定義了一個指標,指向它所屬的模型物件。這樣,給定一個索引,不再需要任何其他資訊,就可以唯一確定一個模型物件中的一個數據項。

當資料集發生變化時,比如部分資料項被刪除或者有新的資料項加入,QModelIndex表示的索引可能失效,也就是不再指向原先所指的資料項。而QPersistentModelIndex所表示的索引卻總是指向某一個數據項,不會隨著資料集的變化而失效,除非它所指的資料項被刪除。

(5)選擇。檢視類允許使用者選擇一些資料項。在最複雜情況下,使用者可以選擇多個父節點所包含的多個子節點。以從上到下、從左到右的閱讀順序,每個父節點下被選的多個子節點可能是不連續的。我們將連續的被選子節點形成的集合稱為“選擇塊”。一個QItemSelectionRange物件記錄一個選擇塊的範圍資訊,它使用資料成員tl(top-left)表示選擇塊左上角資料項的索引,使用資料成員br(bottom-right)表示選擇塊右下腳資料項的索引。一個QItemSelection物件包含多個QItemSelectionRange物件,表示多個選擇塊的資訊。

檢視類使用QItemSelectionModel記錄選擇資訊,該類的資料成員currentSelection所指的QItemSelection物件表示使用者最近一次所做的選擇,而資料成員ranges所指的QItemSelection物件表示最近一次之前所選的選擇塊。