使用QML LocalStorage来存储我们的数据

在先前的例子中,我们可以“使用SQLite offline storage API来存储应用的设置”。我们也在例程“如何在QML应用中动态修改ListModel中的数据并存储它为JSON格式”中展示如何把我们需要的JSON存储到一个本地的文件中。在这篇文章中,我们将使用QtQuick所提供的LocalStorage来存储我们所需要的数据。


为了说明问题,我首先来创建一个基于“QtQuick App with QML UI (qmake)”的模版。首先我们修改我们的main.cpp如下:


Main.qml


#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QDebug>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView view;
    qDebug() << "Local storage path: " << view.engine()->offlineStoragePath();
    QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit()));

    view.setSource(QUrl(QStringLiteral("qrc:///Main.qml")));
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.show();
    return app.exec();
}

当我们运行我们的应用时,我们可以看到:

Local storage path:  "/home/phablet/.local/share/localstorage/QML/OfflineStorage"

这个路径显然是和我们在实际在手机上运行时产生的实际的路径是不同的:



这也说明在Ubuntu上的实现和标准的Qt上的实现还是不同的。从上面我们可以看出数据库的时间路径为:

phablet@ubuntu-phablet:~/.local/share/localstorage.liu-xiao-guo/Databases$ ls
4ff10001f402923590ceb1d12a0cffc6.ini  4ff10001f402923590ceb1d12a0cffc6.sqlite


为了能够使得应用能够自己退出,我们添加了如下的语句:

  QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit()));

这样,在我们的QML代码中使用Qt.quit()时可以让应用退出。这和我们一般的设计是不同的。我们一般情况下是不需要这样做的。对于本应用来说,我们希望在退出时得到一个事件来保存我们的设置,所有我们有这样一个特殊的处理。


为了能够对SQLite数据库访问,我们设计了如下的database.js文件:

database.js


.import QtQuick.LocalStorage 2.0 as Sql

var db;

function initDatabase() {
    print('initDatabase()')

    db = Sql.LocalStorage.openDatabaseSync("CrazyBox", "1.0", "A box who remembers its position", 100000);
    db.transaction( function(tx) {
        print('... create table')
        tx.executeSql('CREATE TABLE IF NOT EXISTS data(name TEXT, value TEXT)');
    });
}

function readData() {
    print('readData()')

    if(!db) { return; }
    db.transaction( function(tx) {
        print('... read crazy object')
        var result = tx.executeSql('select * from data where name="crazy"');
        if(result.rows.length === 1) {
            print('... update crazy geometry')
            // get the value column
            var value = result.rows[0].value;
            // convert to JS object
            var obj = JSON.parse(value)
            // apply to object
            crazy.x = obj.x;
            crazy.y = obj.y;
        }
    });
}

function storeData() {
    print('storeData()')

    if(!db) { return; }
    db.transaction( function(tx) {
        print('... check if a crazy object exists')
        var result = tx.executeSql('SELECT * from data where name = "crazy"');
        // prepare object to be stored as JSON
        var obj = { x: crazy.x, y: crazy.y };
        if(result.rows.length === 1) {// use update
            print('... crazy exists, update it')
            result = tx.executeSql('UPDATE data set value=? where name="crazy"', [JSON.stringify(obj)]);
        } else { // use insert
            print('... crazy does not exists, create it')
            result = tx.executeSql('INSERT INTO data VALUES (?,?)', ['crazy', JSON.stringify(obj)]);
        }
    });
}


这里,我们可以创建一个叫做“CrazyBox”的数据库,并在它里面生产一个叫做data的表。我们可以向表里更新数据。从这个例子里,我们可以看出来,用这个方法来存储我们的JSON数据而不使用到C++代码(参考例程“ 如何在QML应用中动态修改ListModel中的数据并存储它为JSON格式”)。这对大多数不很熟悉C++代码的开发者来说是一个好消息。


Main.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
import "database.js" as DB

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "localstorage.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    Page {
        title: i18n.tr("Localstorage")

        Rectangle {
            id: crazy
            objectName: 'crazy'
            width: 200
            height: 200
            x: 50
            y: 50
            color: "#53d769"
            border.color: Qt.lighter(color, 1.1)

            Text {
                anchors.centerIn: parent
                text: Math.round(parent.x) + '/' + Math.round(parent.y)
            }

            MouseArea {
                anchors.fill: parent
                drag.target: parent
            }
        }


        Component.onCompleted: {
            DB.initDatabase();

            print("it is going to read data");
            DB.readData();
        }

        Component.onDestruction: {
            print("it is going to save data");
            DB.storeData();
        }

        Button {
            anchors.bottom: parent.bottom
            anchors.bottomMargin: units.gu(1)
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Close"
            onClicked: {
                Qt.quit();
            }
        }
    }
}


我们的Main.qml设计非常简单。我们在UI被装载完后,初始化数据库,并读取已经存储的数据。如果数据已经存在,就读出来,并初始化我们的Rectangle “crazy”。在应用退出的时候,我们存储我们的数据。这里应该注意的是,当我们用我们的手指拖动关掉应用时,我们的应用接受不到如下的事件:

        Component.onDestruction: {
            print("it is going to save data");
            DB.storeData();
        }

我们必须通过选择“Quit”按钮来得到这个事件。

运行我们的应用:

  


每当我们退出应用,并重新启动应用后,我们可以发现我们的绿色的方块是在和上次退出时一样的位置。

整个项目的源码在: https://github.com/liu-xiao-guo/localstorage