У минулій статті було розглянуто приклад того, як зберегти об'єкти графічної сцени у файл SVG, а потім ми змогли відкрити його в CorelDraw. А тепер спробуємо цей ж файл відкрити і відновити графічні об'єкти в QGraphicsScene .
Зазначу, що ми не будемо використовувати клас QSvgRenderer для цього з тієї причини, що він без проблем помістить вміст файлу SVG на графічну сцену, але це буде один єдиний графічний об'єкт, а якщо Вам потрібно, щоб він відновився в як окремі графічні об'єкти, наприклад, QGraphicsItem , то необхідно буде парсить файл SVG виготовляти з нього всі графічні об'єкти.
Оскільки файл SVG має структуру XML-формату, то розібрати його не уявить жодної праці за допомогою класів сімейства QDomDocument.
Структура проекта
Як приклад, використовуватиметься проект із попередньої статті, але буде розведений додатковим класом із двома статичними методами.
- SVGExample.pro - профайл проекту;
- svgreader.h - заголовний файл парсера SVG;
- svgreader.cpp – файл вихідних кодів парсера SVG;
- mainwindow.h - заголовний файл головного вікна програми;
- mainwindow.cpp - файл вихідних кодів головного вікна програми;
- mainwindow.ui - файл форми головного вікна програми;
- main.cpp – стартовий файл вихідних кодів програми.
Структура SVG файла
Оскільки нам потрібно парсить цей файл, то заглянемо в його нутрощі.
<?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>
Отже, нас цікавлять теги svg, g, rect. У тезі svg містяться розміри графічної сцени - це *viewBox. * , у який обернутий тег rect містяться кольори заливки та абрису прямокутника, а також товщина абрису. Їх і треба буде спарити.
mainwindow.ui
У цьому файлі буде додана тільки кнопка load, по сигналу від якої відкриємо діалог вибору файлу та запустимо відкриття нашого файлу SVG.
SVGExample.pro
Звертаю вашу увагу на те, що у профайлі проекту потрібно буде підключити модуль xml.
QT += xml
svgreader.h
Нічим не примітний заголовний файл із парою статичних методів у класі.
#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
А ось тут і ховається вся магія. У двох вище наведених методах отримуватимемо список прямокутників та розміри графічної сцени.
#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); }
mainwindow.h
Тут додався лише слот для реакцію сигнал від кнопки відкриття файла.
#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
mainwindow.cpp
Наведу в цьому файлі тільки вміст, що стосується безпосередньо SVG-файлу. У слоті обробки натискання кнопки вибирається за допомогою діалогового вікна файл та розбирається на графічні об'єкти.
#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); } }
Підсумок
В результаті ви зможете розібрати збережений SVG файл, щоб забрати з нього хоча б прямокутники. Якщо ви хочете забирати і всі інші об'єкти, то доведеться добряче попотіти і написати парсер для всіх інших об'єктів за аналогією з прямокутником. Єдине, що хочу відзначити, не намагайтеся за допомогою цього коду відкрити файл, створений спочатку в CorelDraw, справа в тому, що версії структури файлу SVG теж сильно відрізняються. І цей код призначений для парсингу файлу, який був створений у минулому уроці, а структура файлів, що генеруються пакетом CorelDraw дещо відрізняється і не буде прочитана повністю.
Докладніше ви можете з цим ознайомитись у відеоуроці. А проект, який об'єднав обидва уроки завантажити за посиланням: 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
Вот так целиком добавлять можно