Qt - ModelView模式學(xué)習(xí)筆記_第1頁
Qt - ModelView模式學(xué)習(xí)筆記_第2頁
Qt - ModelView模式學(xué)習(xí)筆記_第3頁
Qt - ModelView模式學(xué)習(xí)筆記_第4頁
Qt - ModelView模式學(xué)習(xí)筆記_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

Ref:/superjoel/article/details/5112120setD^taPBintEvenitcommitDataModelIndex<place”widget|Rowi(CGliumdiel&gat^)ht!p://blog.G5/superjoeliReal'm&at*ofdata(FiEe/Databas^.,JfetchupdatesetD^taPBintEvenitcommitDataModelIndex<place”widget|Rowi(CGliumdiel&gat^)ht!p://blog.G5/superjoeliReal'm&at*ofdata(FiEe/Databas^.,Jfetchupdates&tDatasctDatadata-dataGTiLangeddatarendering|SelectionmodelSelectionmode)Model1McdeEnUser|Painter也色訕i=蒼一般來說,Model里面并沒有真正存儲數(shù)據(jù)(數(shù)據(jù)少的話也可以直接存儲在Model里),它的數(shù)據(jù)是從真正的"肉(raw)"里取得,如一個diskfile,或database的queryresultset等等。那么這個model究竟是干什么用的呢?說白了吧,它就是負(fù)責(zé)將“肉”數(shù)據(jù)獲取并提供給view,然后將view所做的對“肉”數(shù)據(jù)的修改更新至真正的"肉"中。所以,讀寫文件、操作數(shù)據(jù)庫、網(wǎng)絡(luò)通訊等一系列與數(shù)據(jù)打交道的工作就在model中做了。有的時候“肉”可能真的很肥,所以model還有一項(xiàng)重要的工作就是把這些"肉”編號。這樣就出現(xiàn)了ModelIndex這個非常重要的類。一般來說,它使用一個2維的編號(row/colum)來對"肉”編號。但對于tree這種有層次結(jié)構(gòu)的數(shù)據(jù)來說,又加上一個parentindex作為第3個編號。即一個父親下面的葉子也是從0,0開始編號,獲取modelindex的時候用遞歸來實(shí)現(xiàn)。0K,現(xiàn)在model已經(jīng)有了一堆編好號碼的“肉”了,誰來買啊?“肉”便宜了。。。。。。View適時出現(xiàn),注意,很多view可以同時來買同一塊“肉”。(汗,不開玩笑了,這篇blog快水了)。當(dāng)view需要顯示某些數(shù)據(jù)時,它們通過modelindex從model中獲取數(shù)據(jù)(調(diào)用model的data函數(shù),當(dāng)model的data變化時,它也會自動發(fā)dataChangedsignal給所有的view以便它們更新)。當(dāng)然,在view中也可以調(diào)用model的setData函數(shù)來設(shè)定某個modelindex所對應(yīng)的數(shù)據(jù)。這里要說明一下model中的數(shù)據(jù),用QVarient來承載,可以是所有Qt支持的類型,比較貼心的是,數(shù)據(jù)可以分成多個角色(role),例如Qt::DisplayRole專用于顯示,Qt::BackgroundRole用于顯示背景色等等。所以在model中,你不光可以對“肉”進(jìn)行編號,還可以對“肉”進(jìn)行“深加工”,使它們更“好看”或是更“美味”。View組織這些數(shù)據(jù)并顯示,但卻沒有做真正的顯示工作,真正的工作留給了delegate。Delegate就是MVC中的Coview讓它顯示時它就在paint函數(shù)中顯示。當(dāng)然,你可以重載這個函數(shù)并實(shí)現(xiàn)你自己的顯示。你還可以給一個view設(shè)定rowdelegate和columdelegate專用于row和colum。當(dāng)用戶觸發(fā)了view的edittrigger時(如雙擊鼠標(biāo)或回車),view開始inplaceedit(beginEditing)。Delegate會在合適的地方創(chuàng)建一個合適的widget(如lineedit或combobox等)處理用戶的輸入,用戶輸入完成以后delegate獲取用戶的輸入并返回。這些輸入可以通過調(diào)用model的setData函數(shù)保存到真正的"肉"中。所以Delegate其實(shí)就是負(fù)責(zé)最終顯示數(shù)據(jù)和處理用戶交互的既然由用戶交互,最重要的肯定是用戶的選擇了。說一下selectionmodel。View將用戶選擇的itemindex全部存入selectionmodel中,顯示的時候根據(jù)selectionmodel的內(nèi)容顯示。另外,多個view可以共享同一個selectionmodel,這樣,當(dāng)你選中其中的一個時,另一個view中的相應(yīng)item也會被選中。QtModel/View/main-snapshot/model-view-programming.html介紹Qt4推出了一組新的itemview類,它們使用model/view結(jié)構(gòu)來管理數(shù)據(jù)與表示層的關(guān)系。這種結(jié)構(gòu)帶來的功能上的分離給了開發(fā)人員更大的彈性來定制數(shù)據(jù)項(xiàng)的表示,它也提供一個標(biāo)準(zhǔn)的model接口,使得更多的數(shù)據(jù)源可以被這些itemview使用。這里對model/view的結(jié)構(gòu)進(jìn)行了描述,結(jié)構(gòu)中的每個組件都進(jìn)行了解釋,給出了一些例子說明了提供的這些類如何使用。Model/View結(jié)構(gòu)Model-View-Controller(MVC),是從Smalltalk發(fā)展而來的一種設(shè)計(jì)模式,常被用于構(gòu)建用戶界面。經(jīng)典設(shè)計(jì)模式的著作中有這樣的描述:MVC由三種對象組成Model是應(yīng)用程序?qū)ο?,ew是它的屏幕表示,Controller定義了用戶界面如何對用戶輸入進(jìn)行響應(yīng)°MVC之前,用戶界面設(shè)計(jì)傾向于三者揉合在一起MVC對它們進(jìn)行了解耦,提高了靈活性與重用性。假如把view與controller結(jié)合在一起,結(jié)果就是model/view結(jié)構(gòu)。這個結(jié)構(gòu)依然是把數(shù)據(jù)存儲與數(shù)據(jù)表示進(jìn)行了分離,它與MVC都基于同樣的思想,但它更簡單一些。這種分離使得在幾個不同的view上顯示同一個數(shù)據(jù)成為可能,也可以重新實(shí)現(xiàn)新的view,而不必改變底層的數(shù)據(jù)結(jié)構(gòu)。為了更靈活的對用戶輸入進(jìn)行處理,引入了delegate這個概念。它的好處是,數(shù)據(jù)項(xiàng)的渲染與編程可以進(jìn)行定制。如上圖所示,model與數(shù)據(jù)源通訊,并提供接口給結(jié)構(gòu)中的別的組件使用。通訊的性質(zhì)依賴于數(shù)據(jù)源的種類與model實(shí)現(xiàn)的方式。view從model獲取modelindexes,后者是數(shù)據(jù)項(xiàng)的引用。通過把modelindexes提供給model,view可以從數(shù)據(jù)源中獲取數(shù)據(jù)。在標(biāo)準(zhǔn)的views中,delegate會對數(shù)據(jù)項(xiàng)進(jìn)行渲染,當(dāng)某個數(shù)據(jù)項(xiàng)被選中時,delegate通過modelindexes與model直接進(jìn)行交流??偟膩碚f,model/view相關(guān)類可以被分成上面所提到的三組:models,views,delegates。這些組件通過抽象類來定義,它們提供了共同的接口,在某些情況下,還提供了缺省的實(shí)現(xiàn)。抽象類意味著需要子類化以提供完整的其他組件希望的功能。這也允許實(shí)現(xiàn)定制的組件。models,views,delegates之間通過信號,槽機(jī)制來進(jìn)行通訊:從model發(fā)出的信號通知view數(shù)據(jù)源中的數(shù)據(jù)發(fā)生了改變。從view發(fā)出的信號提供了有關(guān)被顯示的數(shù)據(jù)項(xiàng)與用戶交互的信息。從delegate發(fā)生的信號被用于在編輯時通知model和view關(guān)于當(dāng)前編輯器的狀態(tài)信息。Models所有的itemmodels都基于QAbstractltemModel類,這個類定義了用于views和delegates訪問數(shù)據(jù)的接口。數(shù)據(jù)本身不必存儲在model,數(shù)據(jù)可被置于一個數(shù)據(jù)結(jié)構(gòu)或另外的類,文件,數(shù)據(jù)庫,或別的程序組件中。關(guān)于model的基本概念在ModelClasses部分中描述。QAbstractItemModel提供給數(shù)據(jù)一個接口,它非常靈活,基本滿足views的需要,無論數(shù)據(jù)用以下任何樣的形式表現(xiàn),如tables,lists,trees。然而,當(dāng)你重新實(shí)現(xiàn)一個model時,如果它基于table或list形式的數(shù)據(jù)結(jié)構(gòu),最好從QAbstractListModel,QAbstractTableModel開始做起,因?yàn)樗鼈兲峁┝诉m當(dāng)?shù)某R?guī)功能的缺省實(shí)現(xiàn)。這些類可以被子類化以支持特殊的定制需求。子類化model的過程在CreateNewModel部分討論QT提供了一些現(xiàn)成的models用于處理數(shù)據(jù)項(xiàng):QStringListModel用于存儲簡單的QString列表。QStandardltemModel管理復(fù)雜的樹型結(jié)構(gòu)數(shù)據(jù)項(xiàng),每項(xiàng)都可以包含任意數(shù)據(jù)。QDirModel提供本地文件系統(tǒng)中的文件與目錄信息。QSqlQueryModel,QSqlTableModel,QSqlRelationTableModel用來訪問數(shù)據(jù)庫。假如這些標(biāo)準(zhǔn)Model不滿足你的需要,你應(yīng)該子類化QAbstractItemModel,QAbstractListModel或是QAbstractTableModel來定制。Views不同的view都完整實(shí)現(xiàn)了各自的功能QListView把數(shù)據(jù)顯示為一個列表,QTableView把Model中的數(shù)據(jù)以table的形式表現(xiàn),QTreeView用具有層次結(jié)構(gòu)的列表來顯示model中的數(shù)據(jù)。這些類都基于QAbstractltemView抽象基類,盡管這些類都是現(xiàn)成的,完整的進(jìn)行了實(shí)現(xiàn),但它們都可以用于子類化以便滿足定制需求。DelegatesQAbstractItemDelegate是model/view架構(gòu)中的用于delegate的抽象基類。缺省的delegate實(shí)現(xiàn)在QltemDelegate類中提供。它可以用于Qt標(biāo)準(zhǔn)views的缺省delegate.排序在model/view架構(gòu)中,有兩種方法進(jìn)行排序,選擇哪種方法依賴于你的底層Model。假如你的model是可排序的,也就是它重新實(shí)現(xiàn)了QAbstractltemModel::sort()函數(shù)QTableView與QTreeView都提供了API,允許你以編程的方式對Model數(shù)據(jù)進(jìn)行排序。另外,你也可以進(jìn)行交互方式下的排序(例如,允許用戶通過點(diǎn)擊view表頭的方式對數(shù)據(jù)進(jìn)行排序),可以這樣做:把QHeaderView::sectionClicked()信號與QTableView::sortByColum()槽或QTreeView::sortByColumn()槽進(jìn)行聯(lián)結(jié)就好了。另一種方法是,假如你的model沒有提供需要的接口或是你想用listview表示數(shù)據(jù),可以用一個代理model在用view表示數(shù)據(jù)之前對你的model數(shù)據(jù)結(jié)構(gòu)進(jìn)行轉(zhuǎn)換。便利類許多便利類都源于標(biāo)準(zhǔn)的view類,它們方便了那些使用Qt中基于項(xiàng)的view與table類,它們不應(yīng)該被子類化,它們只是為Qt3的等價類提供一個熟悉的接口。這些類有QListWidget,QTreeWidget,QTableWidget,它們提供了如Qt3中的QListBox,QlistView,QTable相似的行為。這些類比View類缺少靈活性,不能用于任意的models,推介使用model/view的方法處理數(shù)據(jù)。介紹Qt提供了兩個標(biāo)準(zhǔn)的models:QStandardltemModel和QDirModelQStandardltemModel是一個多用途的model,可用于表示list,table,treeviews所需要的各種不同的數(shù)據(jù)結(jié)構(gòu)。這個model也持有數(shù)據(jù)。QDirModel維護(hù)相關(guān)的目錄內(nèi)容的信息,它本身不持有數(shù)據(jù),僅是對本地文件系統(tǒng)中的文件與目錄的描述。QDirModel是一個現(xiàn)成的model,很容易進(jìn)行配置以用于現(xiàn)存的數(shù)據(jù),使用這個model,可以很好地展示如何給一個現(xiàn)成的view設(shè)定model,研究如何用modelindexes來操縱數(shù)據(jù)。model與views的搭配使用QListView與QTreeView很適合與QDirModel搭配。下面的例子在treeview與listview顯示了相同的信息QDirModel提供了目錄內(nèi)容數(shù)據(jù)。這兩個Views共享用戶選擇,因此每個被選擇的項(xiàng)在每個view中都會被咼亮?!鐾瑇Twoviewsontothesamedirectorymodel■■N^me沁|Type|Modified_extensions上J3280Direclary2004-04-19T12:...』FAQ2080Dir&ct&ry2004-04-19T12:...Qjinclude由臥佬nsion5104Directory2OOW19T12;,.,fllNSTALLrOFAQ7777FAQFile2OO4-O4-13T12...□lib申“_indude10640Dirertary2OD4-O4-19T12:..._JLICENSE.GPLhDINSTALL4482INSTALLFile2OO4-O4-19T12..._JLICENSE.QPL白“_』ib72DirEdory2004-04-19T12:,.,_JMakefile:由」72Directory2004-04-19T12:...□MANIFEST744Dirprtory2004-04-19T12:..._nnksp-ecs=:■JREADME63READMEFile2004-04-19T12:,.,_||3iC5hJLICENSE.GPL18361GPLFile2OD4-O4-19T12:...」PLATFORMS”二|LI匚ENSE.QPL4689QPLFile2OD4-O4-19T12:..._pluginsL-[1Makefile2751File2OO3-O8-11T1G:...^1_qrnalce屮|111JREADMEzl先裝配出一個QDirModel以供使用,再創(chuàng)建views去顯示目錄的內(nèi)容。這給我展示了使用model的最簡單的方式。model的創(chuàng)建與使用都在main()函數(shù)中完成:intmain(intargc,char*argv[]){QApplicationapp(argc,argv);QSplitter*splitter=newQSplitter;QDirModel*model=newQDirModel;〃從缺省目錄創(chuàng)建數(shù)據(jù)QTreeView*tree=newQTreeView(splitter);tree->setModel(model);tree->setRootlndex(model->index(QDir::currentPath()));QListView*list=newQListView(splitter);list->setModel(model);list->setRootlndex(model->index(QDir::currentPath()));〃配置一個view去顯示model中的數(shù)據(jù),只需要簡單地調(diào)用setModel(),并把目錄model作為參數(shù)傳遞〃setRootlndex()告訴views顯示哪個目錄的信息,這需要提供一個modelindex,然后用這個//modelindex去model中去獲取數(shù)據(jù)//index()這個函數(shù)是QDirModel特有的,通過把一個目錄做為參數(shù),得到了需要的modelindex//其他的代碼只是窗口show出來,進(jìn)入程序的事件循環(huán)就好了splitter->setWindowTitle("Twoviewsontothesamedirectorymodel");splitter->show();returnapp.exec();}上面的例子并沒有展示如何處理數(shù)據(jù)項(xiàng)的選擇,這包括很多細(xì)節(jié),以后會提到。Model類基本概念在model/view構(gòu)架中,model為view和delegates使用數(shù)據(jù)提供了標(biāo)準(zhǔn)接口。在Qt中,標(biāo)準(zhǔn)接口QAbstractltemModel類中被定義。不管數(shù)據(jù)在底層以何種數(shù)據(jù)結(jié)構(gòu)存儲,QAabstractltemModel的子類會以層次結(jié)構(gòu)的形式來表示數(shù)據(jù),結(jié)構(gòu)中包含了數(shù)據(jù)項(xiàng)表。我們按這種約定來訪問model中的數(shù)據(jù)項(xiàng),但這個約定不會對如何顯示這些數(shù)據(jù)有任何限制。數(shù)據(jù)發(fā)生改變時,model通過信號槽機(jī)制來通知關(guān)聯(lián)的views0LisftModelTabkM-oddTreeModel1Root加ncQcmnwWcokjmn卜1cocpn;01HootitefnModelIndexes為了使數(shù)據(jù)存儲與數(shù)據(jù)訪問分開,引入了modelindex的概念。通過modelindex,可以引用model中的數(shù)據(jù)項(xiàng),Views和delegates都使用indexes來訪問數(shù)據(jù)項(xiàng),然后再顯示出來。因此,只有model需要了解如何獲取數(shù)據(jù),被model管理的數(shù)據(jù)類型可以非常廣泛地被定義。Modelindexes包含一個指向創(chuàng)建它們的model的指針,這會在配合多個model工作時避免混亂。QAbstractltemModel*model=index.model();modelindexes提供了對一項(xiàng)數(shù)據(jù)信息的臨時引用,通過它可以訪問或是修改model中的數(shù)據(jù)。既然model有時會重新組織內(nèi)部的數(shù)據(jù)結(jié)構(gòu),這時modelindexes便會失效,因此不應(yīng)該保存臨時的modelindexes。假如需要一個對數(shù)據(jù)信息的長期的引用,那么應(yīng)該創(chuàng)建一個persistentmodelindex。這個引用會保持更新。臨時的modelindexes由QModelIndex提供,而具有持久能力的modelindexes則由QPersistentModelIndex提供。在獲取對應(yīng)一個數(shù)據(jù)項(xiàng)的modelindex時,需要考慮有關(guān)于model的三個屬性:行數(shù),列數(shù),父項(xiàng)的modelindex。行與列在最基本的形式中,一個model可作為一個簡單的表來訪問,每個數(shù)據(jù)項(xiàng)由行,列數(shù)來定位。這必不意味著底層的數(shù)據(jù)用數(shù)組結(jié)構(gòu)來存儲。行和列的使用僅僅是一種約定,它允許組件之間相互通訊??梢酝ㄟ^指定model中的行列數(shù)來獲取任一項(xiàng)數(shù)據(jù),可以得到與數(shù)據(jù)項(xiàng)一一對應(yīng)的那個index。QModelIndexindex=model->index(row,column,...);Model為簡單的,單級的數(shù)據(jù)結(jié)構(gòu)如list與tables提供了接口,它們?nèi)缟厦娲a所顯示的那樣,不再需要別的信息被提供。當(dāng)我們在獲取一個

modelindex時,我們需要提供另外的信息。T^bleModelABCHootiterrtrowABCHootiterrtrow-0row=1row-2row-3上圖代表一個基本的tablemodel,它的每一項(xiàng)用一對行列數(shù)來定位。通過行列數(shù),可以獲取代表一個數(shù)據(jù)項(xiàng)的modelindex.QModellndexindexA=model->index(0,0,QModellndex());QModellndexindexB=model->index(1,1,QModelIndex());QModellndexindexC=model->index(2,1,QModelIndex());一個model的頂級項(xiàng),由QModelIndex()取得,它們上式被用作父項(xiàng)。父項(xiàng)類似于表的接口在搭配使用table或listview時理想的,這種行列系統(tǒng)與view顯示的方式是確切匹配的。然則,像treeviews這種結(jié)構(gòu)需要model提供更為靈活的接口來訪問數(shù)據(jù)項(xiàng)。每個數(shù)據(jù)項(xiàng)可能是別的項(xiàng)的父項(xiàng),上級的項(xiàng)可以獲取下級項(xiàng)的列表。當(dāng)獲取model中數(shù)據(jù)項(xiàng)的index時,我們必須指定關(guān)于數(shù)據(jù)項(xiàng)的父項(xiàng)的信息。在model外部,引用一個數(shù)據(jù)項(xiàng)的唯一方法就是通過modelindex,因此需要在求取modelindex時指定父項(xiàng)的信息。QModellndexindex=model->index(row,column,parent);上圖中,A項(xiàng)和C項(xiàng)作為model中頂層的兄弟項(xiàng):QModelIndexindexA=model->index(0,0,QModellndex());QModelIndexindexC=model->index(2,1,QModelIndex());A有許多孩子,它的一個孩子B用以下代碼獲?。篞ModelIndexindexB=model->index(1,0,indexA);項(xiàng)角色model中的項(xiàng)可以作為各種角色來使用,這允許為不同的環(huán)境提供不同的數(shù)據(jù)。舉例來說,Qt::DisplayRole被用于訪問一個字符串,它作為文本會在view中顯示。典型地,每個數(shù)據(jù)項(xiàng)都可以為許多不同的角色提供數(shù)據(jù),標(biāo)準(zhǔn)的角色在Qt::ItemDataRole中定義。我們可以通過指定modelindex與角色來獲取我們需要的數(shù)據(jù):QVariantvalue=model->data(index,role);角色指出了從model中引用哪種類型的數(shù)據(jù)。views可以用不同的形式顯示角色,因此為每個角色提供正確的信息是非常重要的。通過為每個角色提供適當(dāng)數(shù)據(jù),model也為views和delegates提供了暗示,如何正確地把這些數(shù)據(jù)項(xiàng)顯給用戶。不同的views可以自由地解析或忽略這些數(shù)據(jù)信息,對于特殊的場合,也可以定義一些附加的角色。概念總結(jié):1,Modelindexes為views與delegages提供model中數(shù)據(jù)項(xiàng)定位的信息,它與底層的數(shù)據(jù)結(jié)構(gòu)無關(guān)。通過指定行,列數(shù),父項(xiàng)的modelindex來引用數(shù)據(jù)項(xiàng)。依照別的組件的要求,modelindexes被model構(gòu)建。使用index()時,如果指定了有效的父項(xiàng)的modelindex,那么返回得到的modelindex對應(yīng)于父項(xiàng)的某個孩子。使用index()時,如果指定了無效的父項(xiàng)的modelindex,那么返回得到的modelindex對應(yīng)于頂層項(xiàng)的某個孩子。6,角色對一個數(shù)據(jù)項(xiàng)包含的不同類型的數(shù)據(jù)給出了區(qū)分。使用ModelIndexesQDirModel*model=newQDirModel;QModellndexparentindex=model->index(QDir::currentPath());intnumRows=model->rowCount(parentlndex);for(introw=0;row<numRows;++row){QModelIndexindex=model->index(row,0,parentIndex);tringtext=model->data(index,Qt::DisplayRole).toString();//Displaythetextinawidget.}以上的例子說明了從model中獲取數(shù)據(jù)的基本原則:1,model的尺寸可以從rowCount()與columnCount()中得出。這些函數(shù)通常都需要一個表示父項(xiàng)的modelindex。2,modelindexes用來從model中訪問數(shù)據(jù)項(xiàng),數(shù)據(jù)項(xiàng)用行,列,父項(xiàng)modelindex定位。為了訪問model頂層項(xiàng),可以使用QModelIndex()指定。數(shù)據(jù)項(xiàng)為不同的角色提供不同的數(shù)據(jù)。為了獲取數(shù)據(jù),除了modelindex之外,還要指定角色。創(chuàng)建新的Models介紹model/view組件之間功能的分離,允許創(chuàng)建model利用現(xiàn)成的views。這也可以使用標(biāo)準(zhǔn)的功能圖形用戶接口組件像QListView,QTableView和QTreeView來顯示來自各種數(shù)據(jù)源的數(shù)據(jù)為。QAbstractListModel類提供了非常靈活的接口,允許數(shù)據(jù)源以層次結(jié)構(gòu)的形式來管理信息,也允許以某種方式對數(shù)據(jù)進(jìn)行插入、刪除、修改和存儲。它也提供了對拖拽操作的支持。QAbstractListModel與QAbstractTableModel為簡單的非層次結(jié)構(gòu)的數(shù)據(jù)提供了接口,對于比較簡單的list和tablemodels來說,這是不錯的一個開始點(diǎn)。設(shè)計(jì)一個Model當(dāng)我們?yōu)榇嬖诘臄?shù)據(jù)結(jié)構(gòu)新建一個model時,首先要考慮的問題是應(yīng)該選用哪種model來為這些數(shù)據(jù)提供接口。假如數(shù)據(jù)結(jié)構(gòu)可以用數(shù)據(jù)項(xiàng)的列表或表來表示,那么可以考慮子類化QAbstractListModel或QAbstractTableModel,既然這些類已經(jīng)合理地對許多功能提供缺省實(shí)現(xiàn)。然而,假如底層的數(shù)據(jù)結(jié)構(gòu)只能表示成具有層次結(jié)構(gòu)的樹型結(jié)構(gòu),那么必須得子類化QAbstractItemModel無論底層的數(shù)據(jù)結(jié)構(gòu)采取何種形式,在特定的model中實(shí)現(xiàn)標(biāo)準(zhǔn)的QAbstractItemModelAPI總是一個不錯的主意,這使得可以使用更自然的方式對底層的數(shù)據(jù)結(jié)構(gòu)進(jìn)行訪問。這也使得用數(shù)據(jù)構(gòu)建model更為容易,其他的model/view組件也可以使用標(biāo)準(zhǔn)的API與之進(jìn)行交互。一個只讀model示例這個示例實(shí)現(xiàn)了一個簡單的,非層次結(jié)構(gòu)的,只讀的數(shù)據(jù)model,它基于QStringistModel類。它有一個QStringList作為它內(nèi)部的數(shù)據(jù)源,只實(shí)現(xiàn)了一些必要的接口。為了簡單化,它子類化了QAbstractListModel,這個基類提供了合理的缺省行為,對外提供了比QAbstractItemModel更為簡單的接口。當(dāng)我們實(shí)現(xiàn)一個model時,不要忘了QAbstractItemModel本身不存儲任何數(shù)據(jù),它僅僅提供了給views訪問數(shù)據(jù)的接口。classStringListModel:publicQAbstractListModel{Q_OBJECTpublic:StringListModel(constQStringList&strings,QObject*parent=0):QAbstractListModel(parent),stringList(strings){}introwCount(constQModelIndex&parent=QModellndex())const;QVariantdata(constQModelIndex&index,introle)const;QVariantheaderData(intsection,Qt::Orientationorientation,introle=Qt::DisplayRole)const;private:QStringListstringList;};除了構(gòu)造函數(shù),我們僅需要實(shí)現(xiàn)兩個函數(shù):rowCount()返回model中的行數(shù),data()返回與特定modelindex對應(yīng)的數(shù)據(jù)項(xiàng)。具有良好行為的model也會實(shí)現(xiàn)headerData(),它返回tree和tableviews需要的,在標(biāo)題中顯示的數(shù)據(jù)。因?yàn)檫@是一個非層次結(jié)構(gòu)的model,我們不必考慮父子關(guān)系。假如model具有層次結(jié)構(gòu),我們也應(yīng)該實(shí)現(xiàn)index()與parent()函數(shù)。Model的尺寸我們認(rèn)為model中的行數(shù)與stringlist中的string數(shù)目一致:intStringListModel::rowCount(constQModellndex&parent)const{returnstringList.count();}在缺省情況下,從QAbstractListModel派生的model只具有一列,因此不需要實(shí)現(xiàn)columnCount()。Model標(biāo)題與數(shù)據(jù)QVariantStringListModel::data(constQModelIndex&index,introle)const{if(!index.isValid())returnQVariant();if(index.row()>=stringList.size())returnQVariant();if(role==Qt::DisplayRole)returnstringList.at(index.row());elsereturnQVariant();}QVariantStringListModel::headerData(intsection,Qt::Orientationorientation,introle)const{if(role!=Qt::DisplayRole)returnQVariant();if(orientation==Qt::Horizontal)returnQString("Column%1").arg(section);elsereturnQString("Row%1").arg(section);}一個數(shù)據(jù)項(xiàng)可能有多個角色,根據(jù)角色的不同輸出不同的數(shù)據(jù)。上例中,model中的數(shù)據(jù)項(xiàng)只有一個角色,DisplayRole,然而我們也可以重用提供給DisplayRole的數(shù)據(jù),作為別的角色使用,如我們可以作為ToolTipRole來用??删庉嫷膍odel上面我們演示了一個只讀的model,它只用于向用戶顯示,對于許多程序來說,可編輯的listmodel可能更有用。我們只需要給只讀的model提供另外兩個函數(shù)flags()與setData()的實(shí)現(xiàn)。下列函數(shù)聲明被添加到類定義中:Qt::ltemFlagsflags(constQModelIndex&index)const;boolsetData(constQModelIndex&index,constQVariant&value,introle=Qt::EditRole);讓model可編輯delegate會在創(chuàng)建編輯器之前檢查數(shù)據(jù)項(xiàng)是否是可編輯的。model必須得讓delegate知道它的數(shù)據(jù)項(xiàng)是可編輯的。這可以通過為每一個數(shù)據(jù)項(xiàng)返回一個正確的標(biāo)記得到,在本例中,我們假設(shè)所有的數(shù)據(jù)項(xiàng)都是可編輯可選擇的:Qt::ItemFlagsStringListModel::flags(constQModelIndex&ndex)const{if(!index.isValid())returnQt::ltemlsEnabled;returnQAbstractltemModel::flags(index)|Qt::ItemIsEditable;}我們不必知道delegate執(zhí)行怎樣實(shí)際的編輯處理過程,我們只需提供給delegate一個方法,delegate會使用它對model中的數(shù)據(jù)進(jìn)行設(shè)置。這個特殊的函數(shù)就是setData():boolStringListModel::setData(constQModelIndex&ndex,constQVariant&value,introle){if(index.isValid()&&role==Qt::EditRole){stringList.replace(index.row(),value.toString());emitdataChanged(index,index);returntrue;}returnfalse;}當(dāng)數(shù)據(jù)被設(shè)置后,model必須得讓views知道一些數(shù)據(jù)發(fā)生了變化,這可通過發(fā)射一個dataChanged()信號實(shí)現(xiàn)。因?yàn)橹挥幸粋€數(shù)據(jù)項(xiàng)發(fā)生了變化,因此在信號中說明的變化范圍只限于一個modelindex。插入,刪除行在model中改變行數(shù)與列數(shù)是可能的。當(dāng)然在本列中,只考慮行的情況,我們只需要重新實(shí)現(xiàn)插入、刪除的函數(shù)就可以了,下面應(yīng)在類定義中聲明:boolinsertRows(intposition,introws,constQModelIndex&index=QModelIndex());boolremoveRows(intposition,introws,constQModelIndex&index=QModelIndex());既然model中的每行對應(yīng)于列表中的一個string,因此,insertRows()函數(shù)在stringlist中指定位置插入一個空string,父index通常用于決定model中行列的位置,本例中只有一個單獨(dú)的頂級項(xiàng),困此只需要在list中插入空string0boolStringListModel::insertRows(intposition,introws,constQModellndex&parent){beginlnsertRows(QModellndex(),position,position+rows-1);for(introw=0;row<rows;++row){stringList.insert(position,"");}endInsertRows();returntrue;}beginInsertRows()通知其他組件行數(shù)將會改變。endInsertRows()對操作進(jìn)行確認(rèn)與通知。返回true表示成功。刪除操作與插入操作類似:boolStringListModel::removeRows(intposition,introws,constQModelIndex&parent){beginRemoveRows(QModellndex(),position,position+rows-1);for(introw=0;row<rows;++row){stringList.removeAt(position);}endRemoveRows();returntrue;}View類概念在model/view架構(gòu)中,view從model中獲得數(shù)據(jù)項(xiàng)然后顯示給用戶。數(shù)據(jù)顯示的方式不必與model提供的表示方式相同,可以與底層存儲數(shù)據(jù)項(xiàng)的數(shù)據(jù)結(jié)構(gòu)完全不同。內(nèi)容與顯式的分離是通過由QAbstractltemModel提供的標(biāo)準(zhǔn)模型接口,由QAsbstractltemview提供的標(biāo)準(zhǔn)視圖接口共同實(shí)現(xiàn)的。普遍使用modelindex來表示數(shù)據(jù)項(xiàng)0view負(fù)責(zé)管理從model中讀取的數(shù)據(jù)的外觀布局。它們自己可以去渲染每個數(shù)據(jù)項(xiàng),也可以利用delegate來既處理渲染又進(jìn)行編輯。除了顯示數(shù)據(jù),views也處理數(shù)據(jù)項(xiàng)的導(dǎo)航,參與有關(guān)于數(shù)據(jù)項(xiàng)選擇的部分功能。view也實(shí)現(xiàn)一些基本的用戶接口特性,如上下文菜單與拖拽功能。view也為數(shù)據(jù)項(xiàng)提供了缺省的編程功能,也可搭配delegate實(shí)現(xiàn)更為特殊的定制編輯的需求。一個view創(chuàng)建時必不需要model,但在它能顯示一些真正有用的信息之前,必須提供一個model。view通過使用selections來跟蹤用戶選擇的數(shù)據(jù)項(xiàng)。每個view可以維護(hù)單獨(dú)使用的selections,也可以在多個views之間共享。有些views,如QTableView和QTreeView,除數(shù)據(jù)項(xiàng)之外也可顯示標(biāo)題(Headers),標(biāo)題部分通過一個view來實(shí)現(xiàn),QHeaderView。標(biāo)題與view—樣總是從相同的model

中獲取數(shù)據(jù)。從modeI中獲取數(shù)據(jù)的函數(shù)是QabstractltemModel::headerDate(),—般總是以表單的形式中顯示標(biāo)題信息??梢詮腝HeaderView子類化,以實(shí)現(xiàn)更為復(fù)雜的定制化需求。使用現(xiàn)成的viewQt提供了三個現(xiàn)成的view類,它們能夠以用戶熟悉的方式顯示model中的數(shù)據(jù)。QListView把model中的數(shù)據(jù)項(xiàng)以一個簡單的列表的形式顯示,或是以經(jīng)典的圖標(biāo)視圖的形式顯示。QTreeView把model中的數(shù)據(jù)項(xiàng)作為具有層次結(jié)構(gòu)的列表的形式顯示,它允許以緊湊的深度嵌套的結(jié)構(gòu)進(jìn)行顯示。QTableView卻是把model中的數(shù)據(jù)項(xiàng)以表格的形式展現(xiàn),更像是一個電子表格應(yīng)用程序的外觀布局。ii1”曲,ii1”曲,JnewJffiw.pra斶[締除IbjDhAiyQJ.0聆曲腎M嬸,1JvwvltwAb41(-I—1J以上這些標(biāo)準(zhǔn)view的行為足以應(yīng)付大多數(shù)的應(yīng)用程序,它們也提供了一些基本的編輯功能,也可以定制特殊的需求。使用model以前的例子中創(chuàng)建過一個stringlistmodel,可以給它設(shè)置一些數(shù)據(jù),再創(chuàng)建一個view把model中的內(nèi)容展示出來:intmain(intargc,char*argv[]){QApplicationapp(argc,argv);//Unindentedforquotingpurposes:QStringListnumbers;numbers<<"One"<<"Two"<<"Three"<<"Four"<<"Five";QAbstractltemModel*model=newStringListModel(numbers);〃要注意的是,這里把StringListModel作為一個QAbstractItemModel來使用。這樣我們就可以〃使用model中的抽象接口,而且如果將來我們用別的model代替了當(dāng)前這個model,這些代碼也會照樣工作。//QListView提供的列表視圖足以滿足當(dāng)前這個model的需要了。QListView*view=newQListView;view->setModel(model);view->show();returnapp.exec();}view會渲染model中的內(nèi)容,通過model的接口來訪問它的數(shù)據(jù)。當(dāng)用戶試圖編輯數(shù)據(jù)項(xiàng)時,view會使用缺省的delegate來提供一個編輯構(gòu)件。一個model,多個views為多個views提供相同的model是非常簡單的事情,只要為每個view

設(shè)置相同的model。QTableView*firstTableView=newQTableView;QTableView*secondTableView=newQTableView;firstTableView->setModel(model);secondTableView->setModel(model);在model/view架構(gòu)中信號、槽機(jī)制的使用意味著model中發(fā)生的改變會傳遞中聯(lián)結(jié)的所有view中,這保證了不管我們使用哪個view,不管我們使用哪個view,訪問的都是同樣的一份數(shù)據(jù)。上面的圖展示了一個model上的兩個不同的views,盡管在不同的view中顯示的model中的數(shù)據(jù)是一致的,每個view都維護(hù)它們自己的內(nèi)部選擇模型,但有時候在某些情況下,共享一個選擇模型也是合理的。處理數(shù)據(jù)項(xiàng)的選擇view中數(shù)據(jù)項(xiàng)選擇機(jī)制由QltemSelectionModel類提供。所有標(biāo)準(zhǔn)的view缺省都構(gòu)建它們自己的選擇模型,以標(biāo)準(zhǔn)的方式與它們交互。選擇模型可以用selectionModel()函數(shù)取得,替代的選擇模型也可以通過setSelectionModel()來設(shè)置。當(dāng)我們想在一個model上提供多個一致的views時,這種對選擇模型的控制能力非常有用。通常來講,除非你子類化一個model或view,你不必直接操縱selections的內(nèi)容。多個views之間共享選擇接著上邊的例子,我們可以這樣:secondTableView->setSelectionModel(firstTableView->selectionModel());現(xiàn)在所有views都在同樣的選擇模型上操作,數(shù)據(jù)與選擇項(xiàng)都保持同步。上面的例子中,兩個view的類型是相同的,假如這兩個view類型不同,那么所選擇的數(shù)據(jù)項(xiàng)在每個view中的表現(xiàn)形式會有很大的不同。例如,在一個tableview中一個連續(xù)的選擇,在一個treeview中表現(xiàn)出來的可能會是幾個高亮的數(shù)據(jù)項(xiàng)片斷的組合。在views中選擇數(shù)據(jù)項(xiàng)概念用于新的view類中的選擇模型比Qt3中的模型有了很大的改進(jìn)。它為基于model/view架構(gòu)的選擇提供了更為全面的描述。盡管對提供了的views來說,負(fù)責(zé)操縱選擇的標(biāo)準(zhǔn)類已經(jīng)足以應(yīng)付,但是你也可以創(chuàng)建特定的選擇模型來滿足你特殊的需求。關(guān)于在view被選擇的數(shù)據(jù)項(xiàng)的信息保持在QltemSelectionModel類的實(shí)例中。它也為每個獨(dú)立的model中的數(shù)據(jù)項(xiàng)維護(hù)modelindexes信息,與任何views都關(guān)聯(lián)關(guān)系。既然一個model可用于多個views,那么在多個views之間共享選擇信息也是可以做到的,這使得多個views可以以一致的方式進(jìn)行顯示。選擇由多個選擇范圍組成。通過僅僅記錄開始modelindexes與結(jié)束modelindexes,最大化地記錄了可以選擇的范圍。非連續(xù)選擇數(shù)據(jù)項(xiàng)由多個選擇范圍來描述。選擇模型記錄modelindexes的集合來描述一個選擇。最近選擇的數(shù)據(jù)項(xiàng)被稱為currentselection。應(yīng)用程序可以通過使用某種類型的選擇命令來修改選擇的效果。在進(jìn)行選擇操作時,可以把QItemSelectionModel看成是model中所有數(shù)據(jù)項(xiàng)選擇狀態(tài)的一個記錄。一旦建立一個選擇模型,所有項(xiàng)的集合都可以選擇,撤消選擇,或者選擇狀態(tài)進(jìn)行切換而不需要知道哪個數(shù)據(jù)項(xiàng)是否已經(jīng)被選擇過。所有被選擇的項(xiàng)的indexes在任何時候都可以得到,通過信號槽機(jī)制可以通知別的組件發(fā)生的變化。使用選擇模型標(biāo)準(zhǔn)view類提供了缺省的選擇模型,它們可以在大次數(shù)程序中使用。一個view中的選擇模型可以通過調(diào)用view的函數(shù)selectionModel()取得,也可以通過setSelectionModel()在多個views之間共享選擇模型,因此總的來說構(gòu)建一個新的模型一般情況不太必要。通過給QltemSelection指定一個model,—對modelindexes,可以創(chuàng)建一個選擇。indexes的用法依賴于給定的model,這兩個indexes被解釋成選擇的區(qū)塊中的左上角項(xiàng)和右下角項(xiàng)。model中的項(xiàng)的選擇服從于選擇模型。選擇項(xiàng)構(gòu)建一個tablemodel,它有32個項(xiàng),用一個tableview進(jìn)行顯示:TableModel*model=newTableModel(8,4,&app);QTableView*table=newQTableView(O);table->setModel(model);QItemSelectionModel*selectionModel=table->selectionModel();QModelIndextopLeft;QModelIndexbottomRight;topLeft=model->index(0,0,QModelIndex());bottomRight=model->index(5,2,QModelIndex());QItemSelectionselection(topLeft,bottomRight);selectionModel->select(selection,QltemSelectionModel::Select);結(jié)果如下:讀取選擇狀態(tài)存儲在選擇模型中indexes可以用selectionlndexes()函數(shù)來讀取。它返回一個未排序的modelindexes列表,我們可以遍歷它,如果我們知道他們關(guān)聯(lián)于哪個model的話。QModellndexListindexes=selectionModel->selectedlndexes();QModellndexindex;foreach(index,indexes){QStringtext=QString(”(%1,%2)").arg(index.row()).arg(index.column());model->setData(index,text);}選擇模型在選擇發(fā)生變化時會發(fā)出信號。這用于通知別的組件包括整體與當(dāng)前焦點(diǎn)項(xiàng)所發(fā)生的變化。我們可以連接selectionChanged()信號到一個槽,檢查當(dāng)信號產(chǎn)生時哪些項(xiàng)被選擇或被取消選擇。這個槽被調(diào)用時帶有兩個參數(shù),它們都是QItemSelection對象,一個包含新被選擇的項(xiàng),另一個包含新近被取消選擇的項(xiàng)。下面的代碼演示了給新選擇的項(xiàng)添加數(shù)據(jù)內(nèi)容,新近被取消選擇的項(xiàng)的內(nèi)容被清空。voidMainWindow::updateSelection(constQItemSelection&selected,constQItemSelection&deselected){QModellndexindex;QModellndexListitems=selected.indexes();foreach(index,items){QStringtext=QString(”(%1,%2)").arg(index.row()).arg(index.column());model->setData(index,text);}items=deselected.indexes();foreach(index,items)model->setData(index,"");}也可以通過響應(yīng)currentChanged()信號來跟蹤當(dāng)前焦點(diǎn)項(xiàng).對應(yīng)的槽就有兩個接收參數(shù),一個表示之前的焦點(diǎn),另一個表示當(dāng)前的焦點(diǎn)。voidMainWindow::changeCurrent(constQModelIndex¤t,constQModelIndex&previous){statusBar()->showMessage(tr("Movedfrom(%1,%2)to(%3,%4)").arg(previous.row()).arg(previous.column()).arg(current.row()).arg(current.column()));}更新選擇選擇指令是通過選擇標(biāo)志提供的,它被定義在QItemSelectionModel::SelectionFlag中。常用的有Select標(biāo)記,Toggle標(biāo)記,Deselect標(biāo)記‘Current標(biāo)記,Clear標(biāo)記,其意義一目了然。沿上面例子的結(jié)果執(zhí)行以下代碼:QltemSelectiontoggleSelection;topLeft=model->index(2,1,QModelIndex());bottomRight=model->index(7,3,QModelIndex());toggleSelection.select(topLeft,bottomRight);selectionModel->select(toggleSelection,QltemSelectionModel::Toggle);結(jié)果如下:缺省情況下,選擇指令只針對單個項(xiàng)(由modelindexes指定)。然而,選擇指令可以通過與另外標(biāo)記的結(jié)合來改變整行和整列。舉例來說,假如你只使用一個index來調(diào)用select(),但是用Select標(biāo)記與Rows標(biāo)記的組合,那么包括那個項(xiàng)的整行都將被選擇??匆韵率纠篞ItemSelectioncolumnSelection;topLeft=model->index(0,1,QModelIndex());bottomRight=model->index(0,2,QModelIndex());columnSelection.select(topLeft,bottomRight);selectionModel->select(columnSelection,QItemSelectionModel::Select|QltemSelectionModel::Columns);QItemSelectionrowSelection;topLeft=model->index(0,0,QModelIndex());bottomRight=model->index(1,0,QModelIndex());rowSelection.select(topLeft,bottomRight);selectionModel->select(rowSelection,QItemSelectionModel::Select|QItemSelectionModel::Rows);結(jié)果如下選擇模型中所有項(xiàng)為了選擇model中的所有項(xiàng),必須先得創(chuàng)建一個選擇,它包括當(dāng)前層次上的所有項(xiàng):QModellndextopLeft=model->index(0,0,parent);QModellndexbottomRight=model->index(model->rowCount(parent)-1,model->columnCount(parent)-1,parent);QItemSelectionselection(topLeft,bottomRight);selectionModel->select(selection,QltemSelectionModel::Select);頂級index可以這樣:QModelIndexparent=QModelIndex();對具有層次結(jié)構(gòu)的model來說,可以使用hasChildren()函數(shù)來決定給定項(xiàng)是否是其它項(xiàng)的父項(xiàng)。Delegate類概念與MVC模式不同,model/view結(jié)構(gòu)沒有用于與用戶交互的完全獨(dú)立的組件。一般來講,view負(fù)責(zé)把數(shù)據(jù)展示給用戶,也處理用戶的輸入。為了獲得更多的靈性性,交互通過delegagte執(zhí)行。它既提供輸入功能又負(fù)責(zé)渲染view中的每個數(shù)據(jù)項(xiàng)??刂芼elegates的標(biāo)準(zhǔn)接口在QAbstractltemDelegate類中定義。Delegates通過實(shí)現(xiàn)paint()和sizeHint()以達(dá)到渲染內(nèi)容的目的。然而,簡單的基于widget的delegates,可以從QItemDelegate子類化,而不是QAbstractltemDelegate,這樣可以使用它提供的上述函數(shù)的缺省實(shí)現(xiàn)。delegate可以使用widget來處理編輯過程,也可以直接對事件進(jìn)行處理。使用現(xiàn)成的delegateQt提供的標(biāo)準(zhǔn)views都使用QItemDelegate的實(shí)例來提供編輯功能。它以普通的風(fēng)格來為每個標(biāo)準(zhǔn)view渲染數(shù)據(jù)項(xiàng)。這些標(biāo)準(zhǔn)的views包括:QListView,QTableView,QTreeView。所有標(biāo)準(zhǔn)的角色都通過標(biāo)準(zhǔn)views包含的缺省delegate進(jìn)行處理。一個view使用的delegate可以用itemDelegate()函數(shù)取得,而setItemDelegate()函數(shù)可以安裝一個定制delegateo一個簡單的delegate這個delegate使用QSpinBox來提供編輯功能。它主要想用于顯示整數(shù)的models上。盡管我們已經(jīng)建立了一個基于整數(shù)的tablemodel,但我們也可以使用QStandardltemModel,因?yàn)閐elegate可以控制數(shù)據(jù)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論