QML模型基礎架構
QML采用經典的Model-View-Delegate (MVD)?架構來分離數據與界面,這與MVC模式類似但更加適合聲明式UI開發。在這個架構中:
- ?Model?:負責管理數據,可以是簡單的整數,也可以是復雜的C++自定義模型
- ?View?:負責顯示數據,如ListView、GridView、TableView等
- ?Delegate?:負責如何顯示單個數據項,相當于模板
// 基本結構示例
ListView {width: 200; height: 250model: myModel // 數據模型delegate: Rectangle { // 委托項Text { text: model.display }}
}
Qt的模型系統最大的特點是靈活性——幾乎任何數據類型都可以作為模型使用。模型在QML中不僅是數據容器,還通過角色(roles)?系統提供數據訪問接口,委托可以通過這些角色訪問模型中的數據項。
整數模型:最簡單的模型類型
整數模型是QML中最簡單的模型形式,它僅提供一個整數計數,用于生成重復的UI元素。
import QtQuick 2.15
import QtQuick.Controls 2.15Rectangle {width: 600; height: 400ListView {anchors.fill: parentmodel: 5 // 整數作為模型delegate: Rectangle {width: parent.widthheight: 30required property int indexText {text: "我是第"+index + "個元素"}}}
}
?使用場景?:
- 創建固定數量的相似組件
- 快速原型開發
- 不需要復雜數據綁定的簡單界面
?特點?:
- 每個委托項自動獲得index屬性(從0開始)
- 沒有額外的數據角色,僅提供索引值
- 性能高效,適合靜態簡單布局
?高級用法?:
可以與Repeater結合使用,創建網格布局:
Grid {columns: 3Repeater {model: 9 // 創建3x3網格Rectangle {width: 50; height: 50color: index%2 ? "red" : "blue"}}
}
整數模型雖然簡單,但在許多不需要復雜數據的場景下非常實用,能夠避免創建不必要的復雜模型,注意:整數模型中的項目數量不能超過100000000
列表模型ListModel:動態數據管理
ListModel是QML中最常用的模型之一,它允許在QML中直接定義結構化數據,并支持動態修改。
基本用法
import QtQuick 2.0ListModel {id: fruitModelListElement {name: "Apple"cost: 2.45}ListElement {name: "Orange"cost: 3.25}
}
在視圖中使用:
ListView {anchors.fill: parentmodel: fruitModeldelegate: Row {Text { text: "名稱:" + name }Text { text: "價格:" + cost }}
}
?ListElement特性?:
- 必須以小寫字母開頭
- 值只能是簡單類型:字符串、布爾值、數字或枚舉值
- 支持嵌套ListElement,創建層次結構
動態操作
ListModel提供了一系列方法用于動態修改數據:
// 添加數據
fruitModel.append({"name": "Banana", "cost": 1.95})// 插入數據
fruitModel.insert(1, {"name": "Pear", "cost": 3.50})// 修改數據
fruitModel.set(0, {"name": "Pineapple", "cost": 2.75})// 移動數據
fruitModel.move(0, 2, 1) // 將第0項移動到第2項// 刪除數據
fruitModel.remove(1) // 刪除第1項// 清空模型
fruitModel.clear()
高級特性
?動態角色?:ListModel支持動態添加角色,只需設置dynamic屬性為true:
ListModel {id: dynamicModeldynamic: true// 可以后續添加新角色
}Component.onCompleted: {dynamicModel.append({"newRole": "value"})
}
?與WorkerScript配合?:對于大數據量操作,可以在后臺線程處理模型以避免界面卡頓:
WorkerScript {id: workersource: "script.mjs"
}Timer {id: timerinterval: 2000; repeat: truerunning: trueonTriggered: {var msg = {'action': 'appendCurrentTime', 'model': listModel};worker.sendMessage(msg);}
}
ListModel非常適合中等規模的數據集,它提供了QML中直接操作數據的便利性,但對于大規模數據或復雜數據結構,建議使用C++實現的模型。
對象模型ObjectModel:封裝可視化項
ObjectModel是一種特殊模型,它直接包含可視化項而不是數據,因此不需要委托(delegate)。
基本用法
import QtQuick 2.12
import QtQml.Models 2.12ObjectModel {id: itemModelRectangle { height: 30; width: 80; color: "red" }Rectangle { height: 30; width: 80; color: "green" }Rectangle { height: 30; width: 80; color: "blue" }
}ListView {anchors.fill: parentmodel: itemModel
}
?特點?:
- 模型項本身就是可視化組件
- 不需要定義delegate
- 適合固定且數量有限的可視化項集合
- 性能優于Repeater生成的相同數量項
動態操作
ObjectModel也支持動態修改內容:
// 添加項
itemModel.append(rectComponent.createObject())// 插入項
itemModel.insert(1, textComponent.createObject())// 移動項
itemModel.move(0, itemModel.count-1, 1)// 刪除項
itemModel.remove(0)// 獲取項
var item = itemModel.get(2)
與C++集成
ObjectModel也可以從C++端創建和管理:
class ObjectModel : public QObject {Q_OBJECT
public:Q_INVOKABLE QVariant _objmodel() {return QVariant::fromValue(objmodellist);}// 其他操作接口...
private:QList<QObject*> objmodellist;
};
在QML中使用:
ListView {delegate: Text { text: model.modelData.data }model: Global._objmodel()
}
ObjectModel非常適合混合布局場景,其中某些項需要特殊定制而不是統一的數據驅動委托。
表格模型TableModel:處理二維數據
TableModel用于處理表格數據,相比ListModel,它增加了列的概念,適合展示類似數據庫表格的結構化數據。
QML實現
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQml.Models 2.15TableView {anchors.fill: parentcolumnSpacing: 1rowSpacing: 1model: TableModel {TableModelColumn { display: "name" }TableModelColumn { display: "age" }rows: [{"name": "Alice","age": 22},{"name": "Bob","age": 25}]}delegate: Rectangle {Text { text: display }}
}
C++實現
對于更復雜的表格,通常需要在C++中實現:
class DataModel : public QAbstractTableModel {Q_OBJECT
public:int rowCount(const QModelIndex&) const override {return m_data.size();}int columnCount(const QModelIndex&) const override {return m_roleList.size();}QVariant data(const QModelIndex &index, int role) const override {return m_data[index.row()].at(index.column());}QHash<int, QByteArray> roleNames() const override {QHash<int, QByteArray> roles;for(int i=0; i<m_roleList.size(); i++) {roles[Qt::UserRole+i+1] = m_roleList.at(i).toLocal8Bit();}return roles;}
private:QList<QVariantList> m_data;QStringList m_roleList;
};
注冊到QML:
qmlRegisterType<DataModel>("CustomModels", 1, 0, "DataModel");
高級特性
?動態列管理?:可以通過動態添加TableModelColumn來創建靈活的表結構
?行選擇?:配合SelectionModel實現行選擇功能
?排序?:通過實現sort函數添加排序能力
?性能優化?:對于大型表格,需要實現fetchMore/canFetchMore進行分批加載
TableModel特別適合展示業務數據,如數據庫查詢結果、電子表格等結構化信息。
XML列表模型XmlListModel:處理XML數據 使用較少
XmlListModel專門用于解析和展示XML格式的數據,常見于RSS閱讀器或Web API交互場景。
基本用法
import QtQuick 2.0
import QtQuick.XmlListModel 2.0XmlListModel {id: xmlModelsource: "http://www.example.com/feed.xml"query: "/rss/channel/item"XmlRole { name: "title"; query: "title/string()" }XmlRole { name: "pubDate"; query: "pubDate/string()" }
}ListView {width: 180; height: 300model: xmlModeldelegate: Text { text: title + ": " + pubDate }
}
?關鍵屬性?:
- source:XML數據源,可以是本地文件或網絡URL
- query:XPath表達式,指定要處理的節點集
- XmlRole:定義如何從XML節點提取數據
- namespaceDeclarations:處理含命名空間的XML
動態查詢
XmlListModel支持動態修改查詢條件:
// 只獲取特定條件的項目
xmlModel.query = "/rss/channel/item[contains(title, 'Qt')]"// 重新加載模型
xmlModel.reload()
錯誤處理
通過status屬性處理加載狀態:
Text {text: {switch(xmlModel.status) {case XmlListModel.Loading: return "加載中...";case XmlListModel.Ready: return "就緒";case XmlListModel.Error: return "錯誤: "+xmlModel.errorString();}}
}
性能考慮
- 大型XML文件考慮在后臺線程解析
- 使用progress屬性顯示加載進度
- 合理設置XPath查詢,避免復雜查詢影響性能
XmlListModel極大簡化了XML數據處理流程,是構建RSS閱讀器或與Web服務交互的理想選擇。
Package機制:多視圖共享委托--使用較少
Package(包)機制允許多個視圖共享同一組委托實例,實現復雜的視圖交互效果。
基本概念
Package與DelegateModel配合使用,可以:
- 在不同視圖中復用同一委托
- 實現項目在不同視圖間動畫過渡
- 構建主從視圖(Master-Detail)布局
實現示例
import QtQuick 2.12
import QtQml.Models 2.12
import Qt.labs.qmlmodels 1.0DelegateModel {id: visualModelmodel: ListModel {ListElement { name: "Item 1"; color: "red" }ListElement { name: "Item 2"; color: "green" }}delegate: Package {Item { Package.name: "list" }Item { Package.name: "grid" }Rectangle {width: parent.width; height: 50color: model.colorText { text: model.name }parent: viewSwitcher.checked ? grid : list}}
}ListView {width: 200; height: 300model: visualModel.parts.list
}GridView {x: 210; width: 300; height: 300model: visualModel.parts.grid
}
Package機制為構建復雜視圖交互提供了強大支持,是QML模型系統中較高級但極具價值的功能。
模型性能優化與實踐建議
在實際項目中使用QML模型時,?性能和可維護性是需要重點考慮的因素。
性能優化技巧
?虛擬化?:ListView/GridView等視圖默認只創建可見項委托,確保clip屬性為true
?輕量委托?:保持委托盡可能簡單,復雜委托會顯著影響滾動性能
?批處理操作?:對于大規模數據修改,先調用beginResetModel(),修改完成后調用endResetModel()
?C++模型?:數據量超過1000項時考慮使用C++實現的模型
?緩存策略?:對于網絡數據或計算成本高的模型,實現緩存機制
模型選擇指南
模型類型 | 適用場景 | 優點 | 缺點 |
---|---|---|---|
整數模型 | 簡單重復項 | 極簡、高效 | 無數據綁定 |
ListModel | 中小型動態數據 | QML內定義、易用 | 大數據性能差 |
ObjectModel | 固定可視化項 | 無需委托、靈活 | 不適合數據驅動 |
TableModel | 表格數據 | 行列結構、功能豐富 | 復雜度高 |
XmlListModel | XML數據 | 內置解析、XPath支持 | 僅只讀 |
C++模型 | 大型專業應用 | 高性能、功能全面 | 需要C++知識 |
常見問題解決
?數據不同步?:確保模型變更時發出正確的信號(如dataChanged)
?內存泄漏?:ObjectModel中注意管理QObject生命周期
?跨線程訪問?:使用WorkerScript處理耗時操作
?復雜過濾?:考慮使用SortFilterProxyModel等中間模型
最佳實踐
?角色命名?:使用有意義的角色名而非簡單role1、role2
?模塊化?:將復雜模型分離到單獨QML文件或C++類中
?測試?:大數據量下測試滾動性能和內存占用
?文檔?:為自定義模型編寫使用說明,特別是角色定義
?漸進增強?:簡單需求先用ListModel,復雜時再遷移到C++模型
結語
QML的模型系統提供了從簡單到復雜、從純QML到C++集成的全方位解決方案。掌握這些模型類型的特點和使用場景,能夠幫助開發者構建數據驅動的高性能用戶界面。無論是簡單的整數模型還是復雜的C++集成模型,Qt都提供了相應的工具和模式,關鍵在于根據項目需求選擇合適的工具。
隨著Qt的持續發展,QML的模型系統也在不斷進化,如最新的Qt 6中引入的DelegateChooser等新特性,進一步增強了模型的靈活性。建議開發者定期查閱Qt官方文檔,了解最新的最佳實踐和功能增強。