Model/View框架的核心思想是模型(數據)與視圖(顯示)相分離,模型對外提供標準接口存取數據,不關心數據如何顯示,視圖自定義數據的顯示方式,不關心數據如何組織存儲。數據庫
Model/View框架中數據與顯示的分離,能夠容許使用不一樣界面顯示同一數據,也可以在不改變數據的狀況下添加新的顯示界面。爲了處理用戶輸入,引入了委託(delegate)。引入委託的好處是能夠自定義數據項的渲染和編輯。架構
模型必須爲每個數據提供獨一無二的索引,視圖經過索引訪問模型中的數據。app
模型與數據源進行交互,爲框架中其它組件提供接口。交互的本質在於數據源的類型以及模型的實現方式。視圖從模型獲取模型索引,經過將模型索引反向傳給模型,視圖又能夠從數據源獲取數據。在標準視圖中,委託渲染數據項;在須要編輯數據時,委託使用直接模型索引直接與模型進行交互。框架
Model/View架構分爲三部分:模型、視圖和委託。每個組件都由一個抽象類定義,抽象類提供了基本的公共接口以及一些默認實現。編輯器
模型、視圖和委託使用信號槽進行交互:ide
A、底層維護的數據發生改變時,模型發出信號通知視圖函數
B、當用戶與視圖進行交互時,視圖發出信號提供了有關用戶與界面進行交互的信息工具
C、當用戶編輯數據項時,委託發出信號用於告知模型和視圖編輯器的狀態。佈局
全部的模型都是QAbstractItemModel的子類。QAbstractItemModel類定義了供視圖和委託訪問數據的接口。模型並不必定存儲數據自己。QAbstractItemModel提供的接口足夠靈活,足以應付以表格、列表和樹的形式顯示的數據。若是要爲列表或者表格設計自定義的模型,直接繼承QAbstractListModel和QAbstractTableModel類會更好,由於這兩個類已經實現了不少通用函數。性能
QT內置了多種標準模型:
QStringListModel:存儲簡單的字符串列表
QStandardItemModel:能夠用於樹結構的存儲,提供了層次數據
QFileSystemModel:本地系統的文件和目錄信息
QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel:存取數據庫數據
QT提供了多個預約義好的視圖類:
QListView:用於顯示列表
QTableView:用於顯示錶格
QTreeView:用於顯示層次數據
QAbstractItemView
QAbstractItemDelegate 則是全部委託的抽象基類
自 Qt 4.4 以來,默認的委託實現是 QStyledItemDelegate。可是,QStyledItemDelegate 和QItemDelegate 均可以做爲視圖的編輯器,兩者的區別在於,QStyledItemDelegate 使用當前樣式進行繪製。在實現自定義委託時,推薦使用QStyledItemDelegate 做爲基類,或者結合 Qt style sheets。
Model/View框架中,Model提供一種標準接口,供視圖和委託訪問數據。 QT中,Model接口由QAbstractItemModel類進行定義。無論底層數據是如何存儲的,只要是QAbstractItemModel的子類,都提供一種表格形式的層次結構。視圖利用統一的轉換來訪問模型中的數據。模型內部數據的組織方式並不必定和視圖中數據的顯示相同。
List Model雖然是線性的列表,有一個 Root Item(根節點),線性的一個個數據能夠看做是一個只有一列的表格,可是它是有層次的,由於有一個根節點。Table Model就比較容易理解,只是也存在一個根節點。Tree Model主要面向層次數據,而每一層次均可以都不少列,所以也是一個帶有層次的表格。
爲了使數據的顯示同存儲分離,引入了模型索引(model index)的概念。經過模型索引,能夠訪問模型的特定元素的特定部分。視圖和委託使用模型索引來請求所須要的數據。由此能夠看出,只有模型本身須要知道如何得到數據,模型所管理的數據類型可使用通用的方式進行定義。模型索引保存有建立的它的那個模型的指針,使同時操做多個模型成爲可能。
模型索引提供了所須要的信息的臨時索引,用於經過模型取回或者修改數據。因爲模型隨時可能從新組織其內部的結構,所以模型索引極可能變成不可用的,此時,就不該該保存這些數據。若是你須要長期有效的數據片斷,必須建立持久索引。持久索引保證其引用的數據及時更新。臨時索引(也就是一般使用的索引)由QModelIndex類提供,持久索引則是 QPersistentModelIndex 類。
爲了定位模型中的數據,須要三個屬性:行號、列號以及父索引。
在類表格的視圖中,好比列表和表格,行號和列號足以定位一個數據項。但對於樹型結構,因爲樹型結構是一個層次結構,而層次結構中每個節點都有多是一個表格。因此,每個項須要指明其父節點。因爲在模型外部只能用過索引訪問內部數據,所以,index()函數還須要一個 parent 參數:
QModelIndex index = model->index(row, column, parent);
模型能夠針對不一樣的組件(或者組件的不一樣部分,好比按鈕的提示以及顯示的文本等)提供不一樣的數據。例如,Qt::DisplayRole用於視圖的文本顯示。一般來講,模型中的數據項包含一系列不一樣的數據角色,數據角色定義在 Qt::ItemDataRole 枚舉中。
Qt::DisplayRole:文本表格中要渲染的關鍵數據
Qt::EditRole:編輯器中正在編輯的數據
Qt::ToolTipRole:數據項的工具提示的顯示數據
Qt::WhatsThisRole:項爲"What's This?"模式顯示的數據
經過爲每個角色提供恰當的數據,模型能夠告訴視圖和委託如何向用戶顯示內容。不一樣類型的視圖能夠選擇忽略本身不須要的數據,也能夠添加所須要的額外數據。
QStringListModel是一個可編輯的模型,能夠爲組件提供一系列字符串做爲數據,是封裝了QStringList的model。QStringListModel一般做爲只有一列的視圖組件的model,如QListView和QComboBox。
QStringListModel使用實例以下:
#include <QApplication> #include <QStringListModel> #include <QListView> #include <QStringList> int main(int argc, char *argv[]) { QApplication a(argc, argv); QStringListModel* listModel = new QStringListModel; QListView* view = new QListView; QStringList nameList; nameList<<"ZheDong Mao"<<"RenLai Zhou"<<"XiaoPing Deng"<<"ShaoQi Liu"<<"De Zhu"<<"DeHuai Peng"; listModel->setStringList(nameList); view->setModel(listModel); view->setEditTriggers(QAbstractItemView::AnyKeyPressed | QAbstractItemView::DoubleClicked); view->setWindowTitle("QStringListModel"); view->show(); return a.exec(); }
QFileSystemModel是QT標準的文件系統的model。
QFileSystemModel使用實例:
#include <QApplication> #include <QFileSystemModel> #include <QTreeView> #include <QListView> int main(int argc,char* argv[]) { QApplication app(argc,argv); //建立文件系統模型 QFileSystemModel model; //指定根目錄 model.setRootPath(QDir::currentPath()); //建立樹形視圖 QTreeView tree; //爲視圖指定模型 tree.setModel(&model); //指定根索引 tree.setRootIndex(model.index(QDir::currentPath())); //建立列表視圖 QListView list; list.setModel(&model); list.setRootIndex(model.index(QDir::currentPath())); tree.show(); list.show(); return app.exec(); }
QSortFilterProxyModel類提供在其餘的model和view之間排序和過濾數據的支持。QSortFilterProxyModel不能單獨使用,是一個代理,真正的數據須要另外的模型提供。QSortFilterProxyModel的做用是對數據進行排序和過濾。
QSortFilterProxyModel使用實例:
#include <QApplication> #include <QFileSystemModel> #include <QSortFilterProxyModel> #include <QTreeView> #include <QListView> int main(int argc,char* argv[]) { QApplication app(argc,argv); //建立文件系統模型 QFileSystemModel model; //指定根目錄 model.setRootPath(QDir::currentPath()); //建立樹形視圖 QSortFilterProxyModel proxymodel; proxymodel.setFilterKeyColumn(0); proxymodel.setSourceModel(&model); QTreeView tree; //爲視圖指定模型 tree.setModel(&proxymodel); //指定根索引 tree.setRootIndex(proxymodel.index(0, 0, QModelIndex())); //建立列表視圖 QListView list; list.setModel(&proxymodel); list.setRootIndex(proxymodel.index(0, 0, QModelIndex())); tree.show(); list.show(); return app.exec(); }
Model/View 架構中,視圖是數據從模型到最終用戶的途徑。數據經過視圖向用戶進行顯示,但一般數據的顯示同底層數據的存儲是徹底不一樣的。
QAbstractItemModel提供標準的模型接口,使用QAbstractItemView提供標準的視圖接口,能夠將數據同顯示層分離,在視圖中利用前面所說的模型索引。視圖管理來自模型的數據的佈局:既能夠直接渲染數據自己,也能夠經過委託渲染和編輯數據。
視圖不只用於顯示數據,還用於在數據項之間的導航以及數據項的選擇。另外,視圖也須要支持不少基本的用戶界面的特性,例如右鍵菜單以及拖放。視圖能夠提供數據編輯功能,也能夠將編輯功能交由某個委託完成。視圖能夠脫離模型建立,可是在其進行顯示以前,必須存在一個模型。對於用戶的選擇,多個視圖能夠相互獨立,也能夠進行共享。
QT內置了QListView、QTreeView、QTableView視圖類,QListView把model中的數據項以一個簡單的列表的形式顯示,或是以經典的圖標視圖的形式顯示。QTreeView把model中的數據項做爲具備層次結構的列表的形式顯示,容許以緊湊的深度嵌套的結構進行顯示。QTableView把model中的數據項以表格的形式展示。
委託就是供視圖實現某種高級的編輯功能。Model/View 沒有將用戶交互部分徹底分離。通常地,視圖將數據向用戶進行展現而且處理通用的輸入。可是,對於某些特殊要求(好比這裏的要求必須輸入數字),則交予委託完成。這些組件提供輸入功能,同時也能渲染某些特殊數據項。委託的接口由 QAbstractItemDelegate定義。
QAbstractItemDelegate 經過paint()和sizeHint()兩個函數渲染用戶內容(必須本身將渲染器繪製出來)。從QT4.4開始,QT提供了基於組件的子類:QItemDelegate和QStyledItemDelegate。默認的委託是QStyledItemDelegate。QItemDelegate與QStyledItemDelegate的區別在於繪製和向視圖提供編輯器的方式。 QStyledItemDelegate使用當前樣式繪製,而且可以使用Qt Style Sheet,在自定義委託時推薦使用QStyledItemDelegate做爲基類。
繼承自QStyledItemDelegate或QItemDelegate的自定義委託類須要重寫實現如下幾個函數:
[virtual] QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
建立編輯器,做爲用戶編輯數據時所使用的編輯器,從模型中接受數據,返回用戶修改的數據。
[virtual] void setEditorData(QWidget *editor, const QModelIndex &index) const
設置編輯器的數據
[virtual] void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
將數據寫入model
[virtual] void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
更新編輯器佈局
選擇是視圖中經常使用的操做。在列表、樹或者表格中,經過鼠標點擊能夠選中某一項,選中項會變成高亮或者反色。在 QT中,選擇也是使用了一種模型。在 Model/View架構中,選擇模型提供了一種更通用的對選擇操做的描述。對於通常應用而言,QT內置的選擇模型已經足夠,但QT也容許建立本身的選擇模型,來實現一些特殊的操做。
QT使用QItemSelectionModel類獲取視圖中數據項的選擇狀況。QItemSelectionModel能夠保持對視圖中選中數據項的跟蹤。視圖中被選擇的數據項的信息存儲在一個QItemSelectionModel實例中。選中的數據項的模型索引與全部的視圖都是獨立的。一個模型設置多個視圖時,能夠實如今多個視圖之間共享同一個選擇模型,達到同步操做的目的。視圖中總有一個當前數據項和一個選中的數據項,他們能夠是同一個數據項。
選擇由選擇區域組成。模型只將選擇區的開始和結束的索引位置記錄下來,以保證對大的選區也有很好的性能。非連續選區則由多個連續選擇組成。
標準視圖類(QListView、QTreeView、QTableView)提供了默認的選擇模型,足以知足大多數應用程序的需求。一個視圖的選擇模型能夠經過 selectionModel()函數獲取,而後使用setSelectionModel()提供給其它視圖共享,所以,通常沒有必要新建選擇模型。
若是須要建立一個選擇區,須要指定一個模型以及一對索引,使用這些數據建立一個QItemSelection對象。索引指向給定的模型中的數據,而且做爲一個塊狀選擇區的左上角和右下角的索引。爲了將選擇區應用到模型上,須要將選擇區提交到選擇模型。這種操做有多種實現,對於現有選擇模型有着不一樣的影響。
A、構建標準數據項模型並設置設置數據
//構建7行4列的項目模型
QStandardItemModel *model = new QStandardItemModel(7,4,this);
for (int row=0; row < 7; row++)
{
for (int col=0; col < 4; col++)
{
QStandardItem* item = new QStandardItem(QString("%1").arg(row*4+col));
//爲項目模型添加項目
model->setItem(row,col,item);
}
}
B、添加視圖,設置模型
//添加視圖,並將視圖放在窗體中央
QTableview *view = new QTableView;
view->setModel(model);//爲視圖添加模型
setCentralWidget(view);
C、獲取視圖的數據項選擇模型
QItemSelectionModel * selectionModel = view->selectionModel();
D、設置被選擇的數據項
//定義左上,右下的索引,而後使用這兩個索引建立選擇
QModelIndex topLeft;
QModelIndex rightBottom;
QModelIndex parentIndex = QModelIndex();
topLeft = model->index(1,1,parentIndex);
rightBottom = model->index(5,2,parentIndex);
//設置被選擇的數據項
QItemSelection selection(topLeft, rightBottom);
E、設置選擇模式
selectionModel->select(selection,QItemSelectionModel::Select);
QItemSelectionModel::Select:選擇全部指定的選區
QItemSelectionModel::Toggle:反向操做
QItemSelectionModel::Deselect:取消指定的已選選區
QItemSelectionModel::Rows:選擇行
QItemSelectionModel::Columns:選擇列
QItemSelectionModel::Current:將當前選區替換爲新的選區
QItemSelectionModel::Clear:取消所有已有選區
F、選擇模型的選擇改變信號
[signal] void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
選擇發生改變時,觸發信號。會發送被選擇的QItemSelection和未被選擇的QItemSelection
[signal] void QItemSelectionModel::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
當前數據項變化時,觸發信號。會發送當前選擇的QModelIndex和前一個QModelIndex。
G、選擇改變信號的槽函數
void updateSelection(QItemSelection &selected,QItemSelection &deselected);
選擇模型改變後的槽函數
void updateSelection(QItemSelection &selected, QItemSelection &deselected)
{
QModelIndex index;
QModelIndexList indexList = selected.indexes();
foreach (index, indexList)
{
QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
view->model()->setData(index,text);
}
indexList = deselected.indexes();
//清空上一次選擇的項目內容
foreach (index, indexList)
{
view->model()->setData(index,"");
}
}
void changeCurrent(const QModelIndex ¤t,const QModelIndex &previous);
顯示當前選擇項的數據改變狀況
void changeCurrent(const QModelIndex ¤t, const QModelIndex &previous)
{
qDebug() << QString("move(%1,%2)to(%3,%4)")
.arg(previous.row()).arg(previous.column())
.arg(current.row()).arg(current.column());
}
H、信號與槽函數的鏈接
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this,SLOT(updateSelection(QItemSelection&,QItemSelection&)));
connect(selectionModel,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this,SLOT(changeCurrent(QModelIndex,QModelIndex)));
I、視圖間共享選擇模型
QTableView *view2 = new QTableView;
view2->setWindowTitle("TableView2");
view2->setModel(model);//設置模型
view2->setSelectionModel(selectionModel);//設置共同的選擇模型
view2->show();