在使用Qt的GraphicsScene作圖時,遇到類似這樣的需求:在scene中創建節點類似下圖,
現在我要把每個節點的txt保存到xml文件中,結構為
<?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);
保存:
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:
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; }
先寫到這里,下一篇將寫一下,如何完全還原圖形。