Qt 遍历不规则树的节点
在使用Qt的GraphicsScene作图时,遇到类似这样的需求:在scene中创建节点类似下图,
现在我要把每个节点的txt保存到xml文件中,结构为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <? xml version='1.0' encoding='UTF-8'?> < root > < A > < B > </ B > < C > < E > ... </ E > </ C > < D > < F > ... </ F > </ D > </ A > </ root > |
并且,还要能根据xml文件将这个结构还原出来(由于没有保存坐标信息,当然不能恢复原状)。
我将过程分为:遍历,保存xml;读取xml,还原图形。
我使用Qt的示例项目DiagramScene来改造。
首先,起始节点要与其他节点有所不同,便于我们区分。diagramitem类中,DiagramType { Step, Conditional, StartEnd, Io };,我添加一个virStep类型作为开始节点,其他节点类型为Step。其实virStep和Step的图形一模一样,只是类型不同。
在DiagramItem类中增加一些成员
QList<Arrow *> getArrow(){return arrows;}
void setText(QString text){ mText = text;}
void setItemId(QString pid){ ItemId = pid;}//唯一标识,在创建时设置
QString getItemId(){return ItemId;}
QString getText(){ return mText; }
重写其paint函数,在其中添加:
painter->drawText(boundingRect() , Qt::AlignCenter ,mText);
保存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | bool OperateItemFile::SaveRelationship(QList<DiagramItem *> &listItem, QList<Arrow *> &listArrow) { QFileDialog dlg; dlg.setAcceptMode(QFileDialog::AcceptSave); QString strName = dlg.getSaveFileName(getMainWindow() ,QObject::tr( "save xml file" ), "" ,QObject::tr( "XML文件(*.XML)" )); if (strName.isEmpty()) return false ; QDomDocument doc; doc.appendChild( doc.createProcessingInstruction( "xml" , XML_HEAD_INFO)); QFile file(strName); if (!file.open(QIODevice::WriteOnly)){ return false ; } QDomElement root = doc.createElement( "Root" ); doc.appendChild(root); DiagramItem* startItem = NULL ; QDomElement startEle; for ( int i = 0; i < listItem.size(); i++) { if (listItem.at(i)->diagramType() == DiagramItem::virStep){ //开始节点 startEle = doc.createElement(listItem.at(i)->getText()); root.appendChild(startEle); startItem = listItem.at(i); //listItem.removeAt(i); break ; } } if (!startItem) return false ; TravelAllItem(startItem, doc, startEle, listItem); QString xml = doc.toString(); QTextStream txtOutput(&file); txtOutput.setCodec( "UTF-8" ); txtOutput<<xml; file.close(); return true ; } void OperateItemFile::GetSubItems(DiagramItem *item, QList<DiagramItem*>& listNodeItem) { if (!item) return ; listNodeItem.clear(); QList<Arrow*> listArrow = item->getArrow(); for ( int i = 0; i < listArrow.size(); i++) { if (listArrow.at(i)->startItem() == item){ listNodeItem.push_back(listArrow.at(i)->endItem()); } } } void OperateItemFile::TravelAllItem(DiagramItem *rootItem, QDomDocument &doc, QDomElement &rootEle, QList<DiagramItem *> &listItem) { if (listItem.size() < 1) return ; QList<DiagramItem*> listNodeItem; GetSubItems(rootItem, listNodeItem); for ( int i = 0; i < listNodeItem.size(); i++) { QDomElement ele = doc.createElement(listNodeItem.at(i)->getText()); rootEle.appendChild(ele); listItem.removeOne(rootItem); TravelAllItem(listNodeItem.at(i), doc, ele, listItem); } } |
使用GraphicsScene的selectItems函数,从中分别获得diagramitem和arrow。代码如果编译不通,可根据情况修改。
读入xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | void OperateItemFile::ImportTargetFile(DiagramScene* pScene) { QString strName; strName = QFileDialog::getOpenFileName( nullptr , QObject::tr( "open xml file" ), p_globalObject->ProjectPath() + "/" + p_globalObject->getProjectName() ,QObject::tr( "XML文件(*XML*)" )); if (strName.isEmpty()){ return ; } QDomDocument doc; QFile file(strName); if (!file.open(QIODevice::ReadOnly)){ return ; } if (!doc.setContent(&file)) { file.close(); return ; } QDomNode domNodeStart = doc.documentElement(); QList<DiagramItem*> listItem; QList<Arrow*> listArrow; m_bStartNode = true ; SetItemPos(pScene->sceneRect().center()); parsePackage(domNodeStart, listItem, listArrow); pScene->ItemClear(); int i=0; for (i=0 ;i<listItem.size() ;i++){ pScene->addItem(listItem.at(i)); } pScene->setMode(DiagramScene::Mode::MoveItem); //////////////////////////////////////////////////////////////////////////////////// //设置箭头起始节点 int j=0; for (i=0 ;i<listArrow.size(); i++){ for (j=0 ;j<listItem.size() ;j++){ if (listArrow.at(i)->getStartId() == listItem.at(j)->getItemId()){ listArrow.at(i)->setStartItem(listItem.at(j)); break ; } } } //设置箭头结尾节点 for (i=0 ;i<listArrow.size(); i++){ for (j=0 ;j<listItem.size() ;j++){ if (listArrow.at(i)->getEndId() == listItem.at(j)->getItemId()){ listArrow.at(i)->setEndItem(listItem.at(j)); break ; } } } for (i=0 ;i<listArrow.size() ;i++){ if (listArrow.at(i)->startItem()!=NULL) listArrow.at(i)->startItem()->addArrow(listArrow.at(i)); if (listArrow.at(i)->endItem()!=NULL) listArrow.at(i)->endItem()->addArrow(listArrow.at(i)); listArrow.at(i)->setColor(pScene->lineColor()); listArrow.at(i)->setZValue(-1000.0); pScene->addItem(listArrow.at(i)); if (p_globalObject->getCurStatus() == QGlobalObject::devStatus) listArrow.at(i)->setArrowFlag( true ); else listArrow.at(i)->setArrowFlag( true ); listArrow.at(i)->updatePosition(); } file.close(); } bool OperateItemFile::parsePackage(QDomNode pNode, QList<DiagramItem*>& listItem, QList<Arrow*>& listArrow) { QDomNode myNode = pNode.firstChild(); while (!myNode.isNull()){ QDomElement domE = myNode.toElement(); QString qstrTagName = domE.tagName(); myDebug(qstrTagName); if (qstrTagName.compare(PackageRoot) != 0 && !qstrTagName.isEmpty()) { DiagramItem* pItem = NULL; if (m_bStartNode){ pItem = new DiagramItem(DiagramItem::virStep ,NULL); domE.setAttribute( "StartItem" , "true" ); m_bStartNode = false ; } else { pItem = new DiagramItem(DiagramItem::Step ,NULL); } pItem->setPos(m_itemPos); m_itemPos = GetNextItemPos(m_itemPos); //pItem->setZValue(z.toFloat()); pItem->setBkColor(QColor(4294967295)); pItem->setItemId(p_globalObject->getGUID()); pItem->setText(qstrTagName); pItem->setBrush(QBrush(4294967295, (Qt::BrushStyle)1)); domE.setAttribute( "ItemId" , pItem->getItemId()); if (!domE.hasAttribute( "StartItem" )){ Arrow* pArrow = new Arrow(NULL, NULL); QString startItemId = pNode.toElement().attribute( "ItemId" ); QString endItemId = pItem->getItemId(); pArrow->setItemId(startItemId, endItemId); listArrow.push_back(pArrow); } listItem.push_back(pItem); } parsePackage(myNode, listItem, listArrow); myNode = myNode.nextSibling(); } return true ; } QPointF OperateItemFile::GetNextItemPos(QPointF preItemPos) { float rat = 1; qreal x = preItemPos.x()+80; qreal y = x*rat; QPointF pt(x,y); return pt; } |
先写到这里,下一篇将写一下,如何完全还原图形。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具