/
pi_coder
/
ByteMachine
Обзор
Документация
Войти
/
pi_coder
/
ByteMachine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/nodes/input_file_node.cpp
411 строк
13 KB
pimenov-and
Перенос функциональности из старого проекта, в основном это описания узлов
19 июл 2026, 20:13
19 июл 2026, 20:13
cb3c51b
Код
Авторство
О чём код?
//////////////////////////////////////////////////////////////// // ByteMachine // Узел для чтения данных из файла //////////////////////////////////////////////////////////////// #include "input_file_node.h" #include "node_name_manager.h" #include "colors.h" #include "exceptions/base_exception.h" #include "xml_helper.h" #include <QPainter> #include <QDomDocument> #include <QFileInfo> //============================================================== using std::size_t; using std::optional; //============================================================== // Конструктор с параметрами //============================================================== InputFileNode::InputFileNode(QUndoStack *undoStack, QObject *parent) : BaseNode{undoStack, parent} { name_ = nodeNameManager()->addName("inFile"); Q_ASSERT(!name_.isEmpty()); createOutputPin(); setConnections(); InputFileNode::updateStateInfo(); } //============================================================== // Чтение из XML //============================================================== void InputFileNode::readFromXml(const QDomElement &elem) { Q_ASSERT(!elem.isNull()); const QString name = readNameFromXml(elem); const qint32 left = readLeftFromXml(elem); const qint32 top = readTopFromXml(elem); const QString filePath = readFilePathFromXml(elem); const QString comment = readCommentFromXml(elem); setName(name); setLeft(left); setTop(top); setFilePath(filePath); setComment(comment); } //============================================================== // Запись в XML //============================================================== void InputFileNode::writeToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); writeIdToXml(doc, elem); writeNameToXml(doc, elem); writeLeftToXml(doc, elem); writeTopToXml(doc, elem); writeFilePathToXml(doc, elem); writeCommentToXml(doc, elem); } //============================================================== // Функция перерисовки //============================================================== void InputFileNode::draw(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); drawHighlight(painter, theme); drawSimpleBody(painter, theme); drawOutputPins(painter, theme); drawStateArea(painter, theme); drawComments(painter, theme); } //============================================================== // Получение копии узла //============================================================== ShPtrBaseNode InputFileNode::clone() const { const auto cloneNode = ShPtrInputFileNode::create( undoStack(), parent()); cloneNode->setUndo(true); cloneNode->setName(name()); cloneNode->setTopLeft(topLeft()); cloneNode->setFilePath(filePath()); cloneNode->setComment(comment()); cloneNode->setUndo(true); return cloneNode; } //============================================================== // Получение текста подсказки //============================================================== QString InputFileNode::tooltipText() const { QString text{}; text += QString{"%1: \"%2\"\n"}.arg(tr("Name"), name()); text += QString{"%1: \"%2\"\n"}.arg(tr("File path"), filePath()); text += QString{"%1: \"%2\""}.arg(tr("Comment"), comment()); return text; } //============================================================== // Получение размера данных //============================================================== size_t InputFileNode::dataSize() const { if (stateInfo_.isError()) { return 0; } return static_cast<size_t>(file_.size()); } //============================================================== // Получение байта данных //============================================================== quint8 InputFileNode::dataByte(size_t index) const { Q_ASSERT_X(index < dataSize(), "Check index", qPrintable(QString{"index: %1, dataSize: %2"}.arg(index).arg(dataSize()))); if (!file_.seek(index)) { return 0; } quint8 byte = 0; if (file_.read(reinterpret_cast<char*>(&byte), 1) != 1) { return 0; } return byte; } //============================================================== // Получение блока данных //============================================================== ByteList InputFileNode::dataBlock(size_t index, size_t count) const { Q_ASSERT_X(index + count <= dataSize(), "Check index", qPrintable(QString{"index: %1, dataSize: %2"}.arg(index).arg(dataSize()))); if (!file_.seek(index)) { return ByteList{}; } QVector<quint8> block(count); if (file_.read(reinterpret_cast<char*>(block.data()), count) != count) { return ByteList{}; } return ByteList(block.cbegin(), block.cend()); } //============================================================== // Функция вызывается при изменении данных //============================================================== void InputFileNode::dataChanged() { startHighlightTimer(); updateStateInfo(); outputPin_->dataChanged(); emit sigUpdate(); } //============================================================== // Получение выходных пинов //============================================================== QVector<ShPtrOutputPin> InputFileNode::outputPins() { return QVector<ShPtrOutputPin>{outputPin_}; } //============================================================== // Получение выходных пинов (константный вариант) //============================================================== QVector<ShPtrConstOutputPin> InputFileNode::outputPins() const { return QVector<ShPtrConstOutputPin>{outputPin_}; } //============================================================== // Задание пути к файлу //============================================================== void InputFileNode::setFilePath(const QString &path) { if (filePath() != path) { if (file_.isOpen()) { file_.close(); } // const QString oldPath = filePath(); file_.setFileName(path); file_.open(QIODevice::ReadOnly); const PropValue value{"filePath", filePath()}; emit sigChangedProp(value); dataChanged(); fileWatcher_.addPath(path); } } //============================================================== // Сброс пути к файлу //============================================================== void InputFileNode::resetFilePath() { setFilePath(QString{}); } //============================================================== // Функция получения имени свойства для графического // интерфейса по его системному имени //============================================================== QString InputFileNode::getUiPropertyName(const QString &systemName) { const QMap<QString, QString> map { {"name", tr("Name")}, {"left", tr("Left")}, {"top", tr("Top")}, {"width", tr("Width")}, {"height", tr("Height")}, {"topLeft", tr("Top and left")}, {"size", tr("Size")}, {"comment", tr("Comment")}, {"filePath", tr("File path")} }; return map.value(systemName, tr("Unknown")); } //============================================================== // Функция перевода //============================================================== void InputFileNode::retranslate() { updateStateInfo(); } //============================================================== // Функция вызывается при подключении выходного пина //============================================================== void InputFileNode::slotOutputPinConnectChanged(ConnectStates state, InputPin *pin) { Q_ASSERT(!isUnknown(state)); Q_ASSERT(pin != nullptr); dataChanged(); const int conNodeId = pin->parentNode()->id(); const int conPinIndex = pin->parentNode()->indexOfInputPin(pin); const bool isCon = ::isConnect(state); emit sigChangedConnect(0, conNodeId, conPinIndex, isCon); } //============================================================== // Функция вызывается при изменении файла //============================================================== void InputFileNode::slotFileChanged() { dataChanged(); } //============================================================== // Задание соединений //============================================================== void InputFileNode::setConnections() { connect(&fileWatcher_, &QFileSystemWatcher::fileChanged, this, &InputFileNode::dataChanged); } //============================================================== // Создание выходного пина //============================================================== void InputFileNode::createOutputPin() { outputPin_ = ShPtrOutputPin::create(this, 0); connect(outputPin_.get(), &OutputPin::sigConnectChanged, this, &InputFileNode::slotOutputPinConnectChanged); } //============================================================== // Вывод комментариев //============================================================== void InputFileNode::drawComments(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); if (isCommentsVisible()) { QString comments{}; comments += QString{" <<< %1: \"%2\"\n"}.arg(tr("Name"), name()); comments += QString{" %1: \"%2\"\n"}.arg(tr("File path"), filePath()); comments += QString{" %1: \"%2\"\n"}.arg(tr("Comment"), comment()); #ifdef QT_DEBUG comments += " -\n"; comments += QString{" %1: %2\n"}.arg("Id").arg(id()); #endif // QT_DEBUG const int commentsLeft = right(); const int commentsTop = top() + (height() - charHeight()) / 2; const int commentsFlags = Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip; const QRect commentsRect{commentsLeft, commentsTop, 0, 0}; painter->setPen(Colors::nodeText(theme)); painter->drawText(commentsRect, commentsFlags, comments); } } //============================================================== // Чтение пути к файлу из XML //============================================================== QString InputFileNode::readFilePathFromXml(const QDomElement &elem) const { const QString propName = "filePath"; // Получение узла const QDomElement elemFilePath = elem.firstChildElement(propName); if (elemFilePath.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> xmlPath = readValueFromXml(elemFilePath); if (!xmlPath) { const QString msg = tr("Not read property \"%1\" of type %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } // Получение значения const optional<QString> path = strFromXmlFormat(xmlPath.value()); if (!path) { const QString msg = tr("Bad value of property \"%1\" of type %2 with id %3"). arg(propName, strType()).arg(id()); throw BaseException{msg}; } return path.value(); } //============================================================== // Запись пути к файлу в XML //============================================================== void InputFileNode::writeFilePathToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); } //============================================================== // Обновление состояния узла //============================================================== void InputFileNode::updateStateInfo() { const NodeStateInfo oldStateInfo = stateInfo_; if (filePath().isEmpty()) { const QString msg = tr("File path is not set"); stateInfo_ = NodeStateInfo{NodeStates::Error, msg}; } else if (!file_.isOpen()) { const QString msg = tr("Failed to open file"); stateInfo_ = NodeStateInfo{NodeStates::Error, msg}; } else { stateInfo_ = NodeStateInfo{NodeStates::Success, QString{}}; } if (stateInfo_ != oldStateInfo) { emit sigChangedState(stateInfo_); } }