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;
}   

先写到这里,下一篇将写一下,如何完全还原图形。

posted @   阳光下的小土豆  阅读(1753)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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工具
点击右上角即可分享
微信分享提示