/
pi_coder
/
ByteMachine
Обзор
Документация
Войти
/
pi_coder
/
ByteMachine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/nodes/base_node.cpp
1 592 строки
49 KB
pimenov-and
Перенос функциональности из старого проекта, в основном это описания узлов
19 июл 2026, 20:13
19 июл 2026, 20:13
cb3c51b
Код
Авторство
О чём код?
//////////////////////////////////////////////////////////////// // ByteMachine // Базовый класс узла //////////////////////////////////////////////////////////////// #include "base_node.h" #include "node_name_manager.h" #include "undo/undo_change_object_prop_value.h" #include "colors.h" #include "xml_helper.h" #include "qt_helper.h" #include "exceptions/base_exception.h" #include <QMap> #include <QUndoStack> #include <QColor> #include <QPen> #include <QPainter> #include <QDomDocument> #include <QTimerEvent> #include <algorithm> #include <optional> //============================================================== using std::find; using std::distance; using std::optional; //////////////////////////////////////////////////////////////// // Реализация класса BaseNode //////////////////////////////////////////////////////////////// //============================================================== // Статические поля //============================================================== // Размеры символа QSize BaseNode::charSize_{}; // Тип комментариев bool BaseNode::isCommentsVisible_{false}; //============================================================== // Конструктор с параметром //============================================================== BaseNode::BaseNode(QUndoStack *undoStack, QObject *parent) : QObject{parent} { Q_ASSERT(undoStack != nullptr); undoStack_ = undoStack; } //============================================================== // Задание имени //============================================================== void BaseNode::setName(const QString &name) { if (name_ != name) { QString newName = name; if (name.isEmpty()) { newName = name_; } nodeNameManager()->removeName(name_); newName = nodeNameManager()->addName(newName); const QString oldName = name_; name_ = newName; const PropValue value{"name", name_}; emit sigChangedProp(value); if (!isUndo_ && (oldName != newName)) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "name", newName, oldName}; undoStack_->push(undoCmd); } } } //============================================================== // Задание комментария //============================================================== void BaseNode::setComment(const QString &comment) { if (comment_ != comment) { const QString oldComment = comment_; comment_ = comment; const PropValue value{"comment", comment_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "comment", comment, oldComment}; undoStack_->push(undoCmd); } startHighlightTimer(); } } //============================================================== // Сброс комментария //============================================================== void BaseNode::resetComment() { setComment(QString{}); } //============================================================== // Получение представления в виде строки //============================================================== QString BaseNode::toStr() const { return name() + " : " + strType(); } //============================================================== // Задание стека отмен //============================================================== void BaseNode::setUndoStack(QUndoStack *undoStack) { Q_ASSERT(undoStack != nullptr); if (undoStack_ != undoStack) { undoStack_ = undoStack; } } //============================================================== // Задание признака режима отмены //============================================================== void BaseNode::setUndo(bool isUndo) { if (isUndo_ != isUndo) { isUndo_ = isUndo; } } //============================================================== // Сброс признака отмены //============================================================== void BaseNode::resetUndo() { setUndo(false); } //============================================================== // Задание смещения слева //============================================================== void BaseNode::setLeft(qint32 left) noexcept { if (left < gridSize()) { left = gridSize(); } if (left_ != left) { const int oldLeft = left_; left_ = left; const PropValue value{"left", left_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "left", left, oldLeft}; undoStack_->push(undoCmd); } } } //============================================================== // Задание смещения сверху //============================================================== void BaseNode::setTop(qint32 top) noexcept { if (top < gridSize()) { top = gridSize(); } if (top_ != top) { const int oldTop = top_; top_ = top; const PropValue value{"top", top_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "top", top, oldTop}; undoStack_->push(undoCmd); } } } //============================================================== // Задание ширины узла //============================================================== void BaseNode::setWidth(qint32 width) { width = correctWidth(width); if (width_ != width) { const qint32 oldWidth = width_; width_ = width; const PropValue value{"width", width_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "width", width, oldWidth}; undoStack_->push(undoCmd); } } } //============================================================== // Задание высоты узла //============================================================== void BaseNode::setHeight(qint32 height) { height = correctHeight(height); if (height_ != height) { const int oldHeight = height_; height_ = height; const PropValue value{"height", height_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "height", height, oldHeight}; undoStack_->push(undoCmd); } } } //============================================================== // Получение центра //============================================================== QPoint BaseNode::center() const { return QPoint{left() + width() / 2, top() + height() / 2}; } //============================================================== // Задание левого верхнего угла //============================================================== void BaseNode::setTopLeft(QPoint topLeft) { if (topLeft.x() < gridSize()) { topLeft.setX(gridSize()); } if (topLeft.y() < gridSize()) { topLeft.setY(gridSize()); } if (this->topLeft() != topLeft) { const int oldLeft = left_; const int oldTop = top_; left_ = topLeft.x(); top_ = topLeft.y(); const PropValue value{"topLeft", topLeft}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "topLeft", QPoint{left_, top_}, QPoint{oldLeft, oldTop}}; undoStack_->push(undoCmd); } } } //============================================================== // Задание размера узла //============================================================== void BaseNode::setSize(const QSize &size) { const int w = correctWidth(size.width()); const int h = correctHeight(size.height()); if ((w != width()) || (h != height())) { const int oldWidth = width_; const int oldHeight = height_; width_ = w; height_ = h; const PropValue value{"size", QSize{width_, height_}}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "size", QSize{width_, height_}, QSize{oldWidth, oldHeight}}; undoStack_->push(undoCmd); } } } //============================================================== // Начало перемещения //============================================================== void BaseNode::beginMove(const QPoint &topLeft) { isMoving_ = true; movingBeginPos_ = topLeft; } //============================================================== // Функция вызывается при перетаскивании //============================================================== void BaseNode::move(const QPoint &topLeft) { if (isMoving_) { setUndo(true); setTopLeft(topLeft); setUndo(false); } } //============================================================== // Завершение перемещения //============================================================== void BaseNode::endMove(const QPoint &topLeft) { if (isMoving_) { // Формирование операции отмены Q_ASSERT(undoStack_ != nullptr); const QPoint oldPos = movingBeginPos_; const auto undoCmd = new UndoChangeObjectPropValue{this, "topLeft", topLeft, oldPos}; undoStack_->push(undoCmd); isMoving_ = false; movingBeginPos_ = QPoint{}; } } //============================================================== // Начало изменения размера //============================================================== void BaseNode::beginResize() { isResizing_ = true; oldSize_ = size(); } //============================================================== // Функця вызывается при изменении размера //============================================================== void BaseNode::resizing(const QSize &size) { if (isResizing_) { setUndo(true); setSize(size); setUndo(false); } } //============================================================== // Завершение изменения размера //============================================================== void BaseNode::endResize(const QSize &size) { if (isResizing_) { width_ = correctWidth(size.width()); height_ = correctHeight(size.height()); if (QSize{width_, height_} != oldSize_) { // Формирование операции отмены Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "size", QSize{width_, height_}, oldSize_}; undoStack_->push(undoCmd); } isResizing_ = false; } } //============================================================== // Получение входных пинов //============================================================== QVector<ShPtrInputPin> BaseNode::inputPins() { return QVector<ShPtrInputPin>{}; } //============================================================== // Получение входных пинов (константный вариант) //============================================================== QVector<ShPtrConstInputPin> BaseNode::inputPins() const { return QVector<ShPtrConstInputPin>{}; } //============================================================== // Получение областей входных пинов (константный вариант) //============================================================== QVector<QRect> BaseNode::inputPinRects() const { const QSet<int> possiblePinCount = {0, 1, 2}; const int pinCount = inputPinCount(); Q_ASSERT(possiblePinCount.contains(pinCount)); const int pinWidth = BasePin::width(); const int pinHeight = BasePin::height(); const int pinTop = top() - pinHeight / 2; const int centerX = left() + width() / 2; QVector<QRect> rects{}; // Два пина if (pinCount == 2) { const int pinLeft = centerX - width() / 4 - pinWidth / 2 + 4; rects << QRect{pinLeft, pinTop, pinWidth, pinHeight}; const int pinRight = centerX + width() / 4 - pinWidth / 2 - 4; rects << QRect{pinRight, pinTop, pinWidth, pinHeight}; return rects; } // Один пин else if (pinCount == 1) { const int pinLeft = center().x() - pinWidth / 2; rects << QRect{pinLeft, pinTop, pinWidth, pinHeight}; return rects; } // Нет пинов else { return QVector<QRect>{}; } } //============================================================== // Получение входного пина по индексу //============================================================== ShPtrInputPin BaseNode::inputPin(int index) { if (inputPinCount() != 0) { Q_ASSERT((index >= 0) && (index < inputPinCount())); return inputPins().at(index); } else { return nullptr; } } //============================================================== // Получение входного пина по индексу (константный вариант) //============================================================== ShPtrConstInputPin BaseNode::inputPin(int index) const { if (inputPinCount() != 0) { Q_ASSERT((index >= 0) && (index < inputPinCount())); return inputPins().at(index); } else { return nullptr; } } //============================================================== // Получение области входного пина по индексу //============================================================== QRect BaseNode::inputPinRect(int index) const { if (inputPinCount() != 0) { return inputPinRects().at(index); } else { return QRect{}; } } //============================================================== // Проверка наличия входного пина //============================================================== bool BaseNode::containsInputPin(const ShPtrInputPin &pin) const { return inputPins().contains(pin); } //============================================================== // Провека наличия выходного пина (константный вариант) //============================================================== bool BaseNode::containsInputPin(const InputPin *pin) const { const QVector<ShPtrConstInputPin> pins = inputPins(); return find(pins.cbegin(), pins.cend(), pin) != pins.cend(); } //============================================================== // Получение индекса входного пина //============================================================== int BaseNode::indexOfInputPin(const ShPtrInputPin &pin) const { return inputPins().indexOf(pin); } //============================================================== // Получение индекса входного пина (сырой указатель) //============================================================== int BaseNode::indexOfInputPin(const InputPin *pin) const { const QVector<ShPtrConstInputPin> pins = inputPins(); const auto it = find(pins.cbegin(), pins.cend(), pin); if (it != pins.cend()) { return distance(pins.begin(), it); } else { return -1; } } //============================================================== // Получение выходных пинов //============================================================== QVector<ShPtrOutputPin> BaseNode::outputPins() { return QVector<ShPtrOutputPin>{}; } //============================================================== // Получение выходных пинов (константный вариант) //============================================================== QVector<ShPtrConstOutputPin> BaseNode::outputPins() const { return QVector<QSharedPointer<const OutputPin>>{}; } //============================================================== // Получение областей выходных пинов //============================================================== QVector<QRect> BaseNode::ouputPinRects() const { const int pinCount = outputPinCount(); Q_ASSERT((pinCount == 0) || (pinCount == 1)); if (pinCount == 1) { const int pinWidth = BasePin::width(); const int pinHeight = BasePin::height(); const int pinLeft = center().x() - pinWidth / 2; const int pinTop = bottom() - pinHeight / 2; const QRect pinRect{pinLeft, pinTop, pinWidth, pinHeight}; return QVector<QRect>{pinRect}; } else { return QVector<QRect>{}; } } //============================================================== // Получение выходного пина по индексу //============================================================== ShPtrOutputPin BaseNode::outputPin(int index) { if (outputPinCount() != 0) { Q_ASSERT((index >= 0) && (index < outputPinCount())); return outputPins().at(index); } else { return nullptr; } } //============================================================== // Получение выходного пина по индексу (константный вариант) //============================================================== ShPtrConstOutputPin BaseNode::outputPin(int index) const { if (outputPinCount() != 0) { Q_ASSERT((index >= 0) && (index < outputPinCount())); return outputPins().at(index); } else { return nullptr; } } //============================================================== // Получение области выходного пина по индексу //============================================================== QRect BaseNode::outputPinRect(int index) const { if (outputPinCount() != 0) { Q_ASSERT((index >= 0) && (index < outputPinCount())); return ouputPinRects().at(index); } else { return QRect{}; } } //============================================================== // Проверка наличия выходного пина //============================================================== bool BaseNode::containsOutputPin(const ShPtrOutputPin &pin) const { return outputPins().contains(pin); } //============================================================== // Проверка наличия выходного пина (сырой указатель) //============================================================== bool BaseNode::containsOutputPin(const OutputPin *pin) const { const QVector<ShPtrConstOutputPin> pins = outputPins(); return find(pins.cbegin(), pins.cend(), pin) != pins.cend(); } //============================================================== // Получение индекса выходного пина //============================================================== int BaseNode::indexOfOutputPin(const ShPtrOutputPin &pin) const { return outputPins().indexOf(pin); } //============================================================== // Получение индекса выходного пина (сырой указатель) //============================================================== int BaseNode::indexOfOutputPin(const OutputPin *pin) const { const QVector<ShPtrConstOutputPin> pins = outputPins(); const auto it = find(pins.cbegin(), pins.cend(), pin); if (it != pins.cend()) { return distance(pins.begin(), it); } else { return -1; } } //============================================================== // Задание признака выделения //============================================================== void BaseNode::setSelected(bool selected) { if (isSelected_ != selected) { isSelected_ = selected; } } //============================================================== // Сброс признака выделения //============================================================== void BaseNode::resetSelected() { setSelected(false); } //============================================================== // Получение области маркера изменения размера //============================================================== QRect BaseNode::resizebleMarkerRect() const { if (!isResizeble()) { return QRect{}; } constexpr int size = resizebleMarkerSize_ + 2; return QRect{right() - size / 2, bottom() - size / 2, size, size}; } //============================================================== // Получение области с состоянием //============================================================== QRect BaseNode::stateAreaRect() const { if (stateInfo().isSuccess()) { return QRect{}; } return QRect{right() - 8, top() - 8, 16, 16}; } //============================================================== // Задание размеров символа //============================================================== void BaseNode::setCharSize(const QSize &size) { if (charSize_ != size) { charSize_ = size; } } //============================================================== // Задание признака комментариев узлов //============================================================== void BaseNode::setCommentsVisible(bool isVisible) { if (isCommentsVisible_ != isVisible) { isCommentsVisible_ = isVisible; } } //============================================================== // Рисование примитивной основы //============================================================== void BaseNode::drawSimpleBody(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); // Вывод основы QPen pen{currentBorderColor(theme), 2}; pen.setJoinStyle(Qt::MiterJoin); painter->setPen(pen); painter->setBrush(Colors::nodeBack(theme)); painter->drawRect(rect()); // Обводка основы (для того чтобы узел хорошо выглядел на любом фоне) painter->setPen(Colors::nodeBack(theme)); painter->setBrush(Qt::transparent); painter->drawRect(rect().adjusted(-2, -2, 1, 1)); // Вывод типа painter->setPen(Colors::nodeText(theme)); if (!isBypass_) { painter->drawText(rect(), Qt::AlignCenter, strType()); } else { QFont font = painter->font(); font.setStrikeOut(true); painter->setFont(font); painter->drawText(rect(), Qt::AlignCenter, strType()); font.setStrikeOut(false); painter->setFont(font); } } //============================================================== // Рисование входных пинов //============================================================== void BaseNode::drawInputPins(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); const QVector<ShPtrConstInputPin> pins = inputPins(); for (const ShPtrConstInputPin &pin: pins) { drawPin(painter, pin, theme); } } //============================================================== // Рисование выходных пинов //============================================================== void BaseNode::drawOutputPins(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); const QVector<ShPtrConstOutputPin> pins = outputPins(); for (const ShPtrConstOutputPin &pin: pins) { drawPin(painter, pin, theme); } } //============================================================== // Рисование всех пинов //============================================================== void BaseNode::drawPins(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); drawInputPins(painter, theme); drawOutputPins(painter, theme); } //============================================================== // Рисование области состояния //============================================================== void BaseNode::drawStateArea(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); const NodeStates s = stateInfo().state(); switch (s) { case NodeStates::Warning: { drawWarningStateArea(painter, theme); break; } case NodeStates::Error: { drawErrorStateArea(painter, theme); break; } default: { qDebug() << "BaseNode::drawStateArea: bad type of state area"; break; } } } //============================================================== // Рисование маркера изменения размера //============================================================== void BaseNode::drawResizebleMarker(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); // Вывод маркера const QColor color = currentBorderColor(theme); constexpr int halfSizeMarker = resizebleMarkerSize_ / 2; painter->fillRect(right() - halfSizeMarker, bottom() - halfSizeMarker, resizebleMarkerSize_, resizebleMarkerSize_, color); // Дополнительная обводка маркера (для того, чтобы маркер хорошо смотрелся на любом фоне) painter->setPen(Colors::nodeBack(theme)); painter->setBrush(Qt::transparent); painter->drawRect(right() - halfSizeMarker - 1, bottom() - halfSizeMarker - 1, resizebleMarkerSize_ + 1, resizebleMarkerSize_ + 1); } //============================================================== // Вывод подсветки //============================================================== void BaseNode::drawHighlight(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); if (isHighlight_) { const QRect rect{left() - 7, top() - 7, width() + 14, height() + 14}; constexpr QColor color{255, 165, 0, 80}; painter->fillRect(rect, color); } } //============================================================== // Получение текущего цвета границы //============================================================== QColor BaseNode::currentBorderColor(ColorThemes theme) const { return !isSelected() ? Colors::nodeBorder(theme) : Colors::nodeSelectBorder(theme); } //============================================================== // Функция вызывается при срабатывании таймера //============================================================== void BaseNode::timerEvent(QTimerEvent *event) { if (highlightTimerId_ == event->timerId()) { isHighlight_ = false; emit sigUpdate(); killTimer(highlightTimerId_); highlightTimerId_ = 0; } } //============================================================== // Рисование пина //============================================================== void BaseNode::drawPin(QPainter *painter, const ShPtrConstBasePin &pin, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(pin != nullptr); // Вывод пина const QRect pinRect = pin->rect(); QPen pen{currentBorderColor(theme), 2}; pen.setJoinStyle(Qt::MiterJoin); painter->setPen(pen); const QColor pinBackColor = pin->isConnected() ? Colors::nodeConnectPinBack(theme) : Colors::nodePinBack(theme); painter->setBrush(pinBackColor); painter->drawRect(pinRect); // Обводка пина затем, чтобы он хорошо смотрелся на любом фоне painter->setPen(pinBackColor); painter->setBrush(Qt::transparent); painter->drawRect(pinRect.left() - 2, pinRect.top() - 2, pinRect.width() + 3, pinRect.height() + 3); } //============================================================== // Рисование состояния ошибки //============================================================== void BaseNode::drawErrorStateArea(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setPen({Colors::nodeErrStateAreaBorder(theme), 2.0}); painter->setBrush(Colors::nodeErrStateAreaBack(theme)); const int centerX = right(); const int centerY = top(); const int radius = 8; painter->drawEllipse(QPoint{centerX, centerY}, radius, radius); // Вывод восклицательного знака painter->setPen(Qt::black); QFont font = painter->font(); font.setBold(true); painter->setFont(font); const QRect textRect{centerX - radius, centerY - radius, 2 * radius, 2 * radius}; painter->drawText(textRect, Qt::AlignCenter, "!"); painter->restore(); } //============================================================== // Рисование состояния предупреждения //============================================================== void BaseNode::drawWarningStateArea(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setPen({Colors::nodeWarnStateAreaBorder(theme), 2.0}); painter->setBrush(Colors::nodeWarnStateAreaBack(theme)); const int centerX = right(); const int centerY = top(); const int radius = 8; painter->drawEllipse(QPoint{centerX, centerY}, radius, radius); // Вывод восклицательного знака painter->setPen(Qt::black); QFont font = painter->font(); font.setBold(true); painter->setFont(font); const QRect textRect{centerX - radius, centerY - radius, 2 * radius, 2 * radius}; painter->drawText(textRect, Qt::AlignCenter, "!"); painter->restore(); } //============================================================== // Корректировка ширины //============================================================== int BaseNode::correctWidth(int width) const { if (width < minWidth()) { return minWidth(); } else if (width > maxWidth()) { return maxWidth(); } else { return width; } } //============================================================== // Корректировка высоты //============================================================== int BaseNode::correctHeight(int height) const { if (height < minHeight()) { return minHeight(); } else if (height > maxHeight()) { return maxHeight(); } else { return height; } } //============================================================== // Запуск/перезапуск таймера подсветки узла //============================================================== void BaseNode::startHighlightTimer() { if (highlightTimerId_ != 0) { killTimer(highlightTimerId_); } isHighlight_ = true; highlightTimerId_ = startTimer(300); emit sigUpdate(); } //============================================================== // Чтение имени из XML //============================================================== QString BaseNode::readNameFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "name"; // Получение узла const QDomElement elemName = elem.firstChildElement(propName); if (elemName.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strName = readValueFromXml(elemName); if (!strName) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения const optional<QString> name = strFromXmlFormat(strName.value()); if (!name) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return name.value(); } //============================================================== // Чтение смещения слева из XML //============================================================== qint32 BaseNode::readLeftFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "left"; // Получение узла const QDomElement elemLeft = elem.firstChildElement(propName); if (elemLeft.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strLeft = readValueFromXml(elemLeft); if (!strLeft) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения bool ok = false; const qint32 left = strLeft.value().toInt(&ok); if (!ok) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return left; } //============================================================== // Чтение смещения сверху из XML //============================================================== qint32 BaseNode::readTopFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "top"; // Получение узла const QDomElement elemTop = elem.firstChildElement(propName); if (elemTop.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strTop = readValueFromXml(elemTop); if (!strTop) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения bool ok = false; const qint32 top = strTop.value().toInt(&ok); if (!ok) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return top; } //============================================================== // Чтение ширины из XML //============================================================== qint32 BaseNode::readWidthFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "width"; // Получение узла const QDomElement elemWidth = elem.firstChildElement(propName); if (elemWidth.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strWidth = readValueFromXml(elemWidth); if (!strWidth) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения bool ok = false; const qint32 width = strWidth.value().toInt(&ok); if (!ok) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return width; } //============================================================== // Чтение высоты из XML //============================================================== qint32 BaseNode::readHeightFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "height"; // Получение узла const QDomElement elemHeight = elem.firstChildElement(propName); if (elemHeight.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strHeight = readValueFromXml(elemHeight); if (!strHeight) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения bool ok = false; const int height = strHeight.value().toInt(&ok); if (!ok) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return height; } //============================================================== // Чтение признака пропуска из XML //============================================================== bool BaseNode::readBypassFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "bypass"; // Получение узла const QDomElement elemBypass = elem.firstChildElement(propName); if (elemBypass.isNull()) { const QString msg = tr("Not find property \"%1\" of type %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> strBypass = readValueFromXml(elemBypass); if (!strBypass) { const QString msg = tr("Not read property \"%1\" of type %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения const optional<bool> bypass = strToBool(strBypass.value()); if (!bypass) { const QString msg = tr("Bad value of property \"%1\" of type %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return bypass.value(); } //============================================================== // Чтение комментария из XML //============================================================== QString BaseNode::readCommentFromXml(const QDomElement &elem) const { Q_ASSERT(!elem.isNull()); const QString propName = "comment"; // Получение узла const QDomElement elemComment = elem.firstChildElement(propName); if (elemComment.isNull()) { const QString msg = tr("Not find property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения в виде строки const optional<QString> xmlComment = readValueFromXml(elemComment); if (!xmlComment) { const QString msg = tr("Not read property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения const optional<QString> comment = strFromXmlFormat(xmlComment.value()); if (!comment) { const QString msg = tr("Bad value of property \"%1\" of node %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return comment.value(); } //============================================================== // Запись имени в XML //============================================================== void BaseNode::writeNameToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemName = doc.createElement("name"); const QString strName = strToXmlFormat(name()); writeValueToXml(doc, elemName, strName); elem.appendChild(elemName); } //============================================================== // Запись типа в XML //============================================================== void BaseNode::writeTypeToXml(QDomDocument &, QDomElement &elem) const { elem.setAttribute("type", strType()); } //============================================================== // Запись идентификатора в XML //============================================================== void BaseNode::writeIdToXml(QDomDocument &, QDomElement &elem) const { Q_ASSERT(!elem.isNull()); elem.setAttribute("id", id()); } //============================================================== // Запись смещения слева в XML //============================================================== void BaseNode::writeLeftToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemLeft = doc.createElement("left"); const QString strLeft = QString::number(left()); writeValueToXml(doc, elemLeft, strLeft); elem.appendChild(elemLeft); } //============================================================== // Запись смещения сверху в XML //============================================================== void BaseNode::writeTopToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemTop = doc.createElement("top"); const QString strTop = QString::number(top()); writeValueToXml(doc, elemTop, strTop); elem.appendChild(elemTop); } //============================================================== // Запись ширины в XML //============================================================== void BaseNode::writeWidthToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemTop = doc.createElement("width"); const QString strTop = QString::number(width()); writeValueToXml(doc, elemTop, strTop); elem.appendChild(elemTop); } //============================================================== // Запись высоты в XML //============================================================== void BaseNode::writeHeightToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemTop = doc.createElement("height"); const QString strTop = QString::number(height()); writeValueToXml(doc, elemTop, strTop); elem.appendChild(elemTop); } //============================================================== // Запись признака пропуска в XML //============================================================== void BaseNode::writeBypassToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemBypass = doc.createElement("bypass"); const QString strBypass = boolToStr(isBypass_); writeValueToXml(doc, elemBypass, strBypass); elem.appendChild(elemBypass); } //============================================================== // Запись комментария в XML //============================================================== void BaseNode::writeCommentToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); QDomElement elemLeft = doc.createElement("comment"); const QString xmlComment = strToXmlFormat(comment_); writeValueToXml(doc, elemLeft, xmlComment); elem.appendChild(elemLeft); } //////////////////////////////////////////////////////////////// // Реализация функций //////////////////////////////////////////////////////////////// //============================================================== // Функция подключения пинов //============================================================== void connectPins(ShPtrOutputPin &outPin, ShPtrInputPin &inPin, bool isRaiseSignal) { Q_ASSERT(outPin != nullptr); Q_ASSERT(inPin != nullptr); Q_ASSERT(!outPin->containsInputPin(inPin)); Q_ASSERT(inPin->outputPin() == nullptr); inPin->setOutputPin(outPin, isRaiseSignal); outPin->addInputPin(inPin, isRaiseSignal); } //============================================================== // Функция отключения пинов //============================================================== void disconnectPins(ShPtrOutputPin &outPin, ShPtrInputPin &inPin, bool isRaiseSignal) { Q_ASSERT(outPin != nullptr); Q_ASSERT(inPin != nullptr); Q_ASSERT(outPin->containsInputPin(inPin)); Q_ASSERT(inPin->outputPin() == outPin); inPin->setOutputPin(nullptr, isRaiseSignal); outPin->removeInputPin(inPin, isRaiseSignal); } //============================================================== // Получение признака ошибки в родителе узла //============================================================== bool isErrorInParent(const ShPtrBaseNode &node) { Q_ASSERT(node != nullptr); for (int i = 0; i < node->inputPinCount(); ++i) { const ShPtrInputPin pin = node->inputPin(i); if (pin->isConnected()) { const BaseNode *const parentNode = pin->outputPin()->parentNode(); if (parentNode->stateInfo().isError()) { return true; } } } return false; } //============================================================== // Получение признака ошибки в родителе узла (константный вариант) //============================================================== bool isErrorInParent(const ShPtrConstBaseNode &node) { Q_ASSERT(node != nullptr); for (int i = 0; i < node->inputPinCount(); ++i) { const ShPtrConstInputPin pin = node->inputPin(i); if (pin->isConnected()) { const BaseNode *const parentNode = pin->outputPin()->parentNode(); if (parentNode->stateInfo().isError()) { return true; } } } return false; } //============================================================== // Получение признака ошибки в родителе узла (сырые указатели) //============================================================== bool isErrorInParent(BaseNode *node) { Q_ASSERT(node != nullptr); for (int i = 0; i < node->inputPinCount(); ++i) { const ShPtrInputPin pin = node->inputPin(i); if (pin->isConnected()) { const BaseNode *const parentNode = pin->outputPin()->parentNode(); if (parentNode->stateInfo().isError()) { return true; } } } return false; } //============================================================== // Получение признака ошибки в родителе узла (сырые указатели) // (константный вариант) //============================================================== bool isErrorInParent(const BaseNode *node) { Q_ASSERT(node != nullptr); for (int i = 0; i < node->inputPinCount(); ++i) { const ShPtrConstInputPin pin = node->inputPin(i); if (pin->isConnected()) { const BaseNode *const parentNode = pin->outputPin()->parentNode(); if (parentNode->stateInfo().isError()) { return true; } } } return false; }