From 868d4fdc8e4d336772f7161009b50356b0ea8a1e Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Sun, 14 May 2023 23:02:06 +0200 Subject: [PATCH] switched to nlohmann Json --- story-editor/src/main_window.cpp | 99 +----------------- story-editor/src/media_node_model.cpp | 34 +++---- story-editor/src/media_node_model.h | 10 +- story-editor/src/story_graph_model.cpp | 136 +++++++++++++++---------- story-editor/src/story_graph_model.h | 20 ++-- story-editor/src/story_node_base.h | 14 ++- story-editor/src/story_project.cpp | 76 +++++++++++--- story-editor/src/story_project.h | 9 +- 8 files changed, 198 insertions(+), 200 deletions(-) diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 6a3a43d..5ec8187 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -556,71 +556,15 @@ void MainWindow::OpenProjectDialog() void MainWindow::OpenProject(const QString &filePath) { bool success = false; - QString errorMsg; + QString errorMsg = tr("General error"); m_project.Initialize(filePath.toStdString()); - QFile loadFile(m_project.GetProjectFilePath().c_str()); + nlohmann::json model; - if (loadFile.open(QIODevice::ReadOnly)) - { - QJsonParseError err; - QJsonDocument loadDoc = QJsonDocument::fromJson(loadFile.readAll(), &err); - - if (err.error == QJsonParseError::NoError) - { - QJsonObject projectRoot = loadDoc.object(); - - if (projectRoot.contains("project")) - { - QJsonObject projectData = projectRoot["project"].toObject(); - - m_project.SetName(projectData["name"].toString().toStdString()); - m_project.SetUuid(projectData["uuid"].toString().toStdString()); - - QJsonArray resourcesData = projectData["resources"].toArray(); - - for (const auto &r : resourcesData) - { - Resource rData; - QJsonObject obj = r.toObject(); - rData.type = obj["type"].toString().toStdString(); - rData.format = obj["format"].toString().toStdString(); - rData.description = obj["description"].toString().toStdString(); - rData.file = obj["file"].toString().toStdString(); - m_resourcesDock->Append(rData); - } - - - if (projectRoot.contains("nodegraph")) - { - QJsonObject nodeData = projectRoot["nodegraph"].toObject(); - m_model.load(nodeData); - - success = true; - } - else - { - errorMsg = tr("Missing nodegraph section in JSON file."); - } - } - else - { - errorMsg = tr("Missing project section in JSON file."); - } - } - else - { - errorMsg = err.errorString(); - } - } - else - { - errorMsg = tr("Could not open project file."); - } - - if (success) + if (m_project.Load(filePath.toStdString(), model)) { + m_model.Load(model); EnableProject(); } else @@ -633,41 +577,8 @@ void MainWindow::OpenProject(const QString &filePath) void MainWindow::SaveProject() { - QJsonObject jsonModel = m_model.save(); + // QJsonObject jsonModel = m_model.save(); - QJsonObject projectData; - projectData["name"] = m_project.GetName().c_str(); - projectData["uuid"] = m_project.GetUuid().c_str(); - - QJsonArray resourcesData; - - for (std::vector::const_iterator it = m_project.Begin(); it != m_project.End(); ++it) - { - auto &r = *it; - QJsonObject obj; - obj["type"] = r.type.c_str(); - obj["format"] = r.format.c_str(); - obj["description"] = r.description.c_str(); - obj["file"] = r.file.c_str(); - - resourcesData.append(obj); - } - projectData["resources"] = resourcesData; - - QJsonObject saveData; - saveData["project"] = projectData; - saveData["nodegraph"] = jsonModel; - - QJsonDocument doc(saveData); - qDebug() << doc.toJson(); - - QFile f(m_project.GetProjectFilePath().c_str()); - - if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) - return; - - QTextStream fout(&f); - fout << doc.toJson(); statusBar()->showMessage(tr("Saved '%1'").arg(m_project.GetProjectFilePath().c_str()), 2000); } diff --git a/story-editor/src/media_node_model.cpp b/story-editor/src/media_node_model.cpp index 8a09c88..fa39887 100644 --- a/story-editor/src/media_node_model.cpp +++ b/story-editor/src/media_node_model.cpp @@ -9,6 +9,7 @@ #include #include + MediaNodeModel::MediaNodeModel(StoryGraphModel &model) : m_model(model) , m_widget(new QWidget()) @@ -46,27 +47,25 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model) }); } -QJsonObject MediaNodeModel::save() const +nlohmann::json MediaNodeModel::ToJson() const { - QJsonObject obj = NodeDelegateModel::save(); - + // Always start with generic + nlohmann::json j = StoryNodeBase::ToJson(); // Merge two objects - QVariantMap map = obj.toVariantMap(); - map.insert(m_mediaData); - - return QJsonObject::fromVariantMap(map); + j.merge_patch(m_mediaData); + return j; } -void MediaNodeModel::load(const QJsonObject &mediaData) +void MediaNodeModel::FromJson(nlohmann::json &j) { - m_mediaData = mediaData.toVariantMap(); + m_mediaData = j; // Display loaded image - QString imagePath = m_mediaData["image"].toString(); + std::string imagePath = m_mediaData["image"].get(); - if (!imagePath.isEmpty()) + if (imagePath.size() > 0) { - setImage(imagePath); + setImage(imagePath.c_str()); } } @@ -76,7 +75,7 @@ void MediaNodeModel::setImage(const QString &imagePath) if (pix.isNull()) { - std::cout << "!!!!!!! " << m_mediaData["image"].toString().toStdString() << std::endl; + std::cout << "!!!!!!! " << m_mediaData["image"].get() << std::endl; } int w = m_ui.image->width(); @@ -85,15 +84,14 @@ void MediaNodeModel::setImage(const QString &imagePath) m_ui.image->setPixmap(pix); } -void MediaNodeModel::setInternalData(const QVariant &value) +void MediaNodeModel::setInternalData(const nlohmann::json &j) { - QJsonObject obj = value.toJsonObject(); - if (obj.contains("image")) { - setImage(obj.value("image").toString()); + if (j.contains("image")) { + setImage(j["image"].get().c_str()); } // Merge new data into local object - m_mediaData.insert(obj.toVariantMap()); + m_mediaData.merge_patch(j); } unsigned int MediaNodeModel::nPorts(PortType portType) const diff --git a/story-editor/src/media_node_model.h b/story-editor/src/media_node_model.h index 97fdc45..6c2dc0d 100644 --- a/story-editor/src/media_node_model.h +++ b/story-editor/src/media_node_model.h @@ -36,11 +36,10 @@ public: QString name() const override { return QString("MediaNode"); } public: - QJsonObject save() const override; + virtual nlohmann::json ToJson() const override; + virtual void FromJson(nlohmann::json &j) override; - void load(QJsonObject const &mediaData) override; - - void setInternalData(const QVariant &value) override; + void setInternalData(const nlohmann::json &j) override; public: virtual QString modelName() const { return QString("MediaNode"); } @@ -57,6 +56,7 @@ public: bool resizable() const override { return true; } + protected: bool eventFilter(QObject *object, QEvent *event) override; @@ -69,6 +69,6 @@ private: Ui::mediaNodeUi m_ui; std::shared_ptr m_nodeData; - QVariantMap m_mediaData; + nlohmann::json m_mediaData; void setImage(const QString &fileName); }; diff --git a/story-editor/src/story_graph_model.cpp b/story-editor/src/story_graph_model.cpp index 28a77b7..ba4d96b 100644 --- a/story-editor/src/story_graph_model.cpp +++ b/story-editor/src/story_graph_model.cpp @@ -176,6 +176,17 @@ QVariant StoryGraphModel::nodeData(NodeId nodeId, NodeRole role) const return result; } +void StoryGraphModel::SetInternalData(NodeId nodeId, nlohmann::json &j) +{ + auto it = _models.find(nodeId); + if (it == _models.end()) + return; + + auto &model = it->second; + + model->setInternalData(j); +} + bool StoryGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) { bool result = false; @@ -212,10 +223,8 @@ bool StoryGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) break; case NodeRole::InternalData: - { - model->setInternalData(value); break; - } + case NodeRole::InPortCount: break; @@ -307,9 +316,63 @@ bool StoryGraphModel::deleteNode(NodeId const nodeId) return true; } -QJsonObject StoryGraphModel::saveNode(NodeId const nodeId) const +namespace QtNodes { + void to_json(nlohmann::json& j, const ConnectionId& p) { + j = nlohmann::json{ + {"outNodeId", static_cast(p.outNodeId)}, + {"outPortIndex", static_cast(p.outPortIndex)}, + {"intNodeId", static_cast(p.inNodeId)}, + {"inPortIndex", static_cast(p.inPortIndex)}, + }; + } + + void from_json(const nlohmann::json& j, ConnectionId& p) { +// j.at("name").get_to(p.name); +// j.at("address").get_to(p.address); +// j.at("age").get_to(p.age); + } +} // namespace QtNodes + + +nlohmann::json StoryGraphModel::Save() const { - QJsonObject nodeJson; + nlohmann::json j; + nlohmann::json nodesJsonArray; + for (auto const nodeId : allNodeIds()) { + nodesJsonArray.push_back(SaveNode(nodeId)); + } + j["nodes"] = nodesJsonArray; + + nlohmann::json connJsonArray; + for (auto const &cid : _connectivity) { + nlohmann::json o = cid; + connJsonArray.push_back(o); + } + j["connections"] = connJsonArray; + + return j; +} + +void StoryGraphModel::Load(const nlohmann::json &j) +{ + nlohmann::json nodesJsonArray = j["nodes"]; + + for (auto& element : nodesJsonArray) { + LoadNode(element); + } + + nlohmann::json connectionJsonArray = j["connections"]; + + for (auto& connection : connectionJsonArray) { + ConnectionId connId = connection.get(); + // Restore the connection + addConnection(connId); + } +} + +nlohmann::json StoryGraphModel::SaveNode(NodeId const nodeId) const +{ + nlohmann::json nodeJson; nodeJson["id"] = static_cast(nodeId); auto it = _models.find(nodeId); @@ -318,43 +381,25 @@ QJsonObject StoryGraphModel::saveNode(NodeId const nodeId) const auto &model = it->second; - nodeJson["internal-data"] = model->save(); + nodeJson["internal-data"] = model->ToJson(); { QPointF const pos = nodeData(nodeId, NodeRole::Position).value(); - QJsonObject posJson; + nlohmann::json posJson; posJson["x"] = pos.x(); posJson["y"] = pos.y(); nodeJson["position"] = posJson; - nodeJson["inPortCount"] = QString::number(nodeData(nodeId, NodeRole::InPortCount).value()); - nodeJson["outPortCount"] = QString::number(nodeData(nodeId, NodeRole::OutPortCount).value()); + nodeJson["inPortCount"] = nodeData(nodeId, NodeRole::InPortCount).value(); + nodeJson["outPortCount"] = nodeData(nodeId, NodeRole::OutPortCount).value(); } return nodeJson; } -QJsonObject StoryGraphModel::save() const -{ - QJsonObject sceneJson; - QJsonArray nodesJsonArray; - for (auto const nodeId : allNodeIds()) { - nodesJsonArray.append(saveNode(nodeId)); - } - sceneJson["nodes"] = nodesJsonArray; - - QJsonArray connJsonArray; - for (auto const &cid : _connectivity) { - connJsonArray.append(QtNodes::toJson(cid)); - } - sceneJson["connections"] = connJsonArray; - - return sceneJson; -} - -void StoryGraphModel::loadNode(QJsonObject const &nodeJson) +void StoryGraphModel::LoadNode(const nlohmann::json &nodeJson) { // Possibility of the id clash when reading it from json and not generating a // new value. @@ -363,17 +408,17 @@ void StoryGraphModel::loadNode(QJsonObject const &nodeJson) // loading. // 2. When undoing the deletion command. Conflict is not possible // because all the new ids were created past the removed nodes. - NodeId restoredNodeId = nodeJson["id"].toInt(); + NodeId restoredNodeId = nodeJson["id"].get(); _nextNodeId = std::max(_nextNodeId, restoredNodeId + 1); - QJsonObject const internalDataJson = nodeJson["internal-data"].toObject(); + nlohmann::json internalDataJson = nodeJson["internal-data"]; - QString delegateModelName = internalDataJson["model-name"].toString(); + std::string delegateModelName = internalDataJson["model-name"].get(); // std::unique_ptr model = _registry->create(delegateModelName); - auto model = createNode(delegateModelName.toStdString()); + auto model = createNode(delegateModelName); if (model) { // connect(model.get(), @@ -389,37 +434,18 @@ void StoryGraphModel::loadNode(QJsonObject const &nodeJson) Q_EMIT nodeCreated(restoredNodeId); - QJsonObject posJson = nodeJson["position"].toObject(); - QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); + nlohmann::json posJson = nodeJson["position"]; + QPointF const pos(posJson["x"].get(), posJson["y"].get()); setNodeData(restoredNodeId, NodeRole::Position, pos); - _models[restoredNodeId]->load(internalDataJson); + _models[restoredNodeId]->FromJson(internalDataJson); } else { - throw std::logic_error(std::string("No registered model with name ") - + delegateModelName.toLocal8Bit().data()); + throw std::logic_error(std::string("No registered model with name ") + delegateModelName); } } -void StoryGraphModel::load(QJsonObject const &jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - for (QJsonValueRef nodeJson : nodesJsonArray) { - loadNode(nodeJson.toObject()); - } - - QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); - - for (QJsonValueRef connection : connectionJsonArray) { - QJsonObject connJson = connection.toObject(); - - ConnectionId connId = QtNodes::fromJson(connJson); - - // Restore the connection - addConnection(connId); - } -} void StoryGraphModel::addPort(NodeId nodeId, PortType portType, PortIndex portIndex) { diff --git a/story-editor/src/story_graph_model.h b/story-editor/src/story_graph_model.h index f84d46a..3df488f 100644 --- a/story-editor/src/story_graph_model.h +++ b/story-editor/src/story_graph_model.h @@ -96,19 +96,6 @@ public: bool deleteNode(NodeId const nodeId) override; - QJsonObject saveNode(NodeId const) const override; - - QJsonObject save() const; - - /// @brief Creates a new node based on the informatoin in `nodeJson`. - /** - * @param nodeJson conains a `NodeId`, node's position, internal node - * information. - */ - void loadNode(QJsonObject const &nodeJson) override; - - void load(QJsonObject const &jsonDocument); - void addPort(NodeId nodeId, PortType portType, PortIndex portIndex); void removePort(NodeId nodeId, PortType portType, PortIndex first); @@ -132,6 +119,13 @@ public: StoryProject &GetProject() { return m_project; }; void Clear(); + void SetInternalData(NodeId nodeId, nlohmann::json &j); + + nlohmann::json Save() const; + void Load(const nlohmann::json &j); + + nlohmann::json SaveNode(NodeId const) const; + void LoadNode(const nlohmann::json &nodeJson); // Creates a new node signals: void sigChooseFile(NodeId id); diff --git a/story-editor/src/story_node_base.h b/story-editor/src/story_node_base.h index 4bf1745..5bd7d02 100644 --- a/story-editor/src/story_node_base.h +++ b/story-editor/src/story_node_base.h @@ -10,6 +10,7 @@ #include #include #include +#include "json.hpp" using QtNodes::NodeData; using QtNodes::NodeDataType; @@ -32,7 +33,18 @@ public: void setNodeId(NodeId id) { m_nodeId = id; } NodeId getNodeId() { return m_nodeId; } - virtual void setInternalData(const QVariant &value) { + virtual nlohmann::json ToJson() const { + nlohmann::json j; + + j["model-name"] = name().toStdString(); + return j; + } + + virtual void FromJson(nlohmann::json &j) { + // default implementation does nothing + } + + virtual void setInternalData(const nlohmann::json &j) { // default impl } diff --git a/story-editor/src/story_project.cpp b/story-editor/src/story_project.cpp index f8f800f..3ac2fe9 100644 --- a/story-editor/src/story_project.cpp +++ b/story-editor/src/story_project.cpp @@ -15,7 +15,7 @@ void StoryProject::New(const std::string &uuid, const std::string &file_path) void StoryProject::Initialize(const std::string &file_path) { - m_project_path = file_path; + m_project_file_path = file_path; std::filesystem::path p(file_path); m_working_dir= p.parent_path(); @@ -33,12 +33,12 @@ void StoryProject::Initialize(const std::string &file_path) m_initialized = true; } -bool StoryProject::Load(const std::string &file_path) +bool StoryProject::Load(const std::string &file_path, nlohmann::json &model) { std::ifstream f(file_path); bool success = false; -/* + std::filesystem::path p(file_path); m_working_dir= p.parent_path(); @@ -49,6 +49,37 @@ bool StoryProject::Load(const std::string &file_path) nlohmann::json j = nlohmann::json::parse(f); m_nodes.clear(); + + + if (j.contains("project")) + { + nlohmann::json projectData = j["project"]; + + m_name = projectData["name"].get(); + m_uuid = projectData["uuid"].get(); + + nlohmann::json resourcesData = projectData["resources"]; + + for (const auto &obj : resourcesData) + { + Resource rData; + + rData.type = obj["type"].get(); + rData.format = obj["format"].get(); + rData.description = obj["description"].get(); + rData.file = obj["file"].get(); + m_resources.push_back(rData); + } + + if (j.contains("nodegraph")) + { + model = j["nodegraph"]; + success = true; + } + } + + /* + if (j.contains("nodes")) { for (auto& element : j["nodes"]) @@ -106,21 +137,42 @@ bool StoryProject::Load(const std::string &file_path) success = true; - - } catch(std::exception &e) +*/ + } + catch(std::exception &e) { std::cout << e.what() << std::endl; } - - if (success) - { - CreateTree(); - } -*/ return success; } +void StoryProject::Save(const nlohmann::json &model) +{ + nlohmann::json j; + j["project"] = { {"name", m_name}, {"uuid", m_uuid} }; + + { + nlohmann::json resourcesData; + + for (auto &r : m_resources) + { + nlohmann::json obj = {{"type", r.type}, + {"format", r.format}, + {"description", r.description}, + {"file", r.file}}; + + resourcesData.push_back(obj); + } + j["resources"] = resourcesData; + } + + j["nodegraph"] = model; + + std::ofstream o(m_project_file_path); + o << std::setw(4) << j << std::endl; +} + void StoryProject::CreateTree() { // Algorithm: level order traversal of N-ary tree @@ -259,7 +311,7 @@ void StoryProject::SetDisplayFormat(int w, int h) std::string StoryProject::GetProjectFilePath() const { - return m_project_path; + return m_project_file_path; } std::string StoryProject::GetWorkingDir() const diff --git a/story-editor/src/story_project.h b/story-editor/src/story_project.h index 57ca8e4..6ee14b4 100644 --- a/story-editor/src/story_project.h +++ b/story-editor/src/story_project.h @@ -4,7 +4,9 @@ #include #include #include +#include "json.hpp" +// FIXME : Structure très Lunii style, à utiliser pour la conversion peut-être ... struct StoryNode { bool auto_jump; @@ -55,7 +57,9 @@ struct StoryProject StoryNode *m_tree; - bool Load(const std::string &file_path); + bool Load(const std::string &file_path, nlohmann::json &model); + void Save(const nlohmann::json &model); + void CreateTree(); void Clear() { m_uuid = ""; @@ -102,6 +106,7 @@ public: static void EraseString(std::string &theString, const std::string &toErase); static std::string ToUpper(const std::string &input); + private: // Project properties and location std::string m_name; /// human readable name @@ -112,7 +117,7 @@ private: std::vector m_resources; std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped - std::string m_project_path; /// JSON project file + std::string m_project_file_path; /// JSON project file int m_display_w{320}; int m_display_h{240};