Jeste neumime obrazek editorat, misto toho zkusime cteni a zapisovani do souboru.
Priklad souboru abc.xml, ve kterem je ulozen popis dvou elips a jednoho obdelniku.
<?xml version="1.0" encoding="UTF-8"?> <data> <ellipse name="elipsa 1" pen="orange" brush="lime" x="209" y="209" width="100" height="80"/> <ellipse name="elipsa 2" x="325" y="108" width="100" height="80"/> <rectangle name="rectange 1" x="100" y="100" width="100" height="80"/> </data>
Nejprve si popiseme ukladani do souboru - bude snad prehlednejsi nez cteni souboru.
( Muzete se podivat na tutorial: http://www.bogotobogo.com/Qt/Qt5_QtXML_QDomDocument_QDomElement.php )
Do menu “File” na polozku “Save” pripojime nasledujici funkci.
QFileDialog::getSaveFileName zobrazi dialog a necha uzivatele vybrat jmeno pro ukladany soubor.
Pokud by uzivatel ukoncil dialog a nevybral jmeno souboru bude v promenn fileName prazdny retezec.
void MainWindow::on_actionSave_triggered () { QString fileName = QFileDialog::getSaveFileName (this, "Save file"); if (fileName != "") saveFile (fileName); }
Funkce saveFile otevre soubor pro zapis.
Pokud se soubor povede otevrit, pripoji na soubor objekt typu QXmlStreamWriter.
QXmlStreamWriter nam vyrazne usnadni vytvareni XML souboru.
void MainWindow::saveFile (QString fileName) { QFile f (fileName); if (f.open (QFile::WriteOnly)) { QXmlStreamWriter w (&f); writeXml (w, scene); } }
Fuknce writeXml je na konci souboru io.cc
Stream writer se neustale predava odkazem &, tj. pracujeme stale se stejnym stream writerem.
Funkce writeBegin vytvori povnny uvod a otevre element/znacku *<data>*
Potom writeXml v cyklu projde prvky graficke sceny.
Prvky sceny jsou odvozene z typu QGraphicsItem.
My si vybereme jen prvky, ktere nejsou vnorene do jinych grafickych prvku.
#include <QXmlStreamWriter> void writeBegin (QXmlStreamWriter & w) { w.setAutoFormatting (true); w.writeStartDocument (); w.writeStartElement ("data"); } void writeEnd (QXmlStreamWriter & w) { w.writeEndElement(); // end of data w.writeEndDocument(); } void writeXml (QXmlStreamWriter & w, QGraphicsScene * scene) { writeBegin (w); for (QGraphicsItem * item : scene->items (Qt::AscendingOrder)) { if (item->parentItem() == nullptr) writeItem (w, item); } writeEnd (w); }
Pomocna funkce itemType urci jmeno pouzivane v elementech <rectangle>, <ellipse> atd.
Parametr item je ukazatel na QGraphicsItem z ktereho jsou odvozeny jine typy, napr. QGraphicsEllipseItem.
Priblizne takto :
class QGraphicsItem { }; class QAbstractGraphicsShapeItem : public QGraphicsItem { }; class QGraphicsEllipseItem : public QAbstractGraphicsShapeItem { };
Přetypování z odvozene tridy na nadrazenou je v C++ automaticke.
Přetypování z nadrazene tridy na odvozenou se nemusi vzdy podarit.
Výraz dynamic_cast zkusi pretypovat ukazatel a pokud je pretypovani nepodari vrati nullptr.
QGraphicsItem * item; // nejaky neznamy objekt, mozna i nullptr QGraphicsEllipseItem * e = dynamic_cast < QGraphicsEllipseItem * > (item); if (e != nullptr) { /* item je ellipsa, pretypovani se povedlo */ }
A zde je slibovana funkce itemType.
QString itemType (QGraphicsItem * item) { QString result = "node"; if (dynamic_cast < QGraphicsRectItem * > (item) != nullptr) result = "rectangle"; if (dynamic_cast < QGraphicsEllipseItem * > (item) != nullptr) result = "ellipse"; if (dynamic_cast < QGraphicsLineItem * > (item) != nullptr) result = "line"; return result; }
Funkce writeItem vytvori novy XML element, napr. <ellipse>
<ellipse name="jmeno" x="10" y="20"> <rectangle> ... </rectangle> </ellipse>
Ulozi jednotlive atributy x = , y ==
Zjisti zda je prvek typu QAbstractGraphicsShapeItem, pokud ano muze ziskat a ulozit vlastnosti pen a brush.
if (QAbstractGraphicsShapeItem * shape = dynamic_cast < QAbstractGraphicsShapeItem * > (item)) { /* promenna shape je deklarovana uvnitr if ( ), je inicializovana pomoci dynamic_cast a existuje jen vramci slozenych zavarok, kde je tento komentar. */ }
Pokud prvek obsahuje vnorene prvky, vypiseme vnorene prvky.
( <rectangle> v predesle ukazce XML souboru )
for (QGraphicsItem * t : item->childItems ()) writeItem (w, t);
Nakonec uzavreme element, napr </ellipse> pomoci writeEndElement.
void writeItem (QXmlStreamWriter & w, QGraphicsItem * item) { w.writeStartElement (itemType (item)); w.writeAttribute ("name", item->toolTip()); w.writeAttribute ("x", QString::number ( item->x() )); w.writeAttribute ("y", QString::number ( item->y() )); if (QAbstractGraphicsShapeItem * shape = dynamic_cast < QAbstractGraphicsShapeItem * > (item)) { w.writeAttribute ("pen", penToString (shape->pen())); w.writeAttribute ("brush", brushToString (shape->brush())); if (QGraphicsRectItem * r = dynamic_cast < QGraphicsRectItem * > (shape)) { w.writeAttribute ("width", QString::number ( r->rect().width() )); w.writeAttribute ("height", QString::number ( r->rect().height() )); } if (QGraphicsEllipseItem * e = dynamic_cast < QGraphicsEllipseItem * > (shape)) { w.writeAttribute ("width", QString::number ( e->rect().width() )); w.writeAttribute ("height", QString::number ( e->rect().height() )); } } for (QGraphicsItem * t : item->childItems ()) writeItem (w, t); w.writeEndElement(); }