模型/視圖架構基於MVC設計模式發展而來。MVC中,模型(Model)用來表示數據;視圖(View)是界面,用來顯示數據;控制(Controller)定義界面對用戶輸入的反應方式。數據庫
Qt中視圖和控制結合在一塊兒造成新的模型/視圖架構。這一樣將數據的存儲和數據向用戶展現進行了分離,但提供了更爲簡單的框架。數據和界面分離,使得相同的數據能夠在不一樣的視圖顯示,並且能夠擴展新的視圖,而不須要改變底層的數據框架。爲了靈活處理用戶輸入,引入了委託,也稱爲代理,使用它能夠定製數據的渲染和編輯方式。設計模式
模型與數據通訊,爲其它組件提供接口。視圖經過模型索引(Model Index)從模型中獲取數據,模型索引用來表示數據項。委託渲染數據項,編輯項目時,委託使用模型索引直接與模型通訊。數據結構
它們之間的關係爲:架構
(1).當數據源發生變化時,模型發出信號通知視圖。框架
(2).用戶對界面進行操做時,視圖發出信號提供交互信息。編輯器
(3).用戶編輯數據時,代理髮出信號告知模型和視圖編輯器的狀態。ide
模型用來提供數據,並提供了一個標準的接口供視圖和委託來訪問數據。這個接口由QAbstractItemModel類來定義,無論數據項是什麼結構,它都會以層次結構來表示數據,這個結構中包含了數據項表。視圖按照這種約定來訪問模型中的數據,但這不會影響數據的顯示,視圖可使用任何形式將數據顯示出來。當數據發生變化時,模型會經過信號和槽機制通知關聯的視圖。函數
(1) 模型索引 QModelIndex工具
模型索引使數據的表示與數據的獲取相分離,每一塊能夠獲取的數據都用一個模型索引來表示,視圖和委託使用模型索引來請求數據項並顯示。佈局
模型索引包含一個模型指針,指向建立它們的模型,使用多個模型時能夠避免出錯。
因爲模型中的數據在隨時變化,所以模型引隨時會變化,不須要也不該當保存一個模型索引,若是須要長時間引用一塊數據,則必須使用QPersistentModelIndex建立模型索引。
QAbstractItemModel類中提供了 index接口獲取模型索引,QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()),一般可將數據當作是一個表格結構,提供行和列的索引,對於樹形結構,則提供其父索引。
(2) 數據項 QStandardItem
模型中的數據項能夠做爲各類角色在其它組件中使用,容許爲其它組件提供不一樣類型的數據。經過爲每一個角色提供合適的數據,模型能夠告知視圖和委託如何顯示數據。
Qt::DisplayRole |
數據爲QString類型,渲染爲文本 |
Qt::DecorationRole |
數據被渲染爲圖標等裝飾,類型一般爲QColor,QIcon,QPixmap等 |
Qt::EditRole |
數據爲QString類型,可在編輯器中編輯 |
Qt::ToolTipRole |
數據爲QString類型,顯示在工具提示中 |
Qt::StatusTipRole |
數據爲QString類型,顯示在狀態欄 |
Qt::WhatThisRole |
數據爲QString類型,顯示在「What’s This?」模式下 |
Qt::SizeHintRole |
數據類型爲QSize,表示數據項的大小,將會應用到視圖 |
(3) QAbstractItemModel 提供了靈活的接口,能夠將數據表示爲列表、表格、數等形式。其子類 QAbstractListModel 和 QAbstractTableModel 爲列表和表格結構的數據提供了一些常見的默認實現。
Qt定義了許多默認的模型實現,在應用中能夠直接拿來使用。
QStandardItemModel:一個可被看成表模型、表格模型、數模型使用的通用模型,可用來管理複雜的樹型結構樹據,每個數據項均可以包含任意數據。但它有個缺點:加載大數據時較慢。
QStringListModel:用來存儲一個簡單的QString項目的列表模型。
QFileSystemModel:提供本地文件系統中的文件和目錄信息。
QSqlQueryModel:訪問數據庫。
(4) 自定義模型
當要爲一個已經存在的數據結構建立一個新的模型時,須要考慮使用哪一種類型的模型來爲數據提供接口,若是數據結構能夠表示爲項目列表或表格,則能夠考慮子類化QAbstractListModel 和 QAbstractTableModel來實現。
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; // 爲不一樣角色提供數據
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); // 設置對應模型索引下的數據
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; // 表頭數據
Qt::ItemFlags flags(const QModelIndex &index) const; // 返回模型索引是否可編輯,是否可用,編輯,選擇等等狀態
int rowCount(const QModelIndex &parent = QModelIndex()) const; // 當前父索引下的行數
int columnCount(const QModelIndex &parent = QModelIndex()) const; // 當前父索引下的列數
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); // 添加行 添加前須要調用 beginInsertRows,添加完成後調用 endInsertRows
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); // 刪除行 刪除前須要調用 beginRemoveRows,刪除完成後調用 endRemoveRows
視圖用來顯示數據。
視圖中的標準接口由 QAbstractItemView 類提供。
視圖一般管理從模型獲取的數據的總體佈局,它們能夠本身渲染獨立的數據項,也可使用委託來渲染和編輯。
除了呈現數據,視圖還處理項目間的導航以及項目選擇的某些方面,如設置選擇行爲 SelectionBehavior、選擇模式SelectionMode,上下文菜單和拖放等。
(1) QItemSelectionModel 選擇模型
這裏的選擇模型和前面的模型是不一樣的概念,在視圖中被選擇項目的信息都存儲在 QItemSelectionModel 實例中,這樣被選擇項目的模型索引便保持在一個獨立的模型中,與全部的視圖都是獨立的。在一個模型上設置多個視圖時就能夠實現多個視圖共享。QItemSelectionModel 對象能夠經過視圖的接口獲取和從新設置。
QItemSelectionModel *selectionModel() const;
void setSelectionModel(QItemSelectionModel *selectionModel);
QItemSelectionModel 對象會保存當前模型的指針,也能夠返回操做的模型索引列表。
QModelIndexList selectedIndexes() const;
(2) Qt提供了幾種經常使用的視圖,都是使用規範的格示來顯示數據,若是還要實現條形圖、餅狀圖或更復雜的圖形,就要從新實現視圖類。
QListView: 將數據顯示爲一個列表。
QTableView: 將模型中的數據顯示在一個表格中。
QTreeView: 將模型中的數據項顯示在具備層次的列表中。
QColumnView: 提供一個多級視圖(每點開一選項都會在它旁邊出現一個菜單)。
委託提供特殊顯示和編輯功能。委託的標準接口由 QAbstractItemDelegate 類提供。委託經過 paint() 和 sizeHint() 函數來使它們能夠渲染自身的內容。
委託的編輯功能能夠經過兩種方式來實現,一種是使用部件來管理編輯過程,另外一種是直接處理事件(經過子類化輸入控件完成)。
QItemDelegate 和QStyledItemDelegate 是委託Qt提供的兩種委託實現。
QStyledItemDelegate使用當前的樣式來繪製項目,當要自定義的委託要使用樣式表一塊兒應用時,建議使用它做爲基類。
QListView、QTableView和QTreeView都使用QItemDelegate來提供編輯功能,這使得它們只有普通風格的渲染。
視圖能夠獲取和設置委託。
void setItemDelegate(QAbstractItemDelegate *delegate);
QAbstractItemDelegate *itemDelegate() const;
(1) 自定義委託示例
當視圖須要編輯器時,它會告知委託爲被修改的項目提供一個編輯器部件,委託會調用 createEditor 函數提供一個合適的部件。
在自定義委託時,createEditor返回一個可編輯輸入的控件對象,若是不須要編輯,則返回 nullptr, 返回的指針對象不須要保存,由於視圖在不須要的時候會銷燬它。
setEditorData 函數將模型中的數據渲染到編輯器中。
setModelData 函數在用戶完成了輸入以後,將數據存儲到模型中。
updateEditorGeometry 函數用來調整編輯器的位置和大小,QStyleOptionViewItem對象提供了幾何佈局相關的信息。
代理對象會在完成編輯後發射 closeEditor 信號來告知視圖。
class SpinDelegate : public QItemDelegate
{
Q_OBJECT
public:
using QItemDelegate::QItemDelegate;
// 建立編輯器
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
// 爲編輯器設置數據
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
// 將數據寫入到模型
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
// 更新編輯器的幾何佈局
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
QWidget* SpinDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QSpinBox* editor = new QSpinBox{parent};
editor->setRange(0, 40);
return editor;
}
void SpinDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
if (spinBox)
{
bool isOK = false;
int value = index.model()->data(index, Qt::EditRole).toInt(&isOK);
if (isOK)
{
spinBox->setValue(value);
}
}
}
void SpinDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
if (spinBox)
{
spinBox->interpretText(); // 確保取得最新的數值
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
}
void SpinDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
editor->setGeometry(option.rect);
}