QT官網(wǎng):Model/View Programming
Qt包含了一系列item view類破花,它們使用model/view架構(gòu)來管理數(shù)據(jù)及其顯示方式的關(guān)系疲吸。模型(model)提供標準接口來存取數(shù)據(jù)摘悴,視圖(view)定義數(shù)據(jù)的顯示方式烦租。即數(shù)據(jù)的存儲和數(shù)據(jù)的顯示是分開的。
model/view架構(gòu)
Model-View-Contoller(MVC,模型-視圖-控制器)是一種設(shè)計模式掐松,最初源于Smalltalk,經(jīng)常用來構(gòu)建GUI抡句。在設(shè)計模式中待榔,對MVC的描述如下:
MVC包含3種對象流济。Model是應(yīng)用對象绳瘟,View是屏幕的顯示,Controller定義UI對用戶輸入的響應(yīng)方式斤彼。在MVC之前畅卓,UI管理所有的對象翁潘,而MVC把他們進行了解耦歼争,提供了更大的靈活性沐绒。(數(shù)據(jù)-顯示-控制 進行了分離)
如果view和controller合二為一,就變成了Model/View的架構(gòu)扮超。這仍然把數(shù)據(jù)存儲的方式和數(shù)據(jù)顯示的方式進行了分離出刷,但提供了基于相同原則的更簡單的框架馁龟。這種分離使得在不同的view里顯示相同的數(shù)據(jù)變成可能漆魔,以及當(dāng)實現(xiàn)新的view時不用改變底層的數(shù)據(jù)結(jié)構(gòu)。為了靈活的處理用戶的輸入系瓢,Qt引入了代理(delegate)的概念句灌。
在Qt框架(特別是其模型/視圖編程范式中)涯塔,模型(models)負責(zé)存儲和管理數(shù)據(jù)匕荸,而視圖(views)負責(zé)數(shù)據(jù)的可視化顯示榛搔。代理(delegates)則用于定制視圖中的項目渲染和編輯行為东揣。模型索引是連接這三者的關(guān)鍵嘶卧,它允許視圖和代理以一致的方式引用模型中的數(shù)據(jù),而無需關(guān)心這些數(shù)據(jù)是如何在模型中存儲的侦铜。這種方式使得模型/視圖架構(gòu)非常靈活钉稍,可以處理各種不同類型和結(jié)構(gòu)的數(shù)據(jù)棺耍。
model和數(shù)據(jù)源進行通信俊卤,為其他組件提供接口害幅。通信的本質(zhì)取決于數(shù)據(jù)源的類型以及模型的實現(xiàn)方式矫限。
view從model中獲取model index,后者提供了對數(shù)據(jù)項的引用取董。通過向model提供model index棍苹,view可以檢索數(shù)據(jù)源中的數(shù)據(jù)項。
在標準視圖中茵汰,代理負責(zé)渲染數(shù)據(jù)項枢里。當(dāng)數(shù)據(jù)項被編輯時,代理會直接使用模型索引(model index)與模型(model)進行通信蹂午。
通常栏豺,如上所述,模型/視圖類可以分為三組:模型(models)豆胸、視圖(views)和代理(delegates)。這些組件中的每一個都由抽象類定義晚胡,這些抽象類提供了公共接口灵奖,并在某些情況下提供了特性的默認實現(xiàn)。抽象類是為了被繼承以提供其他組件所期望的完整功能集估盘;這也允許編寫專門的組件瓷患。
模型、視圖和代理相互之間通過信號和槽進行通信:
模型的信號通知視圖關(guān)于數(shù)據(jù)源中數(shù)據(jù)的更改遣妥。
視圖的信號提供關(guān)于用戶與正在顯示的項目交互的信息擅编。
代理在編輯過程中使用信號來告訴模型和視圖關(guān)于編輯器狀態(tài)的信息。
基類QAbstractItem*:模型-QAbstractItemModel箫踩,視圖-QAbstractItemView沙咏,代理-QAbstractItemDelegate.
模型Models
所有的模型類都基于QAbstractItemModel類。該類定義了可以被視圖和代理使用的班套、訪問數(shù)據(jù)的接口肢藐。數(shù)據(jù)本身不必存儲在模型中,數(shù)據(jù)可以存儲在文件吱韭、數(shù)據(jù)庫或其他組件中吆豹。
QAbstractItemModel提供了訪問數(shù)據(jù)的接口,它為視圖(如表格理盆、列表和樹)展示數(shù)據(jù)提供了足夠的靈活性痘煤。然而,當(dāng)為類似列表和表格的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)新模型時猿规,QAbstractListModel?和?QAbstractTableModel?類是更好的起點衷快,因為它們?yōu)槌R姾瘮?shù)提供了適當(dāng)?shù)哪J實現(xiàn)。這些類中的每一個都可以被子類化姨俩,以提供支持特定類型列表和表格的模型蘸拔。
Qt提供了一些現(xiàn)成的模型师郑,可以用來處理數(shù)據(jù)項:
QStringListModel:用來存儲簡單的QStringList對象。
QStandardItemModel:管理更復(fù)雜的樹形結(jié)構(gòu)的項调窍,每個可以包含任意數(shù)據(jù)宝冕。
QFileSystemModel:提供本地文件系統(tǒng)中關(guān)于文件和路徑的信息。
QSqlQueryModel邓萨,QSqlTableModel地梨,QSqlRelationalTableModel用于訪問數(shù)據(jù)庫。
如果這些標準的模型不能滿足你的需求缔恳,你可以繼承QAbstractItemModel宝剖,QAbstractListModel或QAbstractTableModel來創(chuàng)建自己的模型。
視圖Views
完全實現(xiàn)的類包括:QListView顯示一列項, QTableView用表格顯示數(shù)據(jù)歉甚,QTreeView用層次結(jié)構(gòu)顯示數(shù)據(jù)万细。上述3類都繼承自QAbstractItemView。這些類可以直接使用铃芦,也可以從這些類中派生新的類雅镊。
代理Delegates
QAbstractItemDelegate是所有代理的基類襟雷。默認的實現(xiàn)的類是QStyledItemDelegete刃滓,這是Qt的標準視圖默認使用的類。二者的區(qū)別在于耸弄,QStyledItemDelegate 使用當(dāng)前樣式表進行繪制咧虎。在實現(xiàn)自定義委托時,推薦使用QStyledItemDelegate 作為基類计呈,或者結(jié)合 Qt style sheets砰诵。
排序Sorting
在模型/視圖的架構(gòu)下有2種排序,選擇哪種取決于底層的模型捌显。
如果你的模型是可排序的茁彭,例如它實現(xiàn)了QAbstractItemModel::sort()函數(shù),QTableView和QTreeView提供了API允許你通過編程的方式對模型進行排序扶歪。此外理肺,我們可以使能交互式排序,即運行用戶通過點擊視圖的表頭進行排序善镰,只要把QHeaderView::sortindicatorChanged()信號連接到QTableView::sortByColumn()槽妹萨,和QTreeView::sortByCOlumn()槽。
便捷類
便捷類包括QListWidget炫欺,QTreeWidget和QTableWidget乎完。
這些類沒有Views類靈活,不能和model使用品洛。推薦使用view類來處理數(shù)據(jù)树姨。
如果想利用model/view摩桶,又想使用item-based接口,可以考慮使用view類娃弓,如QListView典格,QTreeView和QTableView,但和QStandartItemModel一起使用台丛。
使用模型和視圖
下面的內(nèi)容解釋如何在qt中使用模型/視圖的模式耍缴。
Qt包含的2中模型
qt提供了2中基本的模型:QStandardItemModel和QFileSystemModel。QStandardItemModel是個多功能的模型挽霉,可用來表示list防嗡,table和tree需要的各種類型的數(shù)據(jù)結(jié)構(gòu)。該模型中保持數(shù)據(jù)項侠坎。
QFileSystemModel用來維護一個目錄中內(nèi)容的信息蚁趁,它不持有任何數(shù)據(jù)項,而是僅僅簡單的展示文件系統(tǒng)中文件夾內(nèi)容实胸。
模型類
基本概念
在模型/視圖架構(gòu)中他嫡,模型為視圖和代理提供了訪問數(shù)據(jù)的標準接口。在Qt中庐完,標準接口定義在QAbstractItemModel類中钢属。不管底層數(shù)據(jù)項的結(jié)構(gòu)如何, QAbstractItemModel的所有子類用層次結(jié)構(gòu)的方式來展現(xiàn)數(shù)據(jù),包括表格门躯。視圖使用它來訪問模型中的數(shù)據(jù)淆党,但和顯示給用戶的信息并不需要嚴格一致。
模型通過信號和槽的機制通知所有附屬的視圖讶凉,數(shù)據(jù)改變了染乌。
模型索引 model index
為了保證數(shù)據(jù)的顯示和數(shù)據(jù)的訪問分離,引入了模型索引的概念懂讯。每一片可以通過模型來獲取的信息都被表示成一個模型索引荷憋。視圖和代理通過模型索引來請求被顯示的數(shù)據(jù)。
因此褐望,只有模型需要知道如何獲取數(shù)據(jù)勒庄,且被模型所管理的數(shù)據(jù)類型可以被定義地非常的通用。模型索引中包含一個創(chuàng)建他們的模型的指針譬挚,當(dāng)多個模型同時工作時锅铅,不會導(dǎo)致困擾。
QAbstractItemModel *model = index.model();
模型索引提供了對信息的暫時的引用减宣,還能被用來獲取或修改數(shù)據(jù)盐须,但需要通過模型來實現(xiàn)。由于模型可能隨時重構(gòu)它內(nèi)部的結(jié)構(gòu)漆腌,模型索引可能變得無效贼邓,因此不應(yīng)該被存儲阶冈。如果需要長期引用一條信息,一個persistent model index(持久模型索引)需要被創(chuàng)建塑径。暫時的模型索引用QModelIndex類女坑,而持久的模型索引用QPersitentModelIndex類。
想要獲取數(shù)據(jù)項的模型索引统舀,需要項模型提供3個必要屬性:行號row匆骗,列號column,和父項的模型索引誉简。
行和列
最基本的形式碉就,模型訪問表格,數(shù)據(jù)項用行號和列號來定位闷串。如下所示:
QModelIndex index = model->index(row, column, ...);
數(shù)據(jù)項的父項(parents of items)
對于樹形結(jié)構(gòu)瓮钥,一個項還可能是其他項的父項。因此在獲取模型索引時烹吵,還要提供父項:
QModelIndex index = model->index(row, column, parent);
項的角色(Item Roles)
模型中的數(shù)據(jù)項可以實現(xiàn)不同的角色碉熄,例如Qt::DisplayRule是用來訪問字符串的,在視圖中顯示為text肋拔。標準的角色定義在Qt::ItemDataRole中锈津。視圖用不同的方式展示各種角色的數(shù)據(jù),如下圖所示只损。因此給每個角色提供合適的信息很重要一姿。
QVariant value = model->data(index, role);
最常用的角色都定義在 Qt::ItemDataRole里七咧。通過提供合適的數(shù)據(jù)給每個角色跃惫,模型可以提供暗示(hint)給視圖和代理,告訴他們應(yīng)該如何把數(shù)據(jù)項展示給用戶艾栋。
總結(jié):
1.模型索引(Model indexes)為視圖(views)和代理(delegates)提供了關(guān)于模型中項目位置的信息爆存,這種提供信息的方式不依賴于任何底層數(shù)據(jù)結(jié)構(gòu)。
2.數(shù)據(jù)項通過row蝗砾,column以及他們父對象的索引來定位先较。
3.模型索引被模型構(gòu)建,當(dāng)視圖和代理需要的時候悼粮。
4.模型索引有父索引闲勺。
5.如果給一個索引提供了一個invalid的父索引,則表示表示該索引是頂層索引扣猫。
6.角色用來區(qū)分一個item上所包含的不同數(shù)據(jù)菜循。
視圖類
在模型/視圖架構(gòu)中,視圖從模型中獲取數(shù)據(jù)項并展示給用戶看申尤。視圖中展現(xiàn)數(shù)據(jù)的格式可能與數(shù)據(jù)存儲的結(jié)構(gòu)完全不同癌幕,這就是模型/視圖的優(yōu)點衙耕。
視圖通常用來管理數(shù)據(jù)的布局。視圖可以渲染特定的數(shù)據(jù)項勺远,或通過代理來處理渲染和編輯橙喘。
除了展示數(shù)據(jù)外,視圖提供在item中導(dǎo)航胶逢,以及item的選擇厅瞎。
視圖構(gòu)建時可以沒有模型,但想要視圖顯示信息初坠,就必須提供模型磁奖。視圖通過使用selections來跟蹤用戶選擇的items,然后每個視圖維護各自的選擇某筐,或分享給多個視圖比搭。
有些視圖,如QTableView和QTreeView除了顯示items南誊,還顯示header(表頭)身诺,這是通過另一個視圖類實現(xiàn)的,QHeaderView.
使用現(xiàn)成的視圖
Qt提供了3個現(xiàn)成的視圖:QListView, QTreeView和QTableView抄囚。
這三種視圖對絕大多數(shù)應(yīng)用來說是足夠了霉赡。
多個視圖可以共享選擇項,如下所示幔托。
secondTableView->setSelectionModel(firstTableView->selectionModel());
代理類
不同于MVC的模式穴亏,模型/視圖結(jié)構(gòu)沒有單獨的組件來管理用戶的交互。通常重挑,視圖負責(zé)把模型中的數(shù)據(jù)展示給用戶嗓化,并負責(zé)處理用戶的輸入。為了更靈活的處理用戶的輸入谬哀,交互用代理(delegate)來實現(xiàn)刺覆。代理負責(zé)提供輸入的能力,并且也負責(zé)數(shù)據(jù)項的渲染史煎。代理的標準接口定義在QAbstractItemDelegate類中谦屑。
代理被期望能夠渲染他們的內(nèi)容,通過實現(xiàn)paint()和sizeHint()函數(shù)篇梭。
代理(delegates)中的編輯器可以通過使用控件(widgets)來管理編輯過程氢橙,或者直接處理事件來實現(xiàn)。第一種方法(使用控件)將在本節(jié)的后續(xù)部分中介紹恬偷,并且在“SpinBox Delegate”示例中也有所展示悍手。
Pixelator例子展示了如何創(chuàng)建一個用戶代理來實現(xiàn)對table view的特定渲染。