benlau / qsyncable

A solution of nested Json List Model
Apache License 2.0
118 stars 40 forks source link

'faketrello' example feature req -- persist model to file, Board::load() recreates model #2

Open NielsMayer opened 7 years ago

NielsMayer commented 7 years ago

In faketrello example, Board::load() currently loads board statically from code. It would be a more complete example app if it showed "end to end" usage of the model, from initial creation, modification, and persistence on application close.

What's the simplest implementation of a "print method" for the model so that it can be persisted to a file? For example a method to output the current model as JSON?

How could Board::load() read such a file, and then reconstruct the model from the JSON-persisted file?

benlau commented 7 years ago

In fact, It is not difficult. You just need a JSON parsing code to load as QVariantMap/ QVariantList then convert to your own data structure.

Example code of JSON parsing (QVariantMap)

QVariantMap parse(const QString &text)
{
    QJsonParseError error;
    QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8(),&error);

    if (error.error != QJsonParseError::NoError) {
        qWarning() << "JSON::parse() error: "<< error.errorString();
    }

    return doc.object().toVariantMap();
}

stringify

QString stringify(const QVariantMap &data)
{
    QJsonObject object = QJsonObject::fromVariantMap(data);

    QJsonDocument doc;
    doc.setObject(object);
    QByteArray bytes = doc.toJson(QJsonDocument::Indented);

    return bytes;
}
NielsMayer commented 7 years ago

Thanks for the hint!

I added to appdelegatge.cpp:

// NPM -- see https://github.com/benlau/qsyncable/issues/2
QString AppDelegate::stringifyModel()
{
    QVariantList lists;
    int count = cardListStore->storage().size();
    for (int i = 0 ; i < count ; i++) {
        lists << cardListStore->get(i);
    }

    QVariantMap map;
    map["lists"] = lists;
    QJsonObject object = QJsonObject::fromVariantMap(map);

    QJsonDocument doc;
    doc.setObject(object);
    QByteArray bytes = doc.toJson(QJsonDocument::Indented);

    return bytes;
}

// NPM -- see https://github.com/benlau/qsyncable/issues/2
QString AppDelegate::stringifyBoard()
{
    QVariantMap map = m_board.toMap();
    QJsonObject object = QJsonObject::fromVariantMap(map);

    QJsonDocument doc;
    doc.setObject(object);
    QByteArray bytes = doc.toJson(QJsonDocument::Indented);

    return bytes;
}

And then call in main.qml:

    onClosing: {
        close.accepted = true;
        console.log("Board=" + App.stringifyBoard())
        console.log("Model=" + App.stringifyModel())
    }

Which, on application close, outputs the initial board layout from both the model and the board (which are the same).

qml: Board={
    "lists": [
        {
            "cards": [
                {
                    "text": "Card 3",
                    "uuid": "b643690e-a288-4097-89fe-b40176e66869"
                },
                {
                    "text": "Card 2",
                    "uuid": "384a1f8e-f62a-48f3-bb99-982dd453abbe"
                },
                {
                    "text": "Card 1",
                    "uuid": "3f16b090-7947-4f5c-b9c8-d4ce8875b219"
                }
            ],
            "title": "List 1",
            "uuid": "552fefa3-da81-4387-a9da-8ad9a5a501ac"
        },
        {
            "cards": [
                {
                    "text": "Card 8",
                    "uuid": "92b27107-30d1-429f-8cc6-230eb163e711"
                },
                {
                    "text": "Card 7",
                    "uuid": "4e3cdb17-706f-4d04-bea1-7bd6286d2891"
                },
                {
                    "text": "Card 6",
                    "uuid": "193391f2-2ca1-452e-b587-eccc78557953"
                },
                {
                    "text": "Card 5",
                    "uuid": "7cc32364-7726-4a28-b058-8bcfc67d4790"
                },
                {
                    "text": "Card 4",
                    "uuid": "e32f8f2e-7387-48de-a669-a1195e53714b"
                }
            ],
            "title": "List 2",
            "uuid": "442efed1-43c2-4903-846a-867f28ed7eab"
        }
    ]
}

qml: Model={
    "lists": [
        {
            "cards": [
                {
                    "text": "Card 3",
                    "uuid": "b643690e-a288-4097-89fe-b40176e66869"
                },
                {
                    "text": "Card 2",
                    "uuid": "384a1f8e-f62a-48f3-bb99-982dd453abbe"
                },
                {
                    "text": "Card 1",
                    "uuid": "3f16b090-7947-4f5c-b9c8-d4ce8875b219"
                }
            ],
            "title": "List 1",
            "uuid": "552fefa3-da81-4387-a9da-8ad9a5a501ac"
        },
        {
            "cards": [
                {
                    "text": "Card 8",
                    "uuid": "92b27107-30d1-429f-8cc6-230eb163e711"
                },
                {
                    "text": "Card 7",
                    "uuid": "4e3cdb17-706f-4d04-bea1-7bd6286d2891"
                },
                {
                    "text": "Card 6",
                    "uuid": "193391f2-2ca1-452e-b587-eccc78557953"
                },
                {
                    "text": "Card 5",
                    "uuid": "7cc32364-7726-4a28-b058-8bcfc67d4790"
                },
                {
                    "text": "Card 4",
                    "uuid": "e32f8f2e-7387-48de-a669-a1195e53714b"
                }
            ],
            "title": "List 2",
            "uuid": "442efed1-43c2-4903-846a-867f28ed7eab"
        }
    ]
}

TODO: onClosing, save to a file in ~/.local/share/ and on initialization read JSON from that file to recreate model.

QUESTION: In AppDelegate::stringifyModel() is there a simpler way of getting the toplevel model from cardListStore as a QVariantMap or do I need to create a toplevel map of "lists" , and then convert that to JSON (as done in code example above). Probably makes sense to add such code to QSListsModel, to provide a QVariantMap version of the model, rather than do it in appdelegate.cpp ...

NielsMayer commented 7 years ago

See #3 for an implementation of this feature. https://github.com/NielsMayer/qsyncable/tree/faketrello-persistence-feature