Č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.

   <?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="rectangle 1" x="100" y="100" width="100" height="80"/>
           <ellipse name="item1" x="40" y="40" pen="#0000ff" brush="#6495ed" width="40" height="40"/>
           <ellipse name="item2" x="120" y="40" pen="#0000ff" brush="#6495ed" width="40" height="40"/>
       </rectangle>
   </data>

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 <znacky> */
        }
        else if (r.isEndElement())
        {
           /* jsme na konci </znacky> ( nebo /> ) */
 
        }
        r.readNext();
    }

Pokud potkame novou znacku, zjistime jeji jmeno (data, rectangle, ellipse, line).

    QString type = r.name().toString();

Korenovou znacku <data> 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 <rectangle> 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 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;
}
 
qt/graphics_read_xml.txt · Last modified: 2020/09/30 11:23 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki