首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

C++ 實現 QML TreeView Model 實戰(一):基礎二維表模型與 Role 機制

     編輯:Java 綜合教學

C++ 實現 QML TreeView Model 實戰(一):從 ListModel 到 QAbstractItemModel

QML 的視圖組件(ListView、TableView、GridView)默認使用 ListModel 作為數據源,但這無法訪問本地文件系統或連接傳統 SQL 數據庫。工業級開發中,通常採用 C++ 實現數據模型,QML 負責視圖展示 的架構。本文系列將詳細講解如何基於 QAbstractItemModel 實現高性能的 TreeView 模型。

一、核心基類:QAbstractItemModel 的五大虛函數

所有高級模型類(QAbstractTableModel、QSqlTableModel 等)均繼承自 QAbstractItemModel。要實例化自定義模型,必須繼承並實現以下 5 個純虛函數:

函數作用
rowCount(parent)返回指定父節點下的行數
columnCount(parent)返回列數(QML 通常固定返回 1)
index(row, column, parent)創建並返回模型索引,攜帶內部指針
parent(child)返回子節點的父節點索引
data(index, role)根據角色返回數據

二、QML 的 Role 機制:告別列訪問

與 Widget 不同,QML 模型不通過列訪問數據,而是通過 Role(角色)。視圖組件調用模型的 roleNames() 獲取可用角色映射,再以角色名綁定數據。

TableView 中的 Role 綁定示例

TableView {
    TableViewColumn { role: "tagging"; title: ""; width: 50 }
    TableViewColumn { role: "name";    title: "操作"; width: 80 }
}

必須重寫 roleNames()

QHash<int, QByteArray> roleNames() const override;

三、完整模型類定義:SqlMenuEntry

class SqlMenuEntry : public QAbstractItemModel, public QQmlParserStatus {
    Q_OBJECT
public:
    enum MenuEntryRoles {
        idRole = Qt::UserRole + 1,
        nameRole,
        defaultEntryRole,
        customEntryRole,
        iconRole,
        iconHoverRole
    };
    // ... 五大虛函數聲明 ...
    QHash<int, QByteArray> roleNames() const override;
private:
    QHash<int, QByteArray> mRoleNames;
    QList<QHash<int, QVariant>> mRecords; // 二維數據存儲
};

四、關鍵函數實現詳解

1. roleNames() 返回角色映射

QHash<int, QByteArray> SqlMenuEntry::roleNames() const {
    return mRoleNames;
}

2. 構造函數初始化角色名

SqlMenuEntry::SqlMenuEntry(QObject *parent)
    : QAbstractItemModel(parent) {
    mRoleNames[nameRole]       = "name";
    mRoleNames[idRole]         = "menuid";
    mRoleNames[iconRole]       = "icon";
    mRoleNames[defaultEntryRole] = "default";
    mRoleNames[iconHoverRole]  = "iconHover";
}

3. 二維表模型的核心邏輯實現

int SqlMenuEntry::rowCount(const QModelIndex &parent) const {
    return parent.isValid() ? 0 : mRecords.size(); // 扁平列表無子節點
}
int SqlMenuEntry::columnCount(const QModelIndex &) const { return 1; }

QModelIndex SqlMenuEntry::index(int row, int column, const QModelIndex &parent) const {
    if (!parent.isValid() && row >= 0 && row < mRecords.size())
        return createIndex(row, column, mRecords.at(row)); // 保存節點指針
    return QModelIndex();
}
QModelIndex SqlMenuEntry::parent(const QModelIndex &) const { return QModelIndex(); } // 無父節點

QVariant SqlMenuEntry::data(const QModelIndex &index, int role) const {
    if (index.isValid())
        return mRecords[index.row()][role];
    return QVariant();
}

五、數據填充與 QML 註冊


// 添加數據
QHash<int, QVariant> row;
row[nameRole] = "name1";
row[iconRole] = "icon1";
mRecords.append(row);

// 註冊到 QML 類型系統
qmlRegisterType<SqlMenuEntry>("com.limutech.tv", 1, 0, "MenuEntryModel");

QML 中使用


import com.limutech.tv 1.0
MenuEntryModel { id: menuEntryModel }
ListView { model: menuEntryModel; delegate: Item { Text { text: name } } }

六、視圖獲取數據的完整流程

  1. 調用 rowCount() 獲取根節點行數
  2. 調用 columnCount() 獲取列數
  3. 遍歷調用 index() 獲取每行的 ModelIndex(內部指針已存入)
  4. 以 ModelIndex 為 parent 遞歸獲取子節點行/列數
  5. 調用 roleNames() 獲取角色列表
  6. 調用 data(index, role) 獲取數據並交給 Delegate 顯示

展望

下一節將深入講解樹狀數據結構(TreeNode)的設計、parent() 的非空實現、以及數據增刪改的模型通知機制,敬請期待。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved