| 3 | 1/1 | 返回列表 |
| 查看: 866 | 回復(fù): 2 | |||
yalefield金蟲 (文壇精英)
老漢一枚
|
[交流]
【轉(zhuǎn)帖】Qt的Model/View Framework解析 已有2人參與
|
|
http://blog.csdn.net/superjoel/archive/2009/12/31/5112120.aspx 作者:superjoel 看到一個(gè)老外在blog中寫到:Model/View是他認(rèn)為Qt中最不好的一部分了。 真的是這樣嗎? 為了回饋開源社區(qū),我寫了這篇blog,寫的是我認(rèn)為比較有價(jià)值的東東。 題目起得是解析,但也沒有特別細(xì)節(jié)的介紹,點(diǎn)到為止,有興趣的Tx 可以繼續(xù)討論。我所看的資料有《C++ GUI Programming with Qt 4, Second Edition》、Qt官網(wǎng)和Qt源代碼。 在UI中,最常用的就是list/grid/tree了(在Qt中,grid被稱為table)。尤其是做那些數(shù)據(jù)庫相關(guān)的程序,可能每個(gè)界面都要用到 list或grid。在Qt中,它們被歸為item view class。有兩種實(shí)現(xiàn),一種叫item based,這些類名以widget結(jié)尾,如QListWidget等。另一種叫model based,這些類以view結(jié)尾,如QListView等。 item based widget使用起來很簡(jiǎn)單,與MFC中的CListCtrl/CTreeCtrl等差不多。 這些類,既是數(shù)據(jù)容器(data container),又負(fù)責(zé)顯示(presentation),還要處理與用戶的交互(user action)。但,使用這些類的缺點(diǎn),就是在處理大量數(shù)據(jù)顯示時(shí)顯得力不從心。 我記得使用CListCtrl的report mode顯示超過5萬條記錄時(shí)需要等待很長(zhǎng)時(shí)間。因?yàn)樗阉械臄?shù)據(jù)都拷貝進(jìn)來,再顯示,比如這個(gè)list一次只能顯示50條記錄,但它把剩下的 49950條也拷貝進(jìn)了內(nèi)存,并為每條記錄的顯示都加上格式等額外的信息。 我想這種實(shí)現(xiàn)對(duì)于專業(yè)的軟件來說是無法接受的。那么,怎么提高它們的性能呢? 有些人想出了virtual list/grid。它們顯示之前并不需要把所有的數(shù)據(jù)都裝進(jìn)來,如有5萬條記錄,剛開始時(shí)它顯示前50條,顯示哪條時(shí)去取哪條。也就是說后49950條它不管,當(dāng)用戶移動(dòng)滾動(dòng)條或上下鍵改變顯示內(nèi)容時(shí),它會(huì)根據(jù)一系列參數(shù)計(jì)算出目前需要顯示哪幾條。如1000-1050條,那么再去取這幾條數(shù)據(jù)顯示。 由于實(shí)現(xiàn)起來有一定的難度,所以很多MFC實(shí)現(xiàn)的virtual list/grid顯示出來都是“素顏”的(能快速顯示出來已經(jīng)不錯(cuò)了)。當(dāng)然,coding無止境,這種取數(shù)據(jù)的方式我們稱為delayed fetch,如果實(shí)現(xiàn)不好的化,當(dāng)電腦速度慢且用戶急速拖滾動(dòng)條時(shí)也有可能出現(xiàn)白屏、拖尾、模糊等情況。所以,還可以做prefetch和caching來盡力避免這些情況。 好了,這里鋪墊的已經(jīng)夠多了,下面我們直奔主題。 model based view就是我們今天要重點(diǎn)“解析”的。對(duì)應(yīng)的UI類有QListView/QTreeView/QTableView。那么這些view與上面的 widget有什么區(qū)別呢?名字就是最大的區(qū)別。widget本來就是指小東東,而view可是很“大氣”的視圖哦。那么這些view大哥是怎么顯示的呢?下面就說說我不愿多說、但又不得不說的一個(gè)設(shè)計(jì)模式:MVC (Model/View/Controller),model代表數(shù)據(jù)(data set),view代表顯示(presentation),controller處理與用戶交互(user action)。 MVC模式源自smalltalk,現(xiàn)在好像被用的很多了。它的作用其實(shí)是把上面介紹的類似CListCtrl這樣的類解放出來,因?yàn)樗鼈円珊芏嗍虑,太累了。還有就是對(duì)于有些程序員來說,實(shí)現(xiàn)一個(gè)功能復(fù)雜的類可能會(huì)力不從心。分成3個(gè)類每個(gè)類解決不同的問題可能更好實(shí)現(xiàn)。從軟件工程的角度上講,這種方式的decoupling可以降低系統(tǒng)風(fēng)險(xiǎn)。好了,關(guān)于MVC,大家想了解更多的話可以繼續(xù)在網(wǎng)上搜索。 模式是MVC,那么Qt是怎么實(shí)現(xiàn)的呢?我打開了Qt的官網(wǎng)介紹(http://qt.nokia.com/doc/4.6/model-view- programming.html)。我暈,看到了幾十個(gè)類,而且很多都有繼承關(guān)系。很容易讓人迷失啊。不過不要緊,經(jīng)過2天的學(xué)習(xí),我對(duì)它們做了總結(jié),見下圖。Tx們可以從我這幅圖開始,抓住該framework的主線,免得迷失在茫茫淚(類)海:-) (待續(xù)) |
金蟲 (文壇精英)
老漢一枚
|
一般來說,Model里面并沒有真正存儲(chǔ)數(shù)據(jù)(數(shù)據(jù)少的話也可以直接存儲(chǔ)在Model 里),它的數(shù)據(jù)是從真正的“肉(raw)”里取得,如一個(gè)disk file,或database的query result set等等。 那么,這個(gè)model究竟是干什么用的呢?說白了吧,它就是負(fù)責(zé)將“肉”數(shù)據(jù)獲取并提供給view,然后將view所做的對(duì)“肉”數(shù)據(jù)的修改更新至真正的“肉”中。所以,讀寫文件、操作數(shù)據(jù)庫、網(wǎng)絡(luò)通訊等一系列與數(shù)據(jù)打交道的工作就在model中做了。 有的時(shí)候“肉”可能真的很肥,所以 model還有一項(xiàng)重要的工作就是把這些“肉”編號(hào)。這樣就出現(xiàn)了Model Index這個(gè)非常重要的類。一般來說,它使用一個(gè)2維的編號(hào)(row/colum)來對(duì)“肉”編號(hào)。但對(duì)于tree這種有層次結(jié)構(gòu)的數(shù)據(jù)來說,又加上一個(gè)parent index作為第3個(gè)編號(hào)。即:一個(gè)父親下面的葉子也是從0,0開始編號(hào),獲取model index的時(shí)候用遞歸來實(shí)現(xiàn)。 OK,現(xiàn)在model已經(jīng)有了一堆編好號(hào)碼的“肉”了,誰來買啊?“肉”便宜咯...... View適時(shí)出現(xiàn),注意,很多view可以同時(shí)來買同一塊“肉”。(汗,不開玩笑了,這篇blog快水了)。當(dāng)view需要顯示某些數(shù)據(jù)時(shí),它們通過 model index從model中獲取數(shù)據(jù)(調(diào)用model的data函數(shù),當(dāng)model的data變化時(shí),它也會(huì)自動(dòng)發(fā)dataChanged signal給所有的view以便它們更新)。 當(dāng)然,在view中也可以調(diào)用model的setData函數(shù)來設(shè)定某個(gè)model index所對(duì)應(yīng)的數(shù)據(jù)。這里要說明一下model中的數(shù)據(jù),用QVarient來承載,可以是所有Qt支持的類型,比較貼心的是,數(shù)據(jù)可以分成多個(gè)角色 (role),例如Qt: isplayRole專用于顯示,Qt::BackgroundRole用于顯示背景色等等。所以在model中,你不光可以對(duì)“肉”進(jìn)行編號(hào),還可以對(duì)“肉”進(jìn)行“深加工”,使它們更“好看”或是更“美味”。View組織這些數(shù)據(jù)并顯示,但卻沒有做真正的顯示工作,真正的工作留給了delegate。 Delegate就是MVC中的C。view讓它顯示時(shí)它就在paint函數(shù)中顯示。當(dāng)然,你可以重載這個(gè)函數(shù)并實(shí)現(xiàn)你自己的顯示。你還可以給一個(gè) view設(shè)定row delegate和colum delegate專用于row和colum。當(dāng)用戶觸發(fā)了view的edit trigger時(shí)(如雙擊鼠標(biāo)或回車),view開始in place edit (beginEditing)。Delegate會(huì)在合適的地方創(chuàng)建一個(gè)合適的widget(如line edit或combo box等)處理用戶的輸入,用戶輸入完成以后delegate獲取用戶的輸入并返回。這些輸入可以通過調(diào)用model的setData函數(shù)保存到真正的 “肉”中。所以Delegate其實(shí)就是負(fù)責(zé)最終顯示數(shù)據(jù)和處理用戶交互的。 既然由用戶交互,最重要的肯定是用戶的選擇了。 說一下selection model。View將用戶選擇的item index全部存入selection model中,顯示的時(shí)候根據(jù)selection model的內(nèi)容顯示。另外,多個(gè)view可以共享同一個(gè)selection model,這樣,當(dāng)你選中其中的一個(gè)時(shí),另一個(gè)view中的相應(yīng)item也會(huì)被選中。 最后總結(jié)一下。 Smalltalk怎么實(shí)現(xiàn)的MVC我不清楚,但是Qt model/view framework對(duì)MVC的實(shí)現(xiàn)完全是基于C++的虛基類和虛函數(shù)特性。MVC各部分之間的聯(lián)通除了應(yīng)用signal/slot之外,就全是虛函數(shù)了。 Tx們?nèi)绻信d趣的話,可以看看model/view framework源代碼中那些QAbstract開頭的類,它們定義了統(tǒng)一的接口虛函數(shù)后,派生類只需要重新實(shí)現(xiàn)這些函數(shù)即可。這里再重復(fù)一下學(xué)習(xí)這部分時(shí)Tx們應(yīng)該關(guān)注的2條主線。 無論什么view,作用都是把數(shù)據(jù)顯示出來,那么第一條主線是數(shù)據(jù)怎樣從數(shù)據(jù)源顯示到view中。 顯示的數(shù)據(jù)可能被修改,所以第二條主線就是修改后的數(shù)據(jù)怎樣再更新至數(shù)據(jù)源中。如果你能把這2幅sequence map畫出來,相信你就已經(jīng)完全明白了。 我不知道Qt在這里的設(shè)計(jì)是不是做了太多的decoupling。反正靈活性是足夠了(model/view/delegate都可以繼承),但無法讓程序員快速掌握。雖然Qt已經(jīng)弱化了delegate并而且為view提供默認(rèn)的delegate。但在做具體項(xiàng)目時(shí),程序員大部分時(shí)候還是要根據(jù)具體情況重寫派生類數(shù)的。這對(duì)于那些想快速做界面并顯示的程序員來說,可能會(huì)難以接受。 總之,這個(gè)輪子做的是足夠精細(xì)了,但一般在使用之前你還要自己擰點(diǎn)螺絲,上點(diǎn)油。 |
金蟲 (著名寫手)

| 3 | 1/1 | 返回列表 |
| 最具人氣熱帖推薦 [查看全部] | 作者 | 回/看 | 最后發(fā)表 | |
|---|---|---|---|---|
|
[考研] 284求調(diào)劑 +3 | yanzhixue111 2026-03-23 | 6/300 |
|
|---|---|---|---|---|
|
[考研] 336化工調(diào)劑 +4 | 王大坦1 2026-03-23 | 5/250 |
|
|
[考研] 291 求調(diào)劑 +4 | 化工2026屆畢業(yè)?/a> 2026-03-21 | 5/250 |
|
|
[考研] 北科281學(xué)碩材料求調(diào)劑 +8 | tcxiaoxx 2026-03-20 | 9/450 |
|
|
[考研]
求調(diào)劑材料學(xué)碩080500,總分289分
5+3
|
@taotao 2026-03-19 | 21/1050 |
|
|
[考研] 276求調(diào)劑 +3 | YNRYG 2026-03-21 | 4/200 |
|
|
[考研] 317求調(diào)劑 +12 | 申子申申 2026-03-19 | 18/900 |
|
|
[考研] 0854電子信息求調(diào)劑 +3 | α____ 2026-03-22 | 3/150 |
|
|
[考研] 311求調(diào)劑 +6 | 冬十三 2026-03-18 | 6/300 |
|
|
[考研] 307求調(diào)劑 +11 | 冷笙123 2026-03-17 | 11/550 |
|
|
[考研] 289材料與化工(085600)B區(qū)求調(diào)劑 +3 | 這么名字咋樣 2026-03-22 | 4/200 |
|
|
[考研] 一志愿華中農(nóng)業(yè)071010,總分320求調(diào)劑 +5 | 困困困困坤坤 2026-03-20 | 6/300 |
|
|
[考研] 306求調(diào)劑 +5 | 來好運(yùn)來來來 2026-03-22 | 5/250 |
|
|
[考研] 298求調(diào)劑一志愿211 +3 | 上岸6666@ 2026-03-20 | 3/150 |
|
|
[考研] 303求調(diào)劑 +5 | 安憶靈 2026-03-22 | 6/300 |
|
|
[考研]
|
Grand777 2026-03-21 | 3/150 |
|
|
[考研] 一志愿南大,0703化學(xué),分?jǐn)?shù)336,求調(diào)劑 +3 | 收到VS 2026-03-21 | 3/150 |
|
|
[考研] 材料 336 求調(diào)劑 +3 | An@. 2026-03-18 | 4/200 |
|
|
[考研] 中南大學(xué)化學(xué)學(xué)碩337求調(diào)劑 +3 | niko- 2026-03-19 | 6/300 |
|
|
[考博] 26博士申請(qǐng) +3 | 1042136743 2026-03-17 | 3/150 |
|