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

