switched to nlohmann Json

This commit is contained in:
Anthony Rabine 2023-05-14 23:02:06 +02:00
parent ca36d4ac32
commit 868d4fdc8e
8 changed files with 198 additions and 200 deletions

View file

@ -556,71 +556,15 @@ void MainWindow::OpenProjectDialog()
void MainWindow::OpenProject(const QString &filePath) void MainWindow::OpenProject(const QString &filePath)
{ {
bool success = false; bool success = false;
QString errorMsg; QString errorMsg = tr("General error");
m_project.Initialize(filePath.toStdString()); m_project.Initialize(filePath.toStdString());
QFile loadFile(m_project.GetProjectFilePath().c_str()); nlohmann::json model;
if (loadFile.open(QIODevice::ReadOnly)) if (m_project.Load(filePath.toStdString(), model))
{
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)
{ {
m_model.Load(model);
EnableProject(); EnableProject();
} }
else else
@ -633,41 +577,8 @@ void MainWindow::OpenProject(const QString &filePath)
void MainWindow::SaveProject() 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<Resource>::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); statusBar()->showMessage(tr("Saved '%1'").arg(m_project.GetProjectFilePath().c_str()), 2000);
} }

View file

@ -9,6 +9,7 @@
#include <QtWidgets/QFileDialog> #include <QtWidgets/QFileDialog>
#include <QMenu> #include <QMenu>
MediaNodeModel::MediaNodeModel(StoryGraphModel &model) MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
: m_model(model) : m_model(model)
, m_widget(new QWidget()) , 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 // Merge two objects
QVariantMap map = obj.toVariantMap(); j.merge_patch(m_mediaData);
map.insert(m_mediaData); return j;
return QJsonObject::fromVariantMap(map);
} }
void MediaNodeModel::load(const QJsonObject &mediaData) void MediaNodeModel::FromJson(nlohmann::json &j)
{ {
m_mediaData = mediaData.toVariantMap(); m_mediaData = j;
// Display loaded image // Display loaded image
QString imagePath = m_mediaData["image"].toString(); std::string imagePath = m_mediaData["image"].get<std::string>();
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()) if (pix.isNull())
{ {
std::cout << "!!!!!!! " << m_mediaData["image"].toString().toStdString() << std::endl; std::cout << "!!!!!!! " << m_mediaData["image"].get<std::string>() << std::endl;
} }
int w = m_ui.image->width(); int w = m_ui.image->width();
@ -85,15 +84,14 @@ void MediaNodeModel::setImage(const QString &imagePath)
m_ui.image->setPixmap(pix); m_ui.image->setPixmap(pix);
} }
void MediaNodeModel::setInternalData(const QVariant &value) void MediaNodeModel::setInternalData(const nlohmann::json &j)
{ {
QJsonObject obj = value.toJsonObject(); if (j.contains("image")) {
if (obj.contains("image")) { setImage(j["image"].get<std::string>().c_str());
setImage(obj.value("image").toString());
} }
// Merge new data into local object // Merge new data into local object
m_mediaData.insert(obj.toVariantMap()); m_mediaData.merge_patch(j);
} }
unsigned int MediaNodeModel::nPorts(PortType portType) const unsigned int MediaNodeModel::nPorts(PortType portType) const

View file

@ -36,11 +36,10 @@ public:
QString name() const override { return QString("MediaNode"); } QString name() const override { return QString("MediaNode"); }
public: 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 nlohmann::json &j) override;
void setInternalData(const QVariant &value) override;
public: public:
virtual QString modelName() const { return QString("MediaNode"); } virtual QString modelName() const { return QString("MediaNode"); }
@ -57,6 +56,7 @@ public:
bool resizable() const override { return true; } bool resizable() const override { return true; }
protected: protected:
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
@ -69,6 +69,6 @@ private:
Ui::mediaNodeUi m_ui; Ui::mediaNodeUi m_ui;
std::shared_ptr<NodeData> m_nodeData; std::shared_ptr<NodeData> m_nodeData;
QVariantMap m_mediaData; nlohmann::json m_mediaData;
void setImage(const QString &fileName); void setImage(const QString &fileName);
}; };

View file

@ -176,6 +176,17 @@ QVariant StoryGraphModel::nodeData(NodeId nodeId, NodeRole role) const
return result; 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 StoryGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value)
{ {
bool result = false; bool result = false;
@ -212,10 +223,8 @@ bool StoryGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value)
break; break;
case NodeRole::InternalData: case NodeRole::InternalData:
{
model->setInternalData(value);
break; break;
}
case NodeRole::InPortCount: case NodeRole::InPortCount:
break; break;
@ -307,9 +316,63 @@ bool StoryGraphModel::deleteNode(NodeId const nodeId)
return true; 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<qint64>(p.outNodeId)},
{"outPortIndex", static_cast<qint64>(p.outPortIndex)},
{"intNodeId", static_cast<qint64>(p.inNodeId)},
{"inPortIndex", static_cast<qint64>(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<QtNodes::ConnectionId>();
// Restore the connection
addConnection(connId);
}
}
nlohmann::json StoryGraphModel::SaveNode(NodeId const nodeId) const
{
nlohmann::json nodeJson;
nodeJson["id"] = static_cast<qint64>(nodeId); nodeJson["id"] = static_cast<qint64>(nodeId);
auto it = _models.find(nodeId); auto it = _models.find(nodeId);
@ -318,43 +381,25 @@ QJsonObject StoryGraphModel::saveNode(NodeId const nodeId) const
auto &model = it->second; auto &model = it->second;
nodeJson["internal-data"] = model->save(); nodeJson["internal-data"] = model->ToJson();
{ {
QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>(); QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
QJsonObject posJson; nlohmann::json posJson;
posJson["x"] = pos.x(); posJson["x"] = pos.x();
posJson["y"] = pos.y(); posJson["y"] = pos.y();
nodeJson["position"] = posJson; nodeJson["position"] = posJson;
nodeJson["inPortCount"] = QString::number(nodeData(nodeId, NodeRole::InPortCount).value<int>()); nodeJson["inPortCount"] = nodeData(nodeId, NodeRole::InPortCount).value<int>();
nodeJson["outPortCount"] = QString::number(nodeData(nodeId, NodeRole::OutPortCount).value<int>()); nodeJson["outPortCount"] = nodeData(nodeId, NodeRole::OutPortCount).value<int>();
} }
return nodeJson; return nodeJson;
} }
QJsonObject StoryGraphModel::save() const
{
QJsonObject sceneJson;
QJsonArray nodesJsonArray; void StoryGraphModel::LoadNode(const nlohmann::json &nodeJson)
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)
{ {
// Possibility of the id clash when reading it from json and not generating a // Possibility of the id clash when reading it from json and not generating a
// new value. // new value.
@ -363,17 +408,17 @@ void StoryGraphModel::loadNode(QJsonObject const &nodeJson)
// loading. // loading.
// 2. When undoing the deletion command. Conflict is not possible // 2. When undoing the deletion command. Conflict is not possible
// because all the new ids were created past the removed nodes. // because all the new ids were created past the removed nodes.
NodeId restoredNodeId = nodeJson["id"].toInt(); NodeId restoredNodeId = nodeJson["id"].get<int>();
_nextNodeId = std::max(_nextNodeId, restoredNodeId + 1); _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::string>();
// std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName); // std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName);
auto model = createNode(delegateModelName.toStdString()); auto model = createNode(delegateModelName);
if (model) { if (model) {
// connect(model.get(), // connect(model.get(),
@ -389,37 +434,18 @@ void StoryGraphModel::loadNode(QJsonObject const &nodeJson)
Q_EMIT nodeCreated(restoredNodeId); Q_EMIT nodeCreated(restoredNodeId);
QJsonObject posJson = nodeJson["position"].toObject(); nlohmann::json posJson = nodeJson["position"];
QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); QPointF const pos(posJson["x"].get<double>(), posJson["y"].get<double>());
setNodeData(restoredNodeId, NodeRole::Position, pos); setNodeData(restoredNodeId, NodeRole::Position, pos);
_models[restoredNodeId]->load(internalDataJson); _models[restoredNodeId]->FromJson(internalDataJson);
} else { } else {
throw std::logic_error(std::string("No registered model with name ") throw std::logic_error(std::string("No registered model with name ") + delegateModelName);
+ delegateModelName.toLocal8Bit().data());
} }
} }
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) void StoryGraphModel::addPort(NodeId nodeId, PortType portType, PortIndex portIndex)
{ {

View file

@ -96,19 +96,6 @@ public:
bool deleteNode(NodeId const nodeId) override; 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 addPort(NodeId nodeId, PortType portType, PortIndex portIndex);
void removePort(NodeId nodeId, PortType portType, PortIndex first); void removePort(NodeId nodeId, PortType portType, PortIndex first);
@ -132,6 +119,13 @@ public:
StoryProject &GetProject() { return m_project; }; StoryProject &GetProject() { return m_project; };
void Clear(); 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: signals:
void sigChooseFile(NodeId id); void sigChooseFile(NodeId id);

View file

@ -10,6 +10,7 @@
#include <QtNodes/NodeDelegateModel> #include <QtNodes/NodeDelegateModel>
#include <QtNodes/NodeDelegateModelRegistry> #include <QtNodes/NodeDelegateModelRegistry>
#include <QtNodes/Definitions> #include <QtNodes/Definitions>
#include "json.hpp"
using QtNodes::NodeData; using QtNodes::NodeData;
using QtNodes::NodeDataType; using QtNodes::NodeDataType;
@ -32,7 +33,18 @@ public:
void setNodeId(NodeId id) { m_nodeId = id; } void setNodeId(NodeId id) { m_nodeId = id; }
NodeId getNodeId() { return m_nodeId; } 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 // default impl
} }

View file

@ -15,7 +15,7 @@ void StoryProject::New(const std::string &uuid, const std::string &file_path)
void StoryProject::Initialize(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); std::filesystem::path p(file_path);
m_working_dir= p.parent_path(); m_working_dir= p.parent_path();
@ -33,12 +33,12 @@ void StoryProject::Initialize(const std::string &file_path)
m_initialized = true; 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); std::ifstream f(file_path);
bool success = false; bool success = false;
/*
std::filesystem::path p(file_path); std::filesystem::path p(file_path);
m_working_dir= p.parent_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); nlohmann::json j = nlohmann::json::parse(f);
m_nodes.clear(); m_nodes.clear();
if (j.contains("project"))
{
nlohmann::json projectData = j["project"];
m_name = projectData["name"].get<std::string>();
m_uuid = projectData["uuid"].get<std::string>();
nlohmann::json resourcesData = projectData["resources"];
for (const auto &obj : resourcesData)
{
Resource rData;
rData.type = obj["type"].get<std::string>();
rData.format = obj["format"].get<std::string>();
rData.description = obj["description"].get<std::string>();
rData.file = obj["file"].get<std::string>();
m_resources.push_back(rData);
}
if (j.contains("nodegraph"))
{
model = j["nodegraph"];
success = true;
}
}
/*
if (j.contains("nodes")) if (j.contains("nodes"))
{ {
for (auto& element : j["nodes"]) for (auto& element : j["nodes"])
@ -106,21 +137,42 @@ bool StoryProject::Load(const std::string &file_path)
success = true; success = true;
*/
} catch(std::exception &e) }
catch(std::exception &e)
{ {
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
} }
if (success)
{
CreateTree();
}
*/
return success; 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() void StoryProject::CreateTree()
{ {
// Algorithm: level order traversal of N-ary tree // Algorithm: level order traversal of N-ary tree
@ -259,7 +311,7 @@ void StoryProject::SetDisplayFormat(int w, int h)
std::string StoryProject::GetProjectFilePath() const std::string StoryProject::GetProjectFilePath() const
{ {
return m_project_path; return m_project_file_path;
} }
std::string StoryProject::GetWorkingDir() const std::string StoryProject::GetWorkingDir() const

View file

@ -4,7 +4,9 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include "json.hpp"
// FIXME : Structure très Lunii style, à utiliser pour la conversion peut-être ...
struct StoryNode struct StoryNode
{ {
bool auto_jump; bool auto_jump;
@ -55,7 +57,9 @@ struct StoryProject
StoryNode *m_tree; 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 CreateTree();
void Clear() { void Clear() {
m_uuid = ""; m_uuid = "";
@ -102,6 +106,7 @@ public:
static void EraseString(std::string &theString, const std::string &toErase); static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input); static std::string ToUpper(const std::string &input);
private: private:
// Project properties and location // Project properties and location
std::string m_name; /// human readable name std::string m_name; /// human readable name
@ -112,7 +117,7 @@ private:
std::vector<Resource> m_resources; std::vector<Resource> m_resources;
std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped 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_w{320};
int m_display_h{240}; int m_display_h{240};