存储(Storage)

  1. Settings
  2. 本地存储 - SQL(Local Storage - SQL)
    1. 疯狂的矩形框(Crazy Rectangle)
  3. 其它存储接口(Other Storage APIs)

本章将介绍在Qt5中使用QtQuick存储数据。QtQuick只提供了有限的方法来直接存储本地数据。在这样的场景下,它更多的扮演了一个浏览者的角色。在大多数项目中,存储数据由C++后端来完成,并需要将这个功能导入到QtQuick前端。QtQucik没有提供类似Qt C++的主机文件系统接口来读取和写入文件。所以后端工程师需要编写一个这样的插件或者使用网络通道与本地服务器通信来提供这些功能。

每个应用程序都需要持续的存储少量或者大量的信息。可以存储在本地文件系统或者远程服务器上。一些信息将会被结构化、简单化例如程序配置信息,一些信息将会巨大并且复杂例如文档文件,一些信息将会巨大并且结构化需要与某种数据库连接。在这章我们将会讨论如何使用QtQuick通过网络和本地的方式存储数据。

Settings

Qt自身就提供了基于系统方式的应用程序配置(又名选项,偏好)C++类 QSettings。它使用基于当前操作系统的方式存储配置。此外,它支持通用的INI文件格式用来操作跨平台的配置文件。

在Qt5.2中,配置(Settings)被加入到QML中。编程接口仍然在实验模块中,这意味着接口可能在未来会改变。这里需要注意。

这里有一个小例子,对一个矩形框配置颜色。每次用户点击窗口生成一个新的随机颜色。应用程序关闭后重启你将会看到你最后看到的颜色。
默认的颜色是用来初始化根矩形框的颜色。

import QtQuick 2.0
import Qt.labs.settings 1.0

Rectangle {
    id: root
    width: 320; height: 240
    color: '#000000'
    Settings {
        id: settings
        property alias color: root.color
    }
    MousArea {
        anchors.fill: parent
        onClicked: root.color = Qt.hsla(Math.random(), 0.5, 0.5, 1.0);
    }
}

每次颜色值的变化都被存储在配置中。这可能不是我们需要的。只有在要求使用标准属性的时候才存储配置。

Rectangle {
    id: root
    color: settings.color
    Settings {
        id: settings
        property color color: '#000000'
    }
    function storeSettings() { // executed maybe on destruction
        settings.color = root.color
    }
}

可以使用category属性存储不同种类的配置。

Settings {
    category: 'window'
    property alias x: window.x
    property alias y: window.x
    property alias width: window.width
    property alias height: window.height
}

配置同城根据你的应用程序名称,组织和域存储。这些信息通常在你的C++ main函数中设置。

int main(int argc, char** argv) {
    ...
    QCoreApplication::setApplicationName("Awesome Application");
    QCoreApplication::setOrganizationName("Awesome Company");
    QCoreApplication::setOrganizationDomain("org.awesome");
    ...
}

本地存储 - SQL(Local Storage - SQL)

Qt Quick支持一个与浏览器由区别的本地存储编程接口。需要使用”import QtQuick.LocalStorage 2.0”语句来导入后才能使用这个编程接口。

通常使用基于给定的数据库名称和版本号使用系统特定位置的唯一文件ID号来存储数据到一个SQLITE数据库中。无法列出或者删除已有的数据库。你可以使用QQmlEngine::offlineStoragePate()来寻找本地存储。

使用这个编程接口你首选需要创建一个数据库对象,然后在这个数据库上创建数据库事务。每个事务可以包含一个或多个SQL查询。当一个SQL查询在事务中失败后,事务会回滚。

例如你可以使用本地存储从一个简单的注释表中读取一个文本列:

import QtQuick 2.2
import QtQuick.LocalStorage 2.0

Item {
    Component.onCompleted: {
        var db = LocalStorage.openDatabaseSync("MyExample", "1.0", "Example database", 10000);
        db.transaction( function(tx) {
            var result = tx.executeSql('select * from notes');
            for(var i = 0; i < result.rows.length; i++) {
                    print(result.rows[i].text);
                }
            }
        });
    }
}

疯狂的矩形框(Crazy Rectangle)

假设我们想要存储一个矩形在场景中的位置。

下面是我们的基础代码。

import QtQuick 2.2

Item {
    width: 400
    height: 400

    Rectangle {
        id: crazy
        objectName: 'crazy'
        width: 100
        height: 100
        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
        }
    }
}

可以自由的拖动这个矩形。当关闭这个应用程序并再次打开时,这个矩形框仍然在相同的位置。

现在我们将添加矩形的x/y坐标值存储到SQL数据库中。首先我们需要添加一个初始化、读取和保存数据库功能。这些功能在组件构造和组件销毁时被调用。

import QtQuick 2.2
import QtQuick.LocalStorage 2.0

Item {
    // reference to the database object
    property var db;

    function initDatabase() {
        // initialize the database object
    }

    function storeData() {
        // stores data to DB
    }

    function readData() {
        // reads and applies data from DB
    }


    Component.onCompleted: {
        initDatabase();
        readData();
    }

    Component.onDestruction: {
        storeData();
    }
}

你也可以调用已有的JS库提取相关数据库代码来完成所有的逻辑。如果这个逻辑变得更加复杂这将是最好的解决方案。

在数据库初始化函数中,我们创建一个数据库对象并且确保SQL表已经被创建。

function initDatabase() {
    print('initDatabase()')
    db = 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;
        }
    });
}

我们希望在将数据作为一个JSON字符串存储在一列值中。这与典型的SQL不同,但是可以很好的与JS代码结合。所以我们将它存储为一个JS对象的可以使用JSON stringif/parse方法的数据替代使用x,y作为属性值放在数据库表中。最后我们获取一个包含x,y属性有效的JS对象,我们可以将它应用在我们疯狂的矩形中。

为了保存数据,我们需要区分更新和插入的情况。当一个记录已经存在时我们使用更新,如果没有记录则将它插入在“crazy”下。

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)]);
        }
    });
}

替代选择所有记录的设置,我们也可以使用SQLITE计数函数:SELECT COUNT(*) from data where name = “crazy”,将返回使用行选择查询的结果。否则这将是一个通用的SQL代码。所谓额外的特性,我们在查询中使用?绑定SQL值。

现在你可与拖动这个矩形框当你退出应用程序时会将x/y坐标值存储到数据库,下次启动应用程序时矩形框将使用存储的x/y坐标值定位。

其它存储接口(Other Storage APIs)

直接从QML中存储信息,上面的这些方法是主要存储方法。事实上QtQuick最有效的存储方法是使用C++扩展接口调用本地存储系统或者类似Qt云存储使用网络编程接口调用远程存储系统。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2291184112@qq.com

×

喜欢就点赞,疼爱就打赏