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

C++ 實現 QML TreeView Model 實戰(二):樹狀節點結構與層次模型完整實現

     編輯:Java 綜合教學

C++ 實現 QML TreeView Model 實戰(二):樹狀數據結構與層次模型深度解析

上一篇實現了扁平的二維表模型,本文將進階到真正的樹狀結構。核心在於設計節點類 TreeNode 來維護父子關係,並修改模型類以正確處理非空 parent 索引。

一、樹節點類 TreeNode 設計

每個節點需保存:自身數據、父節點指針、子節點列表。


class TreeNode {
public:
    explicit TreeNode();
    TreeNode* parent() const;
    void setParent(TreeNode* p);           // 根節點父節點為 nullptr
    void appendNode(TreeNode* node);       // 添加子節點
    void removeNode(int row);              // 移除子節點
    void setData(int role, QVariant value); // 設置角色數據
    QVariant data(int role) const;         // 獲取角色數據

    QList<TreeNode*> childs;              // 子節點列表(建議私有+訪問器)
private:
    TreeNode* mParent = nullptr;
    QHash<int, QVariant> mRecord;         // 單行數據:role -> value
};

關鍵方法實現


TreeNode* TreeNode::parent() const { return mParent; }
void TreeNode::setParent(TreeNode* p) { mParent = p; }
void TreeNode::appendNode(TreeNode* node) { childs.append(node); }
void TreeNode::removeNode(int row) { childs.removeAt(row); }
QVariant TreeNode::data(int role) { return mRecord[role]; }
void TreeNode::setData(int role, QVariant value) { mRecord[role] = value; }

二、模型類 SqlMenuEntry 重構為樹模型

保留原有角色枚舉與 roleNames(),將 mRecords 替換為根節點列表 mRootEntrys,並新增輔助方法 _addEntryNode。


class SqlMenuEntry : public QAbstractItemModel, public QQmlParserStatus {
    Q_OBJECT
public:
    // ... 角色枚舉、五大虛函數聲明、roleNames() ...
private:
    QHash<int, QByteArray> mRoleNames;
    QList<TreeNode*> mRootEntrys;         // 根節點列表
    void _addEntryNode(TreeNode* node, TreeNode* parent = nullptr);
};

添加節點邏輯


void SqlMenuEntry::_addEntryNode(TreeNode* node, TreeNode* parent) {
    if (parent == nullptr) {
        mRootEntrys.append(node);          // 作為根節點
    } else {
        node->setParent(parent);
        parent->appendNode(node);          // 掛接到父節點
    }
}

構建樹形數據示例


beginResetModel();
auto root = new TreeNode();
root->setData(nameRole, "parent 1");
root->setData(iconRole, "parenticon1");
_addEntryNode(root);                       // 添加根節點

auto child = new TreeNode();
child->setData(nameRole, "child1");
_addEntryNode(child, root);                // 添加子節點
endResetModel();

三、五大虛函數的樹形實現

1. rowCount:區分根節點與子節點


int SqlMenuEntry::rowCount(const QModelIndex &parent) const {
    if (parent.isValid()) {
        TreeNode* parentNode = static_cast<TreeNode*>(parent.internalPointer());
        return parentNode->childs.size();  // 返回子節點數
    }
    return mRootEntrys.size();             // 返回根節點數
}

2. index:創建帶內部指針的 ModelIndex


QModelIndex SqlMenuEntry::index(int row, int column, const QModelIndex &parent) const {
    if (!parent.isValid()) {               // 根節點
        if (row >= 0 && row < mRootEntrys.size())
            return createIndex(row, column, mRootEntrys.at(row));
    } else {                               // 子節點
        TreeNode* parentNode = static_cast<TreeNode*>(parent.internalPointer());
        if (row >= 0 && row < parentNode->childs.size())
            return createIndex(row, column, parentNode->childs[row]);
    }
    return QModelIndex();
}

3. parent:返回父節點索引


QModelIndex SqlMenuEntry::parent(const QModelIndex &child) const {
    TreeNode* node = static_cast<TreeNode*>(child.internalPointer());
    if (node->parent() == nullptr) return QModelIndex(); // 根節點無父
    // 簡化:假設父節點在其父節點中行號為 0,列為 1
    return createIndex(0, 1, node->parent());
}

4. data:從節點獲取角色數據


QVariant SqlMenuEntry::data(const QModelIndex &index, int role) const {
    TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
    return node->data(role);
}

四、QML TreeView 綁定展示


TreeView {
    anchors.fill: parent
    TableViewColumn { title: "Name"; role: "name" }
    TableViewColumn { title: "Entry"; role: "icon" }
    model: MenuEntryModel {}
}

五、關鍵技術點總結

  • internalPointer 傳遞節點指針:實現索引與數據節點的雙向綁定
  • parent() 非空處理:是樹模型區別於表模型的核心
  • TreeNode 封裝數據與關係:職責單一,便於擴展拖拽、編輯等功能
  • beginResetModel/endResetModel:批量更新時通知視圖重建

下一步預告

下一節將講解動態數據增刪改(insertRows, removeRows, setData)、dataChanged 信號觸發時機、以及複雜樹結構下的 parent() 高效實現(避免線性查找父節點行號)。

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