阅读量:2
在项目中遇到一个问题:需要将QString
路径,做成一棵树的样式展示出来。其实倒不是说是问题,只是记录写这块儿的代码的思路
前提:我的所有的QString
都是用"/"
进行分割的,分割后的list
中第一个元素是公共的路径,也就是树的根节点
举例:
string str1 = "A/B/B1/B2"; string str2 = "A/B/B1/B3"; string str4 = "A/C/B3"; string str5 = "A/C/B1"; string str6 = "A/B/B2";
具体的代码如下:
// 定义的树控件 m_treeWidget 为成员变量 m_treeWidget->clear(); // 设置根节点 QTreeWidgetItem *root = new QTreeWidgetItem(m_treeWidget); //树的根 m_treeWidget->addTopLevelItem(root); // 将所有的路径写在一个 vector 中,用于遍历 string str1 = "A/B/B1/B2"; string str2 = "A/B/B1/B3"; string str4 = "A/C/B3"; string str5 = "A/C/B1"; string str6 = "A/B/B2"; vector<string> vec; vec.push_back(str1); vec.push_back(str2); vec.push_back(str4); vec.push_back(str5); vec.push_back(str6); // 遍历所有路径 把所有路径按照"/"分割,保存在list中 for (int i = 0; i < vec.size(); i++) { QString strFirst = QString::fromStdString(vec[i]); // 调用 split 分割 QStringList nameListFirst = strFirst.split('/', Qt::SkipEmptyParts); if (i == 0) { // 因为所有路径的第一个名称为根节点,所以只设置一次根节点的名称 root->setText(0, nameListFirst[0]); } // 在后续递归的时候就不用第一个根节点了 nameListFirst.removeAt(0); if (!nameListFirst.isEmpty()) { this->traverseTree(root, nameListFirst); } } // 设置隐藏树的头部 m_treeWidget->setHeaderHidden(true);
递归的思想
- 判断当前节点的子节点个数是否为空,若为空说明有两种情况:
当前是第一个list
进入递归,树中还没有节点。或者当前节点是树的叶子节点了
无论是哪种情况,都只需要把list中的值设置为当前节点的子节点就可以
终止条件:list
为空。list
为空就说明,当前list
中没有内容了,已经到叶子节点了 - 当前节点的子节点个数不为空,此时说明:
当前节点已经在树中存在,那么就拿到当前节点的所有子节点,判断list[0]
是否已经被包含在所有的子节点中
如果是,说明list[0]
中的名称已经存在子节点中了,则说明当前list[0]
所在的节点已经在树上了,那么把list
移除第一个元素,并且找到重合的节点。将移除后的list
和该重合的节点作为参数传入递归中
如果否,说明list[0]
名称的节点没有在树上,这是就为树添加子节点,子节点的名称就是list[0]
终止条件:当list
为空,说明当前已经到叶子节点了,此时结束。
递归的代码
void traverseTree(QTreeWidgetItem * parent, QStringList& list) { if (!parent) { return; } QString name1 = parent->text(0); // 获取当前节点的子节点个数 int childCount = parent->childCount(); // 第一种情况,当前父节点的子节点为空,就为父节点添加子节点 if (childCount == 0) { QTreeWidgetItem *childItem = new QTreeWidgetItem(parent); // 为子节点设置名称,名称就是list[0] childItem->setText(0, list[0]); list.removeAt(0); // 终止条件:list为空,说明已经到了叶子节点了 if (list.isEmpty()) { return; } traverseTree(childItem, list); } else { // 第二种情况,当前父节点的子节点不为空 // 找出当前父节点的所有子节点名称,保存在list中 QStringList nodeList; for (int j = 0; j < childCount; j++) { QTreeWidgetItem *childItem = parent->child(j); nodeList.append(childItem->text(0)); } QString tempName = list[0]; // 如果list[0]的名称已经包含在子节点名称中,则在所有的子节点中找到同名的节点 if (nodeList.contains(tempName)) { QTreeWidgetItem* node = new QTreeWidgetItem(); for (int i = 0; i < childCount; i++) { QTreeWidgetItem *childItem = parent->child(i); if (childItem->text(0) == tempName) { node = childItem; } } // 将list[0]移除 list.removeAt(0); if (list.isEmpty()) { return; } traverseTree(node, list); } // list[0]的名称不包含在子节点名称中,直接在父节点后添加子节点,节点名称就是list[0] else { // 当前层不包括当前名,直接该名称追加在父节点后 QTreeWidgetItem *childItem = new QTreeWidgetItem(parent); childItem->setText(0, list[0]); list.removeAt(0); if (list.isEmpty()) { return; } // 进入下一层迭代 traverseTree(childItem, list); } } }
不得不感叹,之前写算法的时候写到树的深度和广度遍历,用纯C++写,虽然代码量也不多,但是要递归。现在用了QT,广度遍历直接一个接口就搞定了,封装好的接口真好用啊!!!