基于Qt實現(xiàn)的自定義樹結(jié)構(gòu)容器
在Qt框架中,盡管其提供了許多強(qiáng)大的容器類(如 QList
, QMap
, QTreeWidget
等),但缺少一個通用的、靈活的樹結(jié)構(gòu)容器,直接支持多層級數(shù)據(jù)管理。為了滿足這些需求,本文設(shè)計并實現(xiàn)了一個可復(fù)用的自定義樹結(jié)構(gòu)容器,并討論其在不同項目中的應(yīng)用。
1. 背景與動機(jī)
樹結(jié)構(gòu)在軟件開發(fā)中是常見的數(shù)據(jù)組織形式,常用于以下場景:
- 多層級文件管理器:文件夾與文件的樹形展示。
- 層次化關(guān)系管理:如公司組織結(jié)構(gòu)、任務(wù)依賴關(guān)系。
- 數(shù)據(jù)處理與分類:如屬性分類、規(guī)則樹等。
然而,Qt 中缺少直接的樹結(jié)構(gòu)容器(QTreeWidget 是 UI 組件,QAbstractItemModel 偏向于數(shù)據(jù)視圖)。因此,我們實現(xiàn)了一個靈活、可擴(kuò)展的 通用樹結(jié)構(gòu)容器,支持:
- 動態(tài)添加和刪除節(jié)點。
- 為節(jié)點附加數(shù)據(jù)。
- 數(shù)據(jù)篩選與查找。
- 清晰的樹形結(jié)構(gòu)打印與調(diào)試。
2. 核心設(shè)計與實現(xiàn)
2.1 類設(shè)計概覽
該樹容器包含兩個核心類:
TreeNode:
- 表示樹的單個節(jié)點。
- 包括節(jié)點名稱、父節(jié)點指針、子節(jié)點列表、節(jié)點數(shù)據(jù)。
- 支持節(jié)點添加、刪除、數(shù)據(jù)設(shè)置與清除等基本操作。
Tree:
- 管理整個樹的邏輯。
- 提供全局的節(jié)點操作接口,如添加、刪除節(jié)點,篩選節(jié)點數(shù)據(jù),打印樹結(jié)構(gòu)等。
2.2 TreeNode 類實現(xiàn)
TreeNode 是樹結(jié)構(gòu)的核心,負(fù)責(zé)管理節(jié)點的層次關(guān)系和數(shù)據(jù)存儲。以下是其關(guān)鍵代碼邏輯:
class TreeNode { public: explicit TreeNode(const QString& name, TreeNode* parent = nullptr); ~TreeNode(); // 添加子節(jié)點 TreeNode* addChild(const QString& name); // 移除子節(jié)點 bool removeChild(TreeNode* child); // 設(shè)置與清除節(jié)點數(shù)據(jù) void setData(const QVariant& data); void clearData(); // 獲取節(jié)點信息 QVariant getData() const; const QList<TreeNode*>& getChildren() const; QString getName() const; TreeNode* getParent() const; };
主要功能:
- addChild 和 removeChild 實現(xiàn)樹的動態(tài)結(jié)構(gòu)調(diào)整。
- setData 和 clearData 支持靈活的節(jié)點數(shù)據(jù)管理。
- 提供對父子關(guān)系和數(shù)據(jù)的訪問接口。
2.3 Tree 類實現(xiàn)
Tree 是一個樹容器的管理類。其設(shè)計目標(biāo)是:
- 提供用戶友好的接口,隱藏樹節(jié)點的內(nèi)部操作。
- 支持全局的增刪改查功能。
以下是 Tree 類的部分接口說明:
class Tree { public: Tree(); ~Tree(); // 節(jié)點操作 TreeNode* addNode(TreeNode* parent, const QString& name); bool removeNode(TreeNode* node); // 數(shù)據(jù)操作 void setNodeData(TreeNode* node, const QVariant& data); QVariant getNodeData(TreeNode* node) const; void clearNodeData(TreeNode* node); // 數(shù)據(jù)篩選與樹形打印 QList<TreeNode*> filterNodes(const QString& keyword) const; void printTree() const; };
主要功能:
- addNode:動態(tài)添加節(jié)點,支持將節(jié)點默認(rèn)添加到根節(jié)點。
- ilterNodes:通過關(guān)鍵字查找包含特定數(shù)據(jù)的節(jié)點。
- printTree:以層級縮進(jìn)格式打印樹的結(jié)構(gòu),便于調(diào)試。
2.4 調(diào)用示例
以下是使用 Tree 和 TreeNode 的示例代碼:
int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); // 創(chuàng)建樹容器 Tree tree; // 添加節(jié)點 TreeNode* root = tree.addNode(nullptr, tc("根節(jié)點")); TreeNode* nodeA = tree.addNode(root, tc("節(jié)點A")); TreeNode* nodeB = tree.addNode(root, tc("節(jié)點B")); TreeNode* nodeC = tree.addNode(nodeA, tc("節(jié)點C")); // 設(shè)置節(jié)點數(shù)據(jù) tree.setNodeData(nodeA, tc("溫度過高")); tree.setNodeData(nodeB, tc("正常")); tree.setNodeData(nodeC, tc("壓力過低")); // 打印樹結(jié)構(gòu) tree.printTree(); // 篩選包含 "溫度" 的節(jié)點 QList<TreeNode*> filteredNodes = tree.filterNodes(tc("溫度")); qDebug() << tc("篩選結(jié)果:"); for (TreeNode* node : filteredNodes) { qDebug() << node->getName() << ":" << node->getData().toString(); } return a.exec(); }
運(yùn)行結(jié)果:
根節(jié)點 ()
節(jié)點A (溫度過高)
節(jié)點C (壓力過低)
節(jié)點B (正常)
篩選結(jié)果:
"節(jié)點A" : "溫度過高"
3. 適用場景分析
該樹容器的靈活性使其適用于多種場景,包括但不限于以下項目:
文件管理器:
- 以層次結(jié)構(gòu)管理文件夾和文件。
- 節(jié)點數(shù)據(jù)可存儲文件的元信息(如路徑、大小)。
組織結(jié)構(gòu)管理:
- 用于顯示公司組織架構(gòu)(如部門、員工)。
- 節(jié)點數(shù)據(jù)可附加員工信息。
規(guī)則引擎或決策樹:
- 用于實現(xiàn)條件匹配規(guī)則。
- 節(jié)點存儲規(guī)則條件與結(jié)果。
動態(tài)數(shù)據(jù)分類:
- 實現(xiàn)類似標(biāo)簽分類的功能。
- 支持實時增刪節(jié)點。
調(diào)試工具:
用于顯示復(fù)雜系統(tǒng)中的內(nèi)部數(shù)據(jù)關(guān)系。
4. 優(yōu)勢與改進(jìn)方向
4.1 優(yōu)勢
簡單易用:
- 接口友好,隱藏復(fù)雜的內(nèi)部操作。
- 提供清晰的錯誤提示和默認(rèn)行為。
高擴(kuò)展性:
可以輕松添加新功能,如節(jié)點排序、自定義過濾條件等。
靈活性:
節(jié)點的數(shù)據(jù)類型為 QVariant,支持多種數(shù)據(jù)類型存儲。
跨平臺支持:
依賴 Qt 框架,具備良好的跨平臺能力。
4.2 改進(jìn)方向
線程安全:
增加對并發(fā)操作的支持,例如通過 QMutex 實現(xiàn)線程同步。
持久化:
增加樹結(jié)構(gòu)的序列化和反序列化功能,用于存儲和加載數(shù)據(jù)。
性能優(yōu)化:
對大規(guī)模樹操作(如深度遍歷)進(jìn)行優(yōu)化。
模型綁定:
將樹容器與 QAbstractItemModel 綁定,支持直接用于 Qt 的視圖類(如 QTreeView)。
5. 結(jié)語
本文介紹了一個基于 Qt 實現(xiàn)的自定義樹結(jié)構(gòu)容器,其功能涵蓋了節(jié)點管理、數(shù)據(jù)存儲、篩選與打印等操作,適用于多種項目場景。通過該容器,開發(fā)者可以更加靈活地管理復(fù)雜的層次化數(shù)據(jù),同時其清晰的接口設(shè)計也便于擴(kuò)展與維護(hù)。
6. 源碼
以下是修正后的完整代碼實現(xiàn),包含 TreeNode.h、TreeNode.cpp、Tree.h、Tree.cpp 和 main.cpp 文件。代碼修復(fù)了根節(jié)點初始化問題,并增強(qiáng)了錯誤處理和默認(rèn)邏輯。
TreeNode.h
#ifndef TREENODE_H #define TREENODE_H #include <QString> #include <QList> #include <QVariant> #define tc(a) QString::fromLocal8Bit(a) class TreeNode { public: explicit TreeNode(const QString& name, TreeNode* parent = nullptr); ~TreeNode(); // 添加子節(jié)點 TreeNode* addChild(const QString& name); // 移除子節(jié)點 bool removeChild(TreeNode* child); // 設(shè)置節(jié)點數(shù)據(jù) void setData(const QVariant& data); // 獲取節(jié)點數(shù)據(jù) QVariant getData() const; // 移除節(jié)點數(shù)據(jù) void clearData(); // 獲取所有子節(jié)點 const QList<TreeNode*>& getChildren() const; // 獲取節(jié)點名稱 QString getName() const; // 獲取父節(jié)點 TreeNode* getParent() const; // 檢查是否為葉子節(jié)點 bool isLeaf() const; private: QString nodeName; // 節(jié)點名稱 QVariant nodeData; // 節(jié)點數(shù)據(jù) TreeNode* parentNode; // 父節(jié)點 QList<TreeNode*> childNodes; // 子節(jié)點列表 }; #endif // TREENODE_H
TreeNode.cpp
#include "TreeNode.h" #include <QDebug> TreeNode::TreeNode(const QString& name, TreeNode* parent) : nodeName(name), parentNode(parent) {} TreeNode::~TreeNode() { qDeleteAll(childNodes); // 刪除所有子節(jié)點 } TreeNode* TreeNode::addChild(const QString& name) { TreeNode* child = new TreeNode(name, this); childNodes.append(child); return child; } bool TreeNode::removeChild(TreeNode* child) { if (!child || !childNodes.contains(child)) { qWarning() << tc("移除失敗:節(jié)點不存在!"); return false; } childNodes.removeAll(child); delete child; // 刪除子節(jié)點及其數(shù)據(jù) return true; } void TreeNode::setData(const QVariant& data) { nodeData = data; } QVariant TreeNode::getData() const { return nodeData; } void TreeNode::clearData() { nodeData.clear(); } const QList<TreeNode*>& TreeNode::getChildren() const { return childNodes; } QString TreeNode::getName() const { return nodeName; } TreeNode* TreeNode::getParent() const { return parentNode; } bool TreeNode::isLeaf() const { return childNodes.isEmpty(); }
Tree.h
#ifndef TREE_H #define TREE_H #include "TreeNode.h" class Tree { public: Tree(); ~Tree(); // 添加節(jié)點 TreeNode* addNode(TreeNode* parent, const QString& name); // 移除節(jié)點 bool removeNode(TreeNode* node); // 設(shè)置節(jié)點數(shù)據(jù) void setNodeData(TreeNode* node, const QVariant& data); // 獲取節(jié)點數(shù)據(jù) QVariant getNodeData(TreeNode* node) const; // 移除節(jié)點數(shù)據(jù) void clearNodeData(TreeNode* node); // 查找節(jié)點(通過名稱) TreeNode* findNode(TreeNode* root, const QString& name) const; // 過濾節(jié)點(通過數(shù)據(jù)關(guān)鍵字) QList<TreeNode*> filterNodes(const QString& keyword) const; // 打印樹結(jié)構(gòu) void printTree() const; private: TreeNode* root; // 輔助遞歸方法 void filterRecursive(TreeNode* node, const QString& keyword, QList<TreeNode*>& result) const; void printRecursive(TreeNode* node, int depth) const; }; #endif // TREE_H
Tree.cpp
#include "Tree.h" #include <QDebug> Tree::Tree() { root = new TreeNode(tc("根節(jié)點")); qDebug() << tc("成功初始化根節(jié)點。"); } Tree::~Tree() { delete root; // 自動刪除所有節(jié)點 } TreeNode* Tree::addNode(TreeNode* parent, const QString& name) { if (!parent) { if (!root) { qWarning() << tc("添加失?。焊?jié)點未創(chuàng)建!"); return nullptr; } qDebug() << tc("未指定父節(jié)點,默認(rèn)添加到根節(jié)點。"); parent = root; // 如果父節(jié)點為空,默認(rèn)添加到根節(jié)點 } return parent->addChild(name); } bool Tree::removeNode(TreeNode* node) { if (!node || node == root) { qWarning() << tc("移除失敗:節(jié)點為空或為根節(jié)點!"); return false; } TreeNode* parent = node->getParent(); if (!parent) { qWarning() << tc("移除失敗:父節(jié)點為空!"); return false; } return parent->removeChild(node); } void Tree::setNodeData(TreeNode* node, const QVariant& data) { if (!node) { qWarning() << tc("設(shè)置失敗:節(jié)點為空!"); return; } node->setData(data); } QVariant Tree::getNodeData(TreeNode* node) const { if (!node) { qWarning() << tc("獲取失敗:節(jié)點為空!"); return QVariant(); } return node->getData(); } void Tree::clearNodeData(TreeNode* node) { if (!node) { qWarning() << tc("清除失敗:節(jié)點為空!"); return; } node->clearData(); } TreeNode* Tree::findNode(TreeNode* root, const QString& name) const { if (!root) return nullptr; if (root->getName() == name) return root; for (TreeNode* child : root->getChildren()) { TreeNode* found = findNode(child, name); if (found) return found; } return nullptr; } QList<TreeNode*> Tree::filterNodes(const QString& keyword) const { QList<TreeNode*> result; filterRecursive(root, keyword, result); return result; } void Tree::filterRecursive(TreeNode* node, const QString& keyword, QList<TreeNode*>& result) const { if (node->getData().toString().contains(keyword)) { result.append(node); } for (TreeNode* child : node->getChildren()) { filterRecursive(child, keyword, result); } } void Tree::printTree() const { printRecursive(root, 0); } void Tree::printRecursive(TreeNode* node, int depth) const { qDebug().noquote() << QString(depth * 2, ' ') + node->getName() + " (" + node->getData().toString() + ")"; for (TreeNode* child : node->getChildren()) { printRecursive(child, depth + 1); } }
main.cpp
#include <QCoreApplication> #include "Tree.h" int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); // 創(chuàng)建樹 Tree tree; // 創(chuàng)建子節(jié)點,明確傳入父節(jié)點 TreeNode* nodeA = tree.addNode(nullptr, tc("節(jié)點A")); // 默認(rèn)添加到根節(jié)點 TreeNode* nodeB = tree.addNode(nodeA, tc("節(jié)點B")); TreeNode* nodeC = tree.addNode(nodeA, tc("節(jié)點C")); // 添加數(shù)據(jù) tree.setNodeData(nodeA, tc("溫度過高")); tree.setNodeData(nodeB, tc("正常")); tree.setNodeData(nodeC, tc("壓力過低")); // 獲取數(shù)據(jù) qDebug() << tc("節(jié)點A數(shù)據(jù):") << tree.getNodeData(nodeA).toString(); // 清除數(shù)據(jù) tree.clearNodeData(nodeC); // 過濾節(jié)點 QList<TreeNode*> filteredNodes = tree.filterNodes(tc("溫度")); qDebug() << tc("過濾結(jié)果:"); for (TreeNode* node : filteredNodes) { qDebug() << node->getName() << ":" << node->getData().toString(); } // 打印樹結(jié)構(gòu) tree.printTree(); return a.exec(); }
運(yùn)行結(jié)果
成功初始化根節(jié)點。
未指定父節(jié)點,默認(rèn)添加到根節(jié)點。
未指定父節(jié)點,默認(rèn)添加到根節(jié)點。
未指定父節(jié)點,默認(rèn)添加到根節(jié)點。
節(jié)點A數(shù)據(jù): "溫度過高"
過濾結(jié)果:
"節(jié)點A" : "溫度過高"
根節(jié)點 ()
節(jié)點A (溫度過高)
節(jié)點B (正常)
節(jié)點C ()
功能總結(jié)
該實現(xiàn)支持樹節(jié)點的 添加、刪除、查詢、過濾,以及節(jié)點數(shù)據(jù)的 設(shè)置、獲取、清除。同時,包含中文提示與日志輸出,邏輯健壯且易于擴(kuò)展。
到此這篇關(guān)于基于Qt實現(xiàn)的自定義樹結(jié)構(gòu)容器的文章就介紹到這了,更多相關(guān)Qt自定義樹結(jié)構(gòu)容器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)的數(shù)學(xué)算法
這篇文章和大家分享一下我個人對十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)的想法,目前暫時更新只整數(shù)十進(jìn)制的轉(zhuǎn)換,后續(xù)會更新帶有小數(shù)的進(jìn)制轉(zhuǎn)換,代碼使用c++實現(xiàn)2021-09-09C語言中字符串與各數(shù)值類型之間的轉(zhuǎn)換方法
這篇文章主要介紹了C語言中字符串與各數(shù)值類型之間的轉(zhuǎn)換方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03clion最新激活碼+漢化的步驟詳解(親測可用激活到2089)
這篇文章主要介紹了clion最新版下載安裝+破解+漢化的步驟詳解,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11詳談C與C++的函數(shù)聲明中省略參數(shù)的不同意義
下面小編就為大家分享一篇詳談C與C++的函數(shù)聲明中省略參數(shù)的不同意義,具有非常好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11