Реклама

Qt/C++ - Урок 045. SvgReader на Qt. Восстановление данных из файла SVG в QGraphicsScene

РуководствоQtQGraphicsScene, Qt, SVG, Svg Reader1771

В прошлой статье был рассмотрен пример того, как сохранить объекты графической сцены в файл 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 содержатся размеры прямоугольника, а в теге g , в который обёрнут тег 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

  1. Сохранение объектов QGraphicsScene в файл векторной графики SVG

Видеоурок

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Последние комментарии
  • EVILEG
  • 13 июля 2017 г. 2:12

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Ну например так можете сделать.void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event){ if (QApplication::mouseButtons() == Qt::RightButton) { this->deleteLa...

  • Mark
  • 13 июля 2017 г. 1:26

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Подскажите пожалуйста как в данном проекте по перетаскиванию организовать удаление объекта со scene методом delete item, допустим при щелчке ПКМ по объекту QGraphicsScene. Мои попытки оказалис...

  • EVILEG
  • 10 июля 2017 г. 21:34

Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread

А что делали? Повторяете урок или как? Пытались просто скачать проект в конце статьи и запустить?

Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread

У меня происходит переполнение счетчика count, появляется ошибка malloc(): memory corruption (fast). Не подскажите, как с этим бороться?

  • EVILEG
  • 9 июля 2017 г. 2:07

GameDev на Qt - Урок 3. Уничтожение противников

Поэтому в пятом уроке есть исходники всего проекта )))). Вообще, все эти материалы были не предыдущей версии сайта, которая на WordPress. Во время переноса мог что-то потерять.

Сейчас обсуждают на форуме

Как реализовать отправку e-mail

Возможно что уже и нет необходимости в почтовом клиенте, но в своё время так же столкнулся с данной проблемой в QT. Нашел один интересный проект под названием libqxt, там реализовано дов...

  • BlinCT
  • 18 июля 2017 г. 0:00

тестирование классов в QT

Это был вопрос или утверждение? Не понятно что вы хотели этим сказать. Интересное у вас обращение.

  • Asteri
  • 14 июля 2017 г. 12:23

css

Делюсь, может, пригодится когда-нибудь) QTableView QHeaderView { background-color: #ffffff; } Вот так эта проблема лечится, градиент задать не получается, но хоть...

  • EVILEG
  • 12 июля 2017 г. 19:52

QSqlQuery выполнение sql запросов из файла

Мне думается, что это уже будет дело вкуса и вашего взгляда на проект. Если Вы действительно собираетесь просто скармливать своему софту SQL скрипты, которые должны будут выполняться для...

QML Canvas + Line. Bug?

Вот оно что, значит не баг) Спасибо