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