Class TerminalStripLayoutPattern can be save/load from xml

Add a new class TerminalStripLayoutPatternXml used to
save / load a TerminalStripLayoutPattern class into a xml.

Also create the namespace QETSVG used to read/write some
svg element / attribute to xml. This class is used by the
class TerminalStripLayoutPatternXml.
This commit is contained in:
joshua 2025-06-05 23:04:56 +02:00
parent 2c1b840f9c
commit 95e401a266
8 changed files with 547 additions and 2 deletions

View File

@ -166,7 +166,8 @@ HEADERS += $$files(sources/*.h) \
$$files(sources/TerminalStrip/GraphicsItem/*.h) \
$$files(sources/TerminalStrip/GraphicsItem/properties/*.h) \
$$files(sources/xml/*.h) \
$$files(sources/dxf/*.h)
$$files(sources/dxf/*.h) \
$$files(sources/svg/*.h)
SOURCES += $$files(sources/*.cpp) \
$$files(sources/editor/*.cpp) \
@ -208,7 +209,8 @@ SOURCES += $$files(sources/*.cpp) \
$$files(sources/TerminalStrip/GraphicsItem/*.cpp) \
$$files(sources/TerminalStrip/GraphicsItem/properties/*.cpp) \
$$files(sources/xml/*.cpp) \
$$files(sources/dxf/*.cpp)
$$files(sources/dxf/*.cpp) \
$$files(sources/svg/*.cpp)
# Needed for use promote QTreeWidget in terminalstripeditor.ui
INCLUDEPATH += sources/TerminalStrip/ui

View File

@ -26,8 +26,15 @@ TerminalStripLayoutPattern::TerminalStripLayoutPattern()
updateTerminalsTextOption();
}
/**
* @brief TerminalStripLayoutPattern::setHeaderTextAlignment
* Set text alignment to @param alignment. If alignment have no
* flag this function do nothing
* @param alignment
*/
void TerminalStripLayoutPattern::setHeaderTextAlignment(const Qt::Alignment &alignment)
{
if (!alignment) return;
m_header_text_alignment = alignment;
updateHeaderTextOption();
}
@ -50,8 +57,15 @@ void TerminalStripLayoutPattern::setFont(const QFont &font) {
QETUtils::pixelSizedFont(m_font);
}
/**
* @brief TerminalStripLayoutPattern::setTerminalsTextAlignment
* Set text alignment to @param alignment. If alignment have no
* flag this function do nothing
* @param alignment
*/
void TerminalStripLayoutPattern::setTerminalsTextAlignment(const Qt::Alignment &alignment)
{
if (!alignment) return;
m_terminals_text_alignment = alignment;
updateTerminalsTextOption();
}

View File

@ -953,4 +953,93 @@ bool qGraphicsItemPosFromXml(QGraphicsItem *item, const QDomElement &xml_elmt)
return false;
}
/**
* @brief orientationToAttribute
* Write the Qt::orientation has an attribute of @param element.
* Attribute name is 'orientation' value is 'horizontal' or 'vertical'
* @param orientation
* @param element
*/
void orientationToAttribute(const Qt::Orientation &orientation, QDomElement &element)
{
element.setAttribute(QStringLiteral("orientation"),
orientation == Qt::Horizontal ? QStringLiteral("Horizontal") :
QStringLiteral("Vertical"));
}
/**
* @brief orientationFromAttribute
* @param element
* @param def_value
* @return the Qt::Orientation read in @param element. If an error occur
* the returned orientation is @param def_value.
*/
Qt::Orientation orientationFromAttribute(const QDomElement &element, Qt::Orientation def_value)
{
if (element.hasAttribute(QStringLiteral("orientation"))) {
const auto str {element.attribute(QStringLiteral("orientation"))};
if (str == QLatin1String("Horizontal"))
return Qt::Horizontal;
else if (str == QLatin1String("Vertical"))
return Qt::Vertical;
}
//Error occur during reading, we return the default value
return def_value;
}
void alignmentToAttribute(const Qt::Alignment &alignment, QDomElement &element)
{
QStringList al;
if (alignment &Qt::AlignLeft)
al.append(QStringLiteral("Left"));
if (alignment &Qt::AlignRight)
al.append(QStringLiteral("Right"));
if (alignment &Qt::AlignHCenter)
al.append(QStringLiteral("HCenter"));
if (alignment &Qt::AlignJustify)
al.append(QStringLiteral("Justify"));
if (alignment &Qt::AlignTop)
al.append(QStringLiteral("Top"));
if (alignment &Qt::AlignBottom)
al.append(QStringLiteral("Bottom"));
if (alignment &Qt::AlignBottom)
al.append(QStringLiteral("VCenter"));
if (alignment &Qt::AlignBaseline)
al.append(QStringLiteral("Baseline"));
element.setAttribute(QStringLiteral("alignment"),al.join(QStringLiteral(" ")));
}
/**
* @brief alignmentFromAttribute
* @param element
* @return The alignment read in @param element. If an error
* occured the return Qt::alignment contain no set flag.
*/
Qt::Alignment alignmentFromAttribute(const QDomElement &element)
{
Qt::Alignment al;
if (element.hasAttribute(QStringLiteral("alignment"))) {
const auto alignment {element.attribute(QStringLiteral("alignment"))};
if(alignment.contains(QStringLiteral("Left")))
al = al | Qt::AlignLeft;
if(alignment.contains(QStringLiteral("Right")))
al = al | Qt::AlignRight;
if(alignment.contains(QStringLiteral("HCenter")))
al = al | Qt::AlignHCenter;
if(alignment.contains(QStringLiteral("Justify")))
al = al | Qt::AlignJustify;
if(alignment.contains(QStringLiteral("Top")))
al = al | Qt::AlignTop;
if(alignment.contains(QStringLiteral("Bottom")))
al = al | Qt::AlignBottom;
if(alignment.contains(QStringLiteral("VCenter")))
al = al | Qt::AlignVCenter;
if(alignment.contains(QStringLiteral("Baseline")))
al = al | Qt::AlignBaseline;
}
return al;
}
}

View File

@ -94,6 +94,12 @@ namespace QETXML
QDomElement qGraphicsItemPosToXml(QGraphicsItem *item, QDomDocument &document);
bool qGraphicsItemPosFromXml(QGraphicsItem *item, const QDomElement &xml_elmt);
void orientationToAttribute(const Qt::Orientation &orientation, QDomElement &element);
Qt::Orientation orientationFromAttribute(const QDomElement &element, Qt::Orientation def_value = Qt::Vertical);
void alignmentToAttribute(const Qt::Alignment &alignment, QDomElement &element);
Qt::Alignment alignmentFromAttribute (const QDomElement &element);
QString boolToString(bool value);
bool boolFromString(const QString &value,
bool default_value = true,

151
sources/svg/qetsvg.cpp Normal file
View File

@ -0,0 +1,151 @@
/*
Copyright 2006-2025 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qetsvg.h"
#include <QDomDocument>
#include <QRect>
#include <../qet.h>
/**
* @brief QETSVG::rectToElmt
* Write a QRect as a svg rect element.
* @param rect
* @param parent_document
* @return
*/
QDomElement QETSVG::rectToElmt(const QRect &rect, QDomDocument &parent_document)
{
auto dom_element = parent_document.createElement(QStringLiteral("rect"));
if (!rect.isNull()) {
dom_element.setAttribute(QStringLiteral("x"), rect.x());
yToAttribute(rect.y(), dom_element);
dom_element.setAttribute(QStringLiteral("width"), rect.width());
heightToAttribute(rect.height(), dom_element);
}
return dom_element;
}
/**
* @brief QETSVG::rectFromElmt
* @param xml_element : xml_element of an svg rect.
* The tag name must be 'rect' if not, the returned QRect is null.
* @return a svg rect to QRect
*/
QRect QETSVG::rectFromElmt(const QDomElement &xml_element)
{
QRect rect;
if (xml_element.tagName() == QLatin1String("rect")) {
rect.setRect(xml_element.attribute(QStringLiteral("x"), QStringLiteral("0")).toInt(),
static_cast<int> (yFromAttribute(xml_element, 0)),
xml_element.attribute(QStringLiteral("width"), QStringLiteral("10")).toInt(),
static_cast<int> (heightFromAttribute(xml_element, 10)));
}
return rect;
}
/**
* @brief QETSVG::yToAttribute
* @param y
* @param xml_element
*/
void QETSVG::yToAttribute(const qreal &y, QDomElement &xml_element) {
xml_element.setAttribute(QStringLiteral("y"), QString::number(y));
}
/**
* @brief QETSVG::yFromAttribute
* @param xml_element
* @param def_value
* @return
*/
qreal QETSVG::yFromAttribute(const QDomElement &xml_element, const qreal &def_value) {
qreal value;
if (QET::attributeIsAReal(xml_element, QStringLiteral("y"), &value)) {
return value;
}
return def_value;
}
/**
* @brief QETSVG::heightToAttribute
* @param height
* @param xml_element
*/
void QETSVG::heightToAttribute(const qreal &height, QDomElement &xml_element) {
xml_element.setAttribute(QStringLiteral("height"), QString::number(height));
}
qreal QETSVG::heightFromAttribute(const QDomElement &xml_element, const qreal &def_value) {
qreal value;
if (QET::attributeIsAReal(xml_element, QStringLiteral("height"), &value)) {
return value;
}
return def_value;
}
void QETSVG::rToAttribute(const qreal &r, QDomElement &xml_element) {
xml_element.setAttribute(QStringLiteral("r"), QString::number(r));
}
qreal QETSVG::rFromAttribute(const QDomElement &xml_element, const qreal &def_value) {
qreal value;
if (QET::attributeIsAReal(xml_element, QStringLiteral("r"), &value)) {
return value;
}
return def_value;
}
void QETSVG::pointsToAttribute(const QVector<QPointF> &points, QDomElement &xml_element)
{
QStringList strl;
for (const auto &point : points) {
strl.append(QString::number(point.x()) +
QString(",") +
QString::number(point.y()));
}
xml_element.setAttribute(QStringLiteral("points"), strl.join(" "));
}
/**
* @brief QETSVG::pointsFromAttribute
* @param xml_element
* @return a vector of points stored in attribute 'points' of @xml_element.
* The returned vector can be empty.
*/
QVector<QPointF> QETSVG::pointsFromAttribute(const QDomElement &xml_element)
{
QVector<QPointF> vector;
if (const auto string_points = xml_element.attribute(QStringLiteral("points")).split(QStringLiteral(" ")) ;
!string_points.isEmpty()) {
bool x_ok, y_ok; for (const auto &point : string_points) {
const auto string_x_y = point.split(QStringLiteral(","));
if (string_x_y.size() == 2) {
const auto x = string_x_y[0].toDouble(&x_ok);
const auto y = string_x_y[1].toDouble(&y_ok);
if (x_ok && y_ok) {
vector.append(QPointF{x,y});
}
}
}
}
return vector;
}

51
sources/svg/qetsvg.h Normal file
View File

@ -0,0 +1,51 @@
/*
Copyright 2006-2025 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QETSVG_H
#define QETSVG_H
#include <QDomElement>
class QDomDocument;
class QPointF;
class QRect;
/**
* @namespace QETSVG
* @brief the QETSVG namespace provide function read and write svg.
* Some function work on xml element (ex rect) and some other
* work on attribute of an element (ex x)
*/
namespace QETSVG
{
QDomElement rectToElmt(const QRect &rect, QDomDocument &parent_document);
QRect rectFromElmt(const QDomElement &xml_element);
void yToAttribute(const qreal &y, QDomElement &xml_element);
qreal yFromAttribute(const QDomElement &xml_element, const qreal &def_value=0);
void heightToAttribute(const qreal &height, QDomElement &xml_element);
qreal heightFromAttribute(const QDomElement &xml_element, const qreal &def_value=10);
void rToAttribute(const qreal &r, QDomElement &xml_element);
qreal rFromAttribute(const QDomElement &xml_element, const qreal &def_value=1);
void pointsToAttribute(const QVector<QPointF> &points, QDomElement &xml_element);
QVector<QPointF> pointsFromAttribute (const QDomElement &xml_element);
}
#endif // QETSVG_H

View File

@ -0,0 +1,191 @@
/*
Copyright 2006-2025 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "terminalstriplayoutpatternxml.h"
#include "../TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.h"
#include "../qetxml.h"
#include "../svg/qetsvg.h"
const QString LAYOUT_PATTERN_TAG_NAME { QStringLiteral("terminal_strip_layout_pattern") };
const QString LAYOUTS_PATTERN_TAG_NAME { QStringLiteral("terminal_strip_layouts_pattern") };
/**
* @brief TerminalStripLayoutPatternXml::toXml
* Save a vector of @class TerminalStripLayoutPattern into main xml element
* with tagg name 'terminal_strip_layouts_pattern' who itself embedded each single @class TerminalStripLayoutPattern
* into a xml element with tag name 'terminal_strip_layout_pattern' (layout without 'S' at the end)
* @param patterns
* @param document
* @return
*/
QDomElement TerminalStripLayoutPatternXml::toXml(const QVector<QSharedPointer<TerminalStripLayoutPattern> > &patterns,
QDomDocument &document)
{
auto dom_element = document.createElement(LAYOUTS_PATTERN_TAG_NAME);
for (const auto &pattern : patterns)
{
const auto child_ = toXml(pattern, document);
if (!child_.isNull()) {
dom_element.appendChild(child_);
}
}
return dom_element;
}
/**
* @brief TerminalStripLayoutPatternXml::fromXml
* Load a vector of @class TerminalStripLayoutPattern from a main xml element
* with tagg name 'terminal_strip_layouts_pattern' who itself have several child
* with tag name 'terminal_strip_layout_pattern' (layout without 'S' at the end) for every
* @class TerminalStripLayoutPattern to load
* @param element
* @return
*/
QVector<QSharedPointer<TerminalStripLayoutPattern> > TerminalStripLayoutPatternXml::fromXml(const QDomElement &element)
{
QVector <QSharedPointer<TerminalStripLayoutPattern>> returned_vector;
for (const auto &dom_elmt : QETXML::subChild(element,
LAYOUTS_PATTERN_TAG_NAME,
LAYOUT_PATTERN_TAG_NAME))
{
auto layout_pattern = QSharedPointer<TerminalStripLayoutPattern>::create();
fromXml(layout_pattern, dom_elmt);
returned_vector << layout_pattern;
}
return returned_vector;
}
/**
* @brief TerminalStripLayoutPatternXml::toXml
* Save a @class TerminalStripLayoutPattern to a xml element with tag name terminal_strip_layout_pattern
* @param pattern
* @param document
* @return
*/
QDomElement TerminalStripLayoutPatternXml::toXml(const QSharedPointer<TerminalStripLayoutPattern> &pattern, QDomDocument &document)
{
auto pattern_xml = document.createElement(LAYOUT_PATTERN_TAG_NAME);
if (!pattern.isNull()) {
//Write strip pattern attributes
pattern_xml.setAttribute(QStringLiteral("name"), pattern->m_name);
pattern_xml.setAttribute(QStringLiteral("uuid"), pattern->m_uuid.toString());
//Write header properties
auto header_xml = document.createElement(QStringLiteral("header"));
header_xml.appendChild(QETSVG::rectToElmt(pattern->m_header_rect, document));
auto header_text_xml = document.createElement(QStringLiteral("text"));
QETXML::orientationToAttribute(pattern->m_header_text_orientation, header_text_xml);
QETXML::alignmentToAttribute(pattern->headerTextAlignment(),header_text_xml);
//Write spacer properties
auto spacer_xml = document.createElement(QStringLiteral("spacer"));
spacer_xml.appendChild(QETSVG::rectToElmt(pattern->m_spacer_rect, document));
pattern_xml.appendChild(header_xml).appendChild(header_text_xml);
pattern_xml.appendChild(spacer_xml);
//Write terminals properties
auto terminals_xml = document.createElement(QStringLiteral("terminals"));
for (const auto &rect : pattern->m_terminal_rect) {
terminals_xml.appendChild(QETSVG::rectToElmt(rect, document));
}
auto terminals_text_xml = document.createElement(QStringLiteral("text"));
QETXML::orientationToAttribute(pattern->m_terminals_text_orientation, terminals_text_xml);
QETXML::alignmentToAttribute(pattern->terminalsTextAlignment(),terminals_text_xml);
QETSVG::yToAttribute(pattern->m_terminals_text_y, terminals_text_xml);
QETSVG::heightToAttribute(pattern->m_terminals_text_height, terminals_text_xml);
terminals_xml.appendChild(terminals_text_xml);
auto bridge_xml = document.createElement(QStringLiteral("bridges"));
QETSVG::rToAttribute(static_cast<qreal>(pattern->m_bridge_point_d)/2, bridge_xml);
QETSVG::pointsToAttribute(QVector<QPointF>{QPointF{0, static_cast<qreal> (pattern->m_bridge_point_y_offset.at(0))},
QPointF{0, static_cast<qreal> (pattern->m_bridge_point_y_offset.at(1))},
QPointF{0, static_cast<qreal> (pattern->m_bridge_point_y_offset.at(2))},
QPointF{0, static_cast<qreal> (pattern->m_bridge_point_y_offset.at(3))}},
bridge_xml);
terminals_xml.appendChild(bridge_xml);
pattern_xml.appendChild(terminals_xml);
}
return pattern_xml;
}
/**
* @brief TerminalStripLayoutPatternXml::fromXml
* LOad a @class TerminalStripLayoutPattern from a xml element with tag name terminal_strip_layout_pattern
* @param layout
* @param element
*/
void TerminalStripLayoutPatternXml::fromXml(QSharedPointer<TerminalStripLayoutPattern> &layout, const QDomElement &element)
{
if (element.tagName() != LAYOUT_PATTERN_TAG_NAME) return;
layout->m_name = element.attribute(QStringLiteral("name"), layout->m_name);
layout->m_uuid.fromString(element.attribute(QStringLiteral("uuid"), layout->m_uuid.toString()));
const auto header_xml = element.firstChildElement(QStringLiteral("header"));
if (!header_xml.isNull() && header_xml.hasAttribute(QStringLiteral("rect"))) {
layout->m_header_rect = QETSVG::rectFromElmt(header_xml);
}
if (const auto header_text_xml = header_xml.firstChildElement(QStringLiteral("text"));
!header_text_xml.isNull()) {
layout->m_header_text_orientation = QETXML::orientationFromAttribute(header_text_xml, layout->m_header_text_orientation);
layout->setHeaderTextAlignment(QETXML::alignmentFromAttribute(header_text_xml));
}
const auto spacer_xml = element.firstChildElement(QStringLiteral("spacer"));
if (const auto rect = QETSVG::rectFromElmt(spacer_xml);
!rect.isNull()) {
layout->m_spacer_rect = rect;
}
if (const auto terminals_xml = element.firstChildElement(QStringLiteral("terminals"));
!terminals_xml.isNull()) {
layout->m_terminal_rect.clear();
for (const auto &terminal_rect_xml : QETXML::directChild(terminals_xml, QStringLiteral("rect"))) {
layout->m_terminal_rect.append(QETSVG::rectFromElmt(terminal_rect_xml)) ;
}
if (const auto terminals_text_xml = terminals_xml.firstChildElement(QStringLiteral("text"));
!terminals_text_xml.isNull()) {
layout->m_terminals_text_orientation = QETXML::orientationFromAttribute(terminals_text_xml, layout->m_terminals_text_orientation);
layout->setTerminalsTextAlignment(QETXML::alignmentFromAttribute(terminals_text_xml));
layout->m_terminals_text_y = QETSVG::yFromAttribute(terminals_text_xml);
layout->m_terminals_text_height = QETSVG::heightFromAttribute(terminals_text_xml);
}
if (const auto bridge_xml = terminals_xml.firstChildElement(QStringLiteral("bridges"));
!bridge_xml.isNull()) {
layout->m_bridge_point_d = QETSVG::rFromAttribute(bridge_xml, 2.5)*2;
if (const auto points = QETSVG::pointsFromAttribute(bridge_xml);
points.size() == 4) {
layout->m_bridge_point_y_offset[0]= static_cast<int>(points[0].y());
layout->m_bridge_point_y_offset[1]= static_cast<int>(points[1].y());
layout->m_bridge_point_y_offset[2]= static_cast<int>(points[2].y());
layout->m_bridge_point_y_offset[3]= static_cast<int>(points[3].y());
}
}
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2006-2025 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TERMINALSTRIPLAYOUTPATTERNXML_H
#define TERMINALSTRIPLAYOUTPATTERNXML_H
#include <QDomElement>
#include <QSharedPointer>
class TerminalStripLayoutPattern;
/**
* @brief The TerminalStripLayoutPatternXml class
* A class used to save/restor a @class TerminalStripLayoutPattern to
* xml
*/
class TerminalStripLayoutPatternXml
{
public:
static QDomElement toXml(const QVector<QSharedPointer<TerminalStripLayoutPattern>> &patterns, QDomDocument &document);
static QVector<QSharedPointer<TerminalStripLayoutPattern>> fromXml(const QDomElement &element);
static QDomElement toXml (const QSharedPointer<TerminalStripLayoutPattern> &pattern, QDomDocument &document);
static void fromXml(QSharedPointer<TerminalStripLayoutPattern> &layout, const QDomElement &element);
};
#endif // TERMINALSTRIPLAYOUTPATTERNXML_H