У минулій статті було розглянуто приклад того, як зберегти об'єкти графічної сцени у файл 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
Вот так целиком добавлять можно