Qt包含一組使用模型/視圖結構的類,能夠用來管理數據並呈現給用戶。這種體系結構引入的分離使開發人員更靈活地定製項目,而且提供了一個標準模型的接口,以容許普遍範圍的數據源被使用到到現有的視圖中。
模型 - 視圖 - 控制器(MVC)是一種設計模式,由三類對象組成:
模型:應用程序對象。
視圖:屏幕演示。
控制器:定義了用戶界面響應用戶輸入的方式。
在引入MVC以前,用戶界面的設計每每是將這些對象組合在一塊兒。MVC的解耦帶來了靈活性和重用性。
若是視圖和控制器對象相結合,其結果是模型/視圖結構,仍然分離了數據與呈現給用戶的方式,但提供了基於相同原理的簡單框架。這種分離使得它能夠在幾個不一樣的視圖中顯示相同的數據,而且實現新類型的視圖,而無需改變底層的數據結構。爲了靈活地處理用戶輸入,則引入了委託的概念。在此框架引入委託的優勢是:它容許項目數據顯示和自定義編輯。
模型/視圖結構
模型與數據源進行通訊,在這個體系結構中爲其它組件提供了一個接口。通訊的性質依賴於數據源的類型以及模型的實現方式。
視圖從模型中獲得模型索引,這些都引用到數據項。經過爲模型提供模型索引,視圖能夠從數據源中檢索數據項。
在標準的視圖裏,委託呈現數據項目。當一個項目被編輯,委託與模型直接利用模型索引進行通訊。
模型/視圖/委託通訊
模型、視圖、委託使用信號和槽相互通訊:
模型的信號:通知視圖關於改變由數據源保持的數據。
視圖的信號:提供了關於用戶交互顯示的項目信息。
委託的信號:當編輯時告訴模型和視圖編輯器的狀態。
模型
全部的模型都基於QAbstractItemModel類。這個類定義了一個使用視圖和委託來訪問數據的接口。數據自己不是必需要存儲在模型中,能夠在一個數據結構或一個單獨的類、文件、數據庫、或其它一些應用組件。
QAbstractItemModel爲數據提供了一個接口,它足夠的靈活性來處理表格、列表、樹形式的數據視圖。然而,實現新的列表和相似於表的數據結構模型時,QAbstractListModel和QAbstractTableModel類是更好的起點,由於它們提供了適當的經常使用的功能的默認實現。這些類能夠派生子類,用來提供支持特定種類的列表和表格的模型。
Qt提供了一些現成的模型,能夠用來處理數據項:
QStringListModel:用於存儲簡單的QString的列表項。
QStandardItemModel:管理更復雜的樹結構件,其中每個項目能夠包含任意數據。
QFileSystemModel:提供有關本地文件系統的文件和目錄信息。
QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel:使用模型/視圖約定來訪問數據庫。
若是這些標準模型不能知足要求,則能夠繼承化QAbstractItemModel、QAbstractListModel或QAbstractTableModel來建立自定義模型。
視圖
提供了完整的實現爲各類不一樣的視圖:而QListView顯示項目列表,QTableView中從一個表模型中顯示數據,QTreeView則顯示了層次列表的數據模型項目。每一個類是基於QAbstractItemView抽象基類。雖然這些類是準備使用的實現,他們也能夠被子類化,以提供自定義的視圖。
委託
QAbstractItemDelegate在模型/視圖框架中表明抽象的基類。默認的委託實現由QStyledItemDelegate提供,這被Qt的標準視圖用做默認的委託。然而,QStyledItemDelegate和QItemDelegate獨立替代繪畫,且爲視圖項提供編輯器。它們之間的區別在於QStyledItemDelegate使用當前樣式來繪製項目。所以,建議實現自定義委託或當與Qt樣式表一塊兒使用時,使用QStyledItemDelegate做爲基類。
排序
在模型/視圖結構中有兩種接近的排序方式,選擇哪一種方式取決於你的基礎模型。
若是你的模型是可排序的,也就是說,若是從新實現了QAbstractItemModel::sort()方法,QTableView和QTreeView都提供了一個API,容許以編程方式排序來排序模型數據。此外,能夠啓用交互式排序(即容許用戶將數據經過單擊視圖的標題進行排序),由QHeaderView::sortIndicatorChanged()信號分別鏈接到QTableView:: sortByColumn()槽或QTreeView::sortByColumn()槽。
另外一種方法,若是模型沒有所需的接口,或者若是想使用一個列表視圖來顯示數據,使用代理模型呈現數據視圖以前應轉換模型的結構。
方便的類
一些便利類都源於標準視圖類的依賴Qt的項目爲基礎的項目視圖和表類應用的好處。他們不打算被繼承。
這些類的實例包括QListWidget,QTreeWidget和QTableWidget。
這些類比視圖類靈活性差,且不能與任意的模型使用。建議使用模型/視圖的方法來處理在項目視圖中的數據,除非強烈須要一個基於項目的類。
若是想利用模型/視圖提供的特性方法,同時使用一個基於項目的接口,能夠考慮使用視圖類,例如:QListView、QTableView、QTreeView與QStandardItemModel。
使用視圖與現有的模型
QListView和QTreeView類是最合適的視圖來使用QFileSystemModel。下面介紹的示例在樹視圖中顯示一個目錄,旁邊列表視圖中顯示相同的信息。該視圖共享用戶的選擇,這樣選擇的項目在兩個視圖中高亮顯示。
代碼以下:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSplitter *splitter = new QSplitter;
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());
QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);
tree->setRootIndex(model->index(QDir::currentPath()));
QListView *list = new QListView(splitter);
list->setModel(model);
list->setRootIndex(model->index(QDir::currentPath()));
splitter->setWindowTitle("Two views onto the same file system model");
splitter->show();
return app.exec();
}
上面的例子中,咱們忽略說起如何處理選擇的項目。下面將詳細講述在視圖中處理所選的項目。
基本概念
在模型/視圖結構中,模型提供了視圖與委託訪問數據的標準接口。在Qt中,標準的接口由QAbstractItemModel類定義。不管多麼數據項被存儲在任何底層的數據結構中,QAbstractItemModel的全部子類所表明的數據做爲包含視圖項的分層結構。視圖使用這個約定來訪問模型中的數據項,但並不限制將該信息傳達給用戶的方式。
Model indexes
爲確保數據被分開被訪問,模型索引的概念被引入。能夠經過模型索引來得到每條信息。視圖與委託使用這些索引來請求顯示的數據項。
所以,模型只須要知道如何獲取數據,並經過模型管理的數據的類型能夠被至關廣泛定義。型號索引包含一個指向建立它們的模型的指針,在處理多個模型時能夠防止混亂。
QAbstractItemModel *model = index.model();
模型索引提供臨時參考信息,而且能夠用於經過模型來檢索或修改數據。因爲模型可能重組其內部結構,模型的索引可能會變得無效,不宜存儲。若是須要長期參考一條信息,必須建立一個持久性模型索引。這爲模型保持最新信息提供了一個參考。臨時模型索引由QModelIndex類提供,持久性模型索引由QPersistentModelIndex類提供。
取得對應於數據項的模型索引,模型中必須制定三個屬性:一個行號、一個列號,以及父項的模型索引。
行和列
在最基本的形式中,模型能夠被一個簡單的表訪問,表項位於行號和列號,這並不意味着底層數據存儲在數據結構中,使用行號和列號只是一個慣例,以容許組件相互通訊。咱們能夠經過指定行號和列號的模型索引有關的任何特定信息,經過下面的方式獲得項目的索引:
QModelIndex index = model->index(row, column, ...);
模型提供的接口簡單,單級的數據結構如列表和表格不須要提供任何其餘信息。可是,正如上面的代碼所示,當得到一個模型索引時,咱們須要提供更多的信息。
行和列
圖中顯示了一個基本的表模型,其中的每一個項目的位置由一對行號和列號表示。咱們經過模型索引(一個項目數據)行號和列號來獲取。
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
父節點
由模型提供的相似於表的接口給數據項是理想的當在表格或列表視圖中使用數據,行號和列號準確地映射到視圖顯示項目的方式。然而,結構,如樹視圖須要更模型爲項目暴露一個更靈活的接口。所以,每一個項目也能夠是另外一個表的父項,大體相同的方式,在一個樹視圖中的頂級項目能夠包含另外一個列表項。
當請求的一個模型項的索引時,必須提供有關該項目的父項目的一些信息。在模型外,指定一個項目的惟一途徑是經過一個模型索引,因此父模型索引也必須以下給出:
QModelIndex index = model->index(row, column, parent);
父項,行和列
該圖顯示了一個樹模型,其中每一個項目都依賴於由一個父項,一個行號和一個列號。
項目的「A」和「C」表示模型頂層的兄弟姐妹:
項目「A」有不少孩子,能夠經過以下方式由「A」索引獲得「B」索引:
QModelIndex indexB = model->index(1, 0, indexA);
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
項目角色
模型中的項目能夠爲其餘組件演繹不一樣的角色,容許爲不一樣的狀況提供不一樣類型的數據。例如,Qt::DisplayRole能夠在用於訪問視圖中被顯示爲文本的字符串。一般狀況下,包含數據的項目用於若干不一樣的角色,且標準角色被Qt::ItemDataRole定義。
咱們能夠經過模型索引傳遞給相應的項目向模型請求項目數據,並經過指定一個角色來獲取想要的數據類型,以下:
QVariant value = model->data(index, role);
數據類型被稱爲模型的角色指示器。視圖能夠以不一樣的方式顯示角色,所以,爲每一個角色提供相應的信息很是重要。
項目數據最多見的用途是覆蓋在Qt::ItemDataRole中定義的標準角色。經過爲每一個角色提供相應的項目數據,模型能夠爲視圖和委託提供有關項目應如何呈現給用戶的指示,不一樣的視圖能夠根據須要來解釋或忽略此信息。此外,也能夠爲應用程序的特定目的而定義附加的角色。
使用模型索引
爲了演示如何將數據從一個模型中進行檢索,使用模型索引,咱們建立了一個QFileSystemModel,在窗體上沒有視圖以及顯示文件和目錄的名稱。雖然這並非使用模型的正常方式,它代表模型在處理模型索引上的約定。
咱們用如下述方式構建了一個文件系統模型:
QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);
在這種狀況下,咱們設置了一個默認QFileSystemModel,由該模型使用index()的特定實現來獲取父索引,使用rowCount()函數來計算行號。
爲簡單起見,咱們只關心模型中的第一列中的項目。咱們檢查每一行,依次獲取每一行中的第一個項目的模型索引,以及讀出所存儲在該模型項目中的數據。
for (int row = 0; row <<span style=" color:#c0c0c0;"> numRows; ++row) {
QModelIndex index = model->index(row, 0, parentIndex);
爲了得到一個模型索引,咱們指定了行號、列號(第一列爲零),以及全部咱們想要的項目的父項目的模型索引。每一個項目中的文本檢索可使用模型的data()函數來獲取。咱們指定了模型索引和Qt::DisplayRole來獲取數據項中的字符串。
QString text = model->data(index, Qt::DisplayRole).toString();
// Display the text in a widget.
}
使用模型
咱們建立一個字符串列表模型做爲例子,設置一些數據,並構造一個視圖來顯示模型的內容。這均可以在一個單一的函數執行:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStringList numbers;
numbers << "One" << "Two" << "Three" << "Four" << "Five";
請注意,QStringListModel被聲明爲一個QAbstractItemModel。這使咱們可以爲模型使用抽象接口,並確保代碼仍然有效,即便咱們使用不一樣的模型替換了字符串列表模型。
由而QListView提供的列表視圖足以展現字符串列表模型中的項目。咱們構建視圖,並設置模型可使用下面代碼:
QAbstractItemModel *model = new QStringListModel(numbers);
QListView *view = new QListView;
view->setModel(model);
視圖正常顯示方式
view->show();
return app.exec();
}
視圖展示模型的內容,經過模型的接口訪問數據。當用戶試圖編輯一個項目時,視圖使用缺省表明提供一個編輯器部件。
上面的圖顯示QListView如何使用字符串列表模型表示數據。因爲模型可編輯,視圖會自動容許列表中的每一個項目使用默認的委託進行編輯。
一個模型的多個視圖
一個模型能夠爲多個視圖所使用。在下面的代碼中,咱們建立兩個表視圖,使用的均是建立好的同一個模型。
QTableView *firstTableView = new QTableView;
QTableView *secondTableView = new QTableView;
firstTableView->setModel(model);
secondTableView->setModel(model);
在模型/視圖框架中使用信號和槽是指更改模型能夠傳遞給全部相連的視圖,以確保始終能夠訪問相同的數據,而無論所使用的視圖。
上面的圖顯示了統一模型的兩種不一樣的視圖,每一個都包含了一些選定的項目。儘管模型中的數據在視圖顯示一致,每一個視圖維護它本身的內部選擇模型。這在某些狀況下有用,但對於許多應用來講,則須要一個共享的選擇模型。
視圖共享選擇
雖然視圖類提供本身的默認選擇模型很方便,但當咱們使用多個視圖到同一個模型時,一般須要全部的模型數據和用戶的選擇在全部視圖顯示一致。因爲視圖類容許其內部選擇模型進行更換,那麼可使用以下方式實現視圖之間的統一:
secondTableView->setSelectionModel(firstTableView->selectionModel());
第二個視圖給出了第一個視圖的選擇模型。這兩種視圖如今在同一個選擇模型進行操做,保持了數據和所選項目的同步。數據庫