Im letzten Artikel wurde ein Beispiel zum Speichern von Grafikszenenobjekten in einer SVG-Datei betrachtet, und dann konnten wir sie in CorelDraw. öffnen. Und jetzt versuchen wir, dieselbe Datei zu öffnen und Grafikobjekte in QGraphicsScene wiederherzustellen.
Beachten Sie, dass wir hierfür nicht die Klasse QSvgRenderer verwenden werden, da sie den Inhalt der SVG-Datei ohne Probleme auf der Grafikbühne platziert, ABER es wird ein einzelnes Grafikobjekt sein, und wenn Sie es als separate Grafikobjekte wiederherstellen möchten, zum Beispiel QGraphicsItem , dann ist es notwendig, die SVG-Datei zu parsen, um alles zu machen grafische Objekte daraus.
Da die SVG-Datei eine Struktur im XML-Format hat, ist es nicht schwierig, sie mit den Klassen der Familie QDomDocument. zu parsen.
Projektstruktur
Als Beispiel wird das Projekt aus dem vorherigen Artikel verwendet, aber mit einer zusätzlichen Klasse mit zwei statischen Methoden verwässert.
- SVGExample.pro - Projektprofil;
- svgreader.h - SVG-Parser-Header-Datei;
- svgreader.cpp - SVG-Parser-Quellcodedatei;
- mainwindow.h - Header-Datei des Hauptanwendungsfensters;
- mainwindow.cpp - Quellcodedatei für das Hauptanwendungsfenster;
- mainwindow.ui - Formulardatei des Hauptfensters der Anwendung;
- main.cpp - Quelldatei der Anwendung starten.
Die Struktur der SVG-Datei
Da wir diese Datei parsen müssen, werfen wir einen Blick auf ihr Inneres.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="141.111mm" height="141.111mm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Example</title> <desc>File created by SVG Example</desc> <defs> </defs> <g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" > <g fill="#ff0000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="2" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)" font-family="MS Shell Dlg 2" font-size="8.25" font-weight="400" font-style="normal" > <rect x="10" y="50" width="100" height="50"/> </g> </g>
Wir interessieren uns also für svg, g, rect. Das svg -Tag enthält die Abmessungen der Grafikszene – das ist viewBox. Das rect -Tag enthält die Abmessungen des Rechtecks , und das Tag g* * , das das Tag rect** umschließt, enthält die Füll- und Umrissfarben des Rechtecks sowie die Dicke des Umrisses. Sie müssen verschrottet werden.
mainwindow.ui
In dieser Datei wird nur der Lade-Button hinzugefügt, bei dessen Signal wir den Dateiauswahldialog öffnen und mit dem Öffnen unserer SVG-Datei beginnen.
SVGExample.pro
Ich mache Sie darauf aufmerksam, dass Sie im Projektprofil das Modul xml. einfügen müssen
QT += xml
svgreader.h
Eine unauffällige Header-Datei mit ein paar statischen Methoden in der Klasse.
#ifndef SVGREADER_H #define SVGREADER_H #include <QList> #include <QGraphicsRectItem> class SvgReader { public: SvgReader(); static QList<QGraphicsRectItem *> getElements(const QString filename); static QRectF getSizes(const QString filename); }; #endif // SVGREADER_H
svgreader.cpp
Und darin liegt die ganze Magie. Bei den beiden oben genannten Methoden erhalten wir eine Liste von Rechtecken und die Abmessungen der Grafikszene.
#include "svgreader.h" #include <QPen> #include <QFile> #include <QMessageBox> #include <QDomDocument> #include <QStringList> SvgReader::SvgReader() { } QList<QGraphicsRectItem *> SvgReader::getElements(const QString filename) { QList<QGraphicsRectItem *> rectList; // Объявим в стеке список прямоугольников QDomDocument doc; // объект документа QFile file(filename); // Открываем наш SVG-файл // Если он не открылся или не удалось передать содержимое в QDocDocument if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) return rectList; // то возвратим список, но пустой // Ищем в документе все объекты с тегом g QDomNodeList gList = doc.elementsByTagName("g"); // Начинаем их перебирать for (int i = 0; i < gList.size(); i++) { QDomNode gNode = gList.item(i); // Выделяем из списка ноду QDomElement rectangle = gNode.firstChildElement("rect"); // И ищем в ней элемент c тегом rect // Если полученный элементы не нулевой, то if (rectangle.isNull()){ continue; } else { // начинаем формировать прямоугольник QGraphicsRectItem *rect = new QGraphicsRectItem(); // Этот флаг делает объект перемещаемым, потребуется для проверки rect->setFlag(QGraphicsItem::ItemIsMovable); // Забираем размеры из тега rect QDomElement gElement = gNode.toElement(); rect->setRect(rectangle.attribute("x").toInt(), rectangle.attribute("y").toInt(), rectangle.attribute("width").toInt(), rectangle.attribute("height").toInt()); /* Забираем из элемента ноды gNode параметры цветов * да да да... именно из gNode, а не из rectangle. Эти параметры храняться в теге g * */ QColor fillColor(gElement.attribute("fill", "#ffffff")); // цвет заливки fillColor.setAlphaF(gElement.attribute("fill-opacity","0").toFloat()); rect->setBrush(QBrush(fillColor)); // а также цвет и толщина абриса QColor strokeColor(gElement.attribute("stroke", "#000000")); strokeColor.setAlphaF(gElement.attribute("stroke-opacity").toFloat()); rect->setPen(QPen(strokeColor,gElement.attribute("stroke-width", "0").toInt())); rectList.append(rect); // добавляем прямоугольник в список } } file.close(); return rectList; } QRectF SvgReader::getSizes(const QString filename) { QDomDocument doc; // инициализируем в стеке объект QDomDocument QFile file(filename); // Открываем наш SVG-файл // Если он не открылся или не удалось передать содержимое в QDocDocument if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) return QRectF(0,0,200,200); // то возвратим значения для сцены по умолчанию /* Далее забираем список элементов с тегом svg. * В случае, если список элементов будет не пустой, * то заберём размеры графической сцены. * */ QDomNodeList list = doc.elementsByTagName("svg"); if(list.length() > 0) { QDomElement svgElement = list.item(0).toElement(); QStringList parameters = svgElement.attribute("viewBox").split(" "); return QRectF(parameters.at(0).toInt(), parameters.at(1).toInt(), parameters.at(2).toInt(), parameters.at(3).toInt()); } return QRectF(0,0,200,200); }
Hauptfenster.h
Hier wurde nur ein Slot hinzugefügt, um auf ein Signal von der Datei-Öffnen-Schaltfläche zu reagieren.
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QGraphicsScene> #include <QGraphicsRectItem> #include <QSvgGenerator> #include <QFileDialog> #include <QPainter> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_saveButton_clicked(); void on_loadButton_clicked(); private: Ui::MainWindow *ui; QGraphicsScene *scene; // Графическая сцена QString path; // Путь сохранения файла }; #endif // MAINWINDOW_H
Hauptfenster.cpp
Ich werde in dieser Datei nur den Inhalt angeben, der sich direkt auf das Parsen der SVG-Datei bezieht. In dem Schaltflächenklick-Verarbeitungsslot wird eine Datei unter Verwendung des Dialogfelds ausgewählt und in grafische Objekte zerlegt.
#include "mainwindow.h" #include "ui_mainwindow.h" #include "svgreader.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); scene = new QGraphicsScene(); ui->graphicsView->setScene(scene); scene->setSceneRect(0,0,400,400); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_saveButton_clicked() { // Код из предыдущего урока по работе с SVG } void MainWindow::on_loadButton_clicked() { QString newPath = QFileDialog::getOpenFileName(this, trUtf8("Open SVG"), path, tr("SVG files (*.svg)")); if (newPath.isEmpty()) return; path = newPath; scene->clear(); scene->setSceneRect(SvgReader::getSizes(path)); // Зададим размеры графической сцены // Установим на графическую сцену объекты, получив их с помощью метода getElements foreach (QGraphicsRectItem *item, SvgReader::getElements(path)) { QGraphicsRectItem *rect = item; scene->addItem(rect); } }
Insgesamt
Als Ergebnis können Sie die von Ihnen gespeicherte SVG-Datei parsen, um mindestens die Rechtecke daraus aufzunehmen. Wenn Sie alle anderen Objekte aufnehmen möchten, müssen Sie viel schwitzen und einen Parser für all diese anderen Objekte schreiben, analog zu einem Rechteck. Das Einzige, was ich anmerken möchte, ist, dass Sie nicht versuchen, eine ursprünglich in CorelDraw erstellte Datei mit diesem Code zu öffnen, Tatsache ist, dass die Versionen der SVG-Dateistruktur auch sehr unterschiedlich sind. Und dieser Code dient zum Analysieren der Datei, die in der letzten Lektion erstellt wurde, und die Struktur der vom CorelDraw -Paket generierten Dateien ist etwas anders und wird nicht vollständig gelesen.
Mehr dazu erfahren Sie im Video-Tutorial. Und das Projekt, das beide Lektionen kombiniert hat, kann unter folgendem Link heruntergeladen werden: SvgExample
Здравствуйте!
Там в коде у меня есть:
Для формирования задал объект QGraphiscPathItem
А кажется понял, я должен из атрибутов path сформировать заново элементы Ellipse, просто нужно правильно подобрать эти атрибуты.....
Ну да. Там должны быть координаты, их нужно пропарсить и по ним построить Path, либо преобразовать их в эллипс. Вообще, кое-что кривовато переносится в SVG местами.
How can I open another polygon type such as circle(ellipse) ?
The principle will be similar. You need to research content of svg file and make parsing of needed tag.
Thank You sir, So how can I change this method
For what You want to use another class instead of QRectF?
Sir,I tried your code for open ellipse item.but my program not open ellipse item.what should I need to do?
I think You have another version of SVG file. First, need to see content of SVG file. It is simple XML-format, therefore just need to research content.
Sir I post is as a topic,please help me to solve this problem
Скажите пожалуйста, как пользоваться QSvgRenderer и загружать целиком в QGraphicsScene ?
Если делаю так, то получаю белый экран:
А если делаю с помощью вашего класса, то тоже получаю белый экрак.
Рисую на сцене так: Qt/C++ - Урок 021. Рисование мышью в Qt
Вот так целиком добавлять можно