====== Čtení XML souboru ====== Reakce na File/open: zobrazime dialogovy box pro vyber jmena souboru. \\ ( demo.cc http://gitlab.fjfi.cvut.cz/culikzde/sos/-/tree/master/graphics04 ) void MainWindow::on_actionOpen_triggered () { QString fileName = QFileDialog::getOpenFileName (this, "Open file"); if (fileName != "") openFile (fileName); } Pokud si uzivatel vybere jmeno souboru, zavolame **openFile**. \\ Pomoci tridy **QFile** otevreme soubor. \\ Vytvorime lokalni promennou typu **QXmlStreamReader** pripojenou na nas soubor. \\ void MainWindow::openFile (QString fileName) { QFile f (fileName); if (f.open (QFile::ReadOnly)) { QXmlStreamReader r (&f); readXml (r, scene); } refreshTree (); } Funkci **readXml** pouzijeme take na konci konstruktoru tridy MainWindow \\ k nacteni souboru abc.xml pri startu naseho programu. Soubor abc.xml nalezneme mezi zdrojovymi soubory v adresari graphics04/data. abc.xml je "zakompilovan" do naseho spustitelneho programu. \\ V Qt Creatoru nento soubor naleznete ve vetvi "Resources". \\ V projektovem souboru graphics04.pro je uveden soubor resources.qrc, ve kterem je uveden abc.xml. Za behu programu se na "zakompilovane" soubory odvolavame pomoci cest zacinajicich **dvojteckou**. openFile (":/data/abc.xml"); ===== Funkce readXml ===== Funkce **readXml** je deklarovana v io.h \\ Nova data jsou vkladana do scene zadane parametrem **scene**. \\ V ramci sceny muzeme prvky vkladat dovnitr prvku **target**. \\ Nebo primo do sceny, pokud target je nullptr. void readXml (QXmlStreamReader & r, QGraphicsScene * scene, QGraphicsItem * target = nullptr); Pro pripomenuti priklad xml souboru abc.xml. Implementace readXml je v souboru io.cc \\ Napiseme cyklus, ktery vyuziva promennou **r** typu **QXmlStreamReader**. \\ Cyklus pobezi, dokud **atEnd** neohlasi konec souboru. \\ Metody **isStartElement** a **isEndElement** oznamuji, pokud jsme na zacatku nebo konci XML prvku. \\ **readNext** posouva reader na dalsi cast XML souboru. while (! r.atEnd()) { if (r.isStartElement()) { /* jsme na zacatku nejake */ } else if (r.isEndElement()) { /* jsme na konci ( nebo /> ) */ } r.readNext(); } Pokud potkame novou znacku, zjistime jeji jmeno (data, rectangle, ellipse, line). QString type = r.name().toString(); Korenovou znacku budu ignorovat. \\ Pro jine znacky zavolam metodu **readItem**, ktera vrati ukazatel na novy graficky prvek. \\ Novy prvek pridam primo do sceny, nebo pod prvek **target**. \\ A novy prvek budu pouzivat jako novy **target**, do doby nez potkam zaviraci znacku. \\ V nasem XML souboru obsahuje dve vnorene elipsy. void readXml (QXmlStreamReader & r, QGraphicsScene * scene, QGraphicsItem * target) { while (! r.atEnd()) { if (r.isStartElement()) { QString type = r.name().toString(); if (type != "data") // top level element { QGraphicsItem * item = readItem (r); if (item != nullptr) { // add item if (target != nullptr) item->setParentItem (target); else scene->addItem (item); // change target target = item; } } } else if (r.isEndElement()) { if (target != nullptr) target = target->parentItem (); // back to above target } r.readNext(); } } ===== Zjednodušená funkce readItem ===== Pomocna funkce **createItem** podle zadaneho jmena vytvori graficky prvek. QGraphicsItem * createItem (QString type) { QGraphicsItem * result = nullptr; if (type == "rectangle") result = new QGraphicsRectItem; else if (type == "ellipse") result = new QGraphicsEllipseItem; else if (type == "line") result = new QGraphicsLineItem; return result; } Funkce **readItem** podle jmena znacky vytvori graficky prvek. \\ Objekt **a** typu ** QXmlStreamAttributes** obsahuje jednotlive atributy (napr. x="209" y="209" z naseho souboru). \\ Vyzvedneme jednotlive hodnoty atributu z XML souboru a ulozime do grafickeho prvku. \\ Na zaver nastavime priznaky ItemIsMovable a ItemIsSelectable dovolujici manipulaci pomoci mysi. QGraphicsItem * readItem (QXmlStreamReader & r) { QString type = r.name().toString(); QGraphicsItem * item = createItem (type); QXmlStreamAttributes a = r.attributes(); QString name = getString (a, "name"); item->setToolTip(name); int x = getNumber (a, "x"); int y = getNumber (a, "y"); item->setPos (x, y); item->setFlag (QGraphicsItem::ItemIsMovable); item->setFlag (QGraphicsItem::ItemIsSelectable); return item; } Funkce **getString** a **getNumber** pro pristup k jednotlivym atributum. \\ Pokud atribut v XML souboru neni, funkce getString vrati prazdny retezec, funkce getNumber hodnotu zadanou prametrem init. QString getString (QXmlStreamAttributes & a, QString name) { QString result = ""; if (a.hasAttribute (name)) { result = a.value(name).toString(); } return result; } int getNumber (QXmlStreamAttributes & a, QString name, int init = 0) { int result = init; if (a.hasAttribute (name)) { bool ok; result = a.value(name).toInt(&ok); if (! ok) result = init; } return result; } ===== Kompletní funkce readItem ===== Funkce **readItem** si musi poradit s ruznymi typy grafickych prvku prectenych z XML souboru. **dynamic_cast** jsme jiz pouzivali k urceni typu pri zapisu [[sos:graphics_write_xml#urceni_typu_jednotlivych_grafickych_prvku|dynamic_cast]] Nasledujici konstrukce zkusi pretypovat **item** na ukazatel na tridu **T**. \\ Vysledek ulozi do promenne **e** opet typu ukazatel na **T**. \\ Promenna **e** existuje jen po dobu existence **if** prikazu, v nasem pripade jen v **if** a nasledujicich slozenych zavorkach. \\ Pokud se pretypovani nepovede, je do promenne **e** ulozen **nullptr**, ktery *if* povazuje za **false** a slozene zavorky se neprovedou ( a **else** zde nemame ) if (T * e = dynamic_cast < T * > (item)) { } Zde je funkce **readItem** i se zpracovanim ruznych typu. QGraphicsItem * readItem (QXmlStreamReader & r) { QGraphicsItem * item = nullptr; if (r.isStartElement()) { QString type = r.name().toString(); item = createItem (type); QXmlStreamAttributes a = r.attributes(); QString name = getString (a, "name"); item->setToolTip(name); int x = getNumber (a, "x"); int y = getNumber (a, "y"); item->setPos (x, y); if (QAbstractGraphicsShapeItem * shape = dynamic_cast < QAbstractGraphicsShapeItem * > (item)) { QColor c = getColor(a, "pen", QColor ("red")); QColor d = getColor(a, "brush", QColor ("yellow")); shape->setPen (c); shape->setBrush (d); if (QGraphicsRectItem * r = dynamic_cast < QGraphicsRectItem * > (shape)) { int w = getNumber (a, "w", 100); int h = getNumber (a, "h", 80); r->setRect (0, 0, w, h); } if (QGraphicsEllipseItem * e = dynamic_cast < QGraphicsEllipseItem * > (shape)) { int w = getNumber (a, "w", 100); int h = getNumber (a, "h", 80); e->setRect (0, 0, w, h); } } if (QGraphicsLineItem * t = dynamic_cast < QGraphicsLineItem * > (item)) { int w = getNumber (a, "w", 100); int h = getNumber (a, "h", 80); t->setLine (0, 0, w, h); } item->setFlag (QGraphicsItem::ItemIsMovable); item->setFlag (QGraphicsItem::ItemIsSelectable); } return item; }