Canonical Voices

UbuntuTouch

我们可以看到在Ubuntu SDK中有一个自己的WebView。它没有采用Qt标准的Webkit库。在Ubuntu上,我们对下面的引擎做了很多的优化(oxide引擎),这样使得我们的WebView性能更加优越。


下面我们通过一个例子来设计出一个简单的Browser。


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Web 0.2
import QtQuick.Layouts 1.1

/*!
    \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: "browser.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(100)
    height: units.gu(75)

    Page {
        title: i18n.tr("")

        RowLayout {
            id:toolbar
            anchors.top: parent.top
            width: parent.width
            height: units.gu(8)
            spacing: units.gu(1)

            Icon {
                id: back
                anchors.verticalCenter: parent.verticalCenter
                name: "go-previous"
                height: input.height
                width: height
                visible: webview.canGoBack

                MouseArea {
                    anchors.fill: parent
                    onClicked: webview.goBack();
                }
            }
            Icon {
                id: forward
                anchors.verticalCenter: parent.verticalCenter
                name: "go-next"
                height: input.height
                width: height
                visible: webview.canGoForward
                MouseArea {
                    anchors.fill: parent
                    onClicked: webview.goForward();
                }
            }

            TextField {
                id: input
                anchors.verticalCenter: parent.verticalCenter
                height: parent.height - units.gu(1)
                Layout.maximumWidth: parent.width
                Layout.preferredWidth: parent.width - back.width - forward.width
                text: "http://www.baidu.com"

                onAccepted: {
                    webview.url = input.text
                }
            }
        }

        WebView {
            id: webview
            anchors.top: toolbar.bottom
            height: parent.height - toolbar.height
            width: parent.width

            url: "http://www.baidu.com"
        }

    }
}


在这里,我们使用了:

import Ubuntu.Web 0.2

模块。在上面我们使用了两个Icon来返回或向前看。同时我们设计了一个TextField来输入我们想要去的地址。注意地址必须是以http开始的字符串。当我们按下enter键后,就会自己打开页面。


  


  


代码不多,但是它完成了我们想要完成的东西。

整个项目的源码在:git clone https://gitcafe.com/ubuntu/browser.git



作者:UbuntuTouch 发表于2015/6/8 14:14:41 原文链接
阅读:286 评论:0 查看评论

Read more
UbuntuTouch

对于有些QML应用来说,震动是非常重要的一个功能。特别是对一下游戏来说。那么我们怎么在QML应用中震动呢?


我们官方有一个API HapticsEffect,这个API的功能就是让我们的应用来震动的。使用这个API非常容易:


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtFeedback 5.0

/*!
    \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: "vibration.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(100)
    height: units.gu(75)

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

        HapticsEffect {
            id: rumbleEffect
            attackIntensity: 0.0
            attackTime: 250
            intensity: 1.0
            duration: 100
            fadeTime: 250
            fadeIntensity: 0.0
        }

        Column {
            spacing: units.gu(1)
            anchors {
                margins: units.gu(2)
                fill: parent
            }

            Button {
                objectName: "button"
                width: parent.width

                text: i18n.tr("Vibrate me!")

                onClicked: {
                    rumbleEffect.start();  // plays a rumble effect
                }
            }
        }
    }
}

这里我们import了我们需要的库:

import QtFeedback 5.0

然后,实例化我们的HapticsEffect:

  HapticsEffect {
            id: rumbleEffect
            attackIntensity: 0.0
            attackTime: 250
            intensity: 1.0
            duration: 100
            fadeTime: 250
            fadeIntensity: 0.0
        }

当我们按下我们的按钮时,我们及开始震动了。当然这个必须是在手机上才可以测试到。


整个项目的源码在:git clone https://gitcafe.com/ubuntu/vibration.git

作者:UbuntuTouch 发表于2015/5/14 15:51:39 原文链接
阅读:263 评论:0 查看评论

Read more
UbuntuTouch

在前面的一篇文章中“如何在Ubuntu手机中使用前置照相机”,我们可以使用相应的C++代码来控制前后位置的照相机来拍照,但是我们又如何能够把所拍到的照片存储到相应的文件中呢?我们可以使用Qt 5.4版本中的Item所提供的一个新的功能“grabToImage”。这样我们可以很方便地把我们得到的照片存到我们相应的目录中。


在Ubuntu手机平台中,由于安全的原因,我们的应用只可以访问自己所在的可以访问的目录。在这个例程中,我们使用了一些环境的变量来得到我们所需要的存储的目录的位置:


我们整个的代码如下:


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtMultimedia 5.0
import readenv 1.0

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "camerashot.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(100)
    height: units.gu(75)

    Page {
        id: mainPage
        title: i18n.tr("camerashot")

        property string path: ""

        ReadEnv {
            id: env
        }

        Item {
            id: page1
            anchors.fill: parent

            Camera {
                id: camera

                imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

                exposure {
                    exposureCompensation: -1.0
                    exposureMode: Camera.ExposurePortrait
                }

                flash.mode: Camera.FlashRedEyeReduction

                imageCapture {
                    onImageCaptured: {
                        photoPreview.source = preview  // Show the preview in an Image
                    }
                }
            }

            VideoOutput {
                id: video
                source: camera
                anchors.fill: parent
                focus : visible // to receive focus and capture key events when visible
                orientation: -90

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        var APP_ID = env.getenv("APP_ID");
                        var app_pkgname = APP_ID.split('_')[0]
                        mainPage.path = env.getenv("XDG_DATA_HOME") +
                                "/" + app_pkgname + "/pic.png";
                        console.log("share path: " + mainPage.path);

                        var a = video.grabToImage(function(result) {
                            result.saveToFile(mainPage.path);
                        }, Qt.size(photoPreview.width, photoPreview.height) );

                        console.log("return result: " + a);

                    }
                }

                Image {
                    id: photoPreview
                }
            }

            Button {
                id: button
                anchors.bottom: parent.bottom
                anchors.right: parent.right

                text: "Show the taken picture"

                onClicked: {
                    pic.visible = true;
                    page1.visible = false;
                    console.log("image path: " + mainPage.path);
                    image.source = "";
                    image.source = "file:///" + mainPage.path;
                }
            }
        }

        Item {
            id: pic

            anchors.fill: parent
            visible: false

            Image {
                id: image
                cache: false
                anchors.fill: parent
            }

            Label {
                text: mainPage.path
            }

            Image {
                anchors.bottom: parent.bottom
                anchors.left: parent.left
                anchors.leftMargin: units.gu(1)
                anchors.bottomMargin: units.gu(1)
                width: units.gu(3)
                height: units.gu(3)

                source: "images/icon-left-arrow.png"
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        pic.visible = false
                        page1.visible = true;
                    }
                }
            }
        }

    }
}


整个的设计非常简单。在主界面中我们可以在相机的影像的任何地方进行触屏来拍下我们的照片。我们可以使用“Show the taken picture”按钮来进入下一个页面来显示存储的图片。为了方便设计,我们选择了一个固定的文件名。当然,开发者们可以自己来选择自己的所需要的文件名。


  


整个项目的源码在: git clone https://gitcafe.com/ubuntu/camerashot.git

作者:UbuntuTouch 发表于2015/5/18 16:55:18 原文链接
阅读:250 评论:0 查看评论

Read more
UbuntuTouch

我前一段时间在我的博客里写了一篇文章“如何在QML应用中读写文件”,那篇文章是介绍如何使用C++来读取文件的。那种方法是一个比较通用的方法。但是对于有些应用来说,我们可以通过配置JSON来创建我们的UI,或者对不同的平台进行配置,而不用写一个单独的设置文件来做这件事。那么我们如何不需要通过C++的方法来读取Json文件呢?


我们可以使用我们的SDK创建一个最基本的QML应用。为了能够读取Json文件,我们创建一个叫做“jsonparser.js”的文件:

/* Based on:
 * JSONListModel - a QML ListModel with JSON and JSONPath support
 *
 * Copyright (c) 2012 Romain Pokrzywka (KDAB) (romain@kdab.com)
 * Licensed under the MIT licence
 * (http://opensource.org/licenses/mit-license.php)
 * https://github.com/s3u/JSONPath
 */

//Qt.include("jsonpath.js")

function readJsonFile(source, callback) {

    var xhr = new XMLHttpRequest;
    xhr.open("GET", source);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            var doc = xhr.responseText;
//            console.log("JSON: " + json)
            var json = JSON.parse(doc);
            callback.update(json);
        }
    }

   xhr.send();
}


我们通过XMLHttpRequest来完成本地文件的请求工作,并使用callback中的update来传人到QML文件中进行解析。为了能够更加方便地解析我们得到的Json文件,我们使用了

 https://github.com/s3u/JSONPath

里提供的库。我们可以把“jsonpath.js”下载到我们项目的根目录下,以方便使用。具体的使用方法可以在网上查看。

为了显示我们的JSO数据,我们对我们的Main.qml进行了修改:

import QtQuick 2.0
import Ubuntu.Components 1.1
import "jsonparser.js" as API
import "jsonpath.js" as JSONPath

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "readjsonfile.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)

    function update(json) {
        console.log("it is called in update!");
        // we can start to interpret the returned json
        var authors = JSONPath.jsonPath(json, "$.store.book[*].author");

        console.log("length: " + authors.length);

        for (var i in authors ) {
            console.log("author[" + i +"] " + authors[i]);
            mymodel.append( { "content": authors[i],
                               "type": "Authors"
                           });
        }

        // Get all of the books
        var books = JSONPath.jsonPath(json, "$.store.book[*].title");
        console.log("length: " + books.length);
        for (var j in books ) {
            console.log("author[" + j +"] " + books[j]);
            mymodel.append( { "content": books[j],
                               "type": "Books"
                           });
        }

    }

    Page {
        id: mainPage
        title: i18n.tr("Read Json File")


        ListModel {
            id: mymodel
        }

        Component {
            id: sectionHeading
            Rectangle {
                width: mainPage.width
                height: childrenRect.height
                color: "lightsteelblue"

                Text {
                    text: section
                    font.bold: true
                    font.pixelSize: 20
                }
            }
        }

        ListView {
            id: listview
            anchors.fill: parent
            model: mymodel
            delegate: Text {
                text: content
            }

            section.property: "type"
            section.criteria: ViewSection.FullString
            section.delegate: sectionHeading
        }

        Component.onCompleted: {
            console.log("Start to read JSON file");
            API.readJsonFile("sample.json", main)
        }
    }
}

为了完成我们的实验,我们也创建了一个sample.json文件。内容如下:

{
  "store": {
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}


运行我们的应用,我们应用显示的结果如下:


所有的源码在:git clone https://gitcafe.com/ubuntu/readjsonfile.git

作者:UbuntuTouch 发表于2015/5/13 14:08:10 原文链接
阅读:229 评论:0 查看评论

Read more
UbuntuTouch

目前我们调试Scope大多数使用的是qDebug来输出信息来查看的,或cerr标准C++输出。在这篇文章中,我们将展示用gdb来调试我们的Scope。


1)首先创建一个最基本的Scope


我们使用我们的SDK来创建一个最基本的Scope:


   

   

这样我们就创建了一个最基本的Scope。


2)为我们的手机安装gdbsever


由于一些原因,我们的手机并没有gdbserver。为了调试,我们需要安装这个软件才可以在线调试。我们启动一个terminal,并键入如下的命令:





这样我们就安装好gdbserver了。



3)调试我们的Scope


我们可以打开我们的Scope项目,并使用鼠标点击相应的区域来设置断点:




在我们的编译中设置“-DCMAKE_BUILD_TYPE=debug”标志位:




我们可以按下“F5”键,这样我们可以看到我们的Scope运行到我们设置的断点处。我们可以检查我们所想要知道的变量的值,也可以查看程序运行的顺序。





我们可以在上面的窗口中看见各个变量的值,使用“F10”键可以一步一步地跟踪Scope的运行。

由于一些原因,在调试完Scope,卸载Scope时可能会重启手机。目前我们已经知道这个问题。相应的bug在地址可以看到。

作者:UbuntuTouch 发表于2015/5/13 20:25:21 原文链接
阅读:214 评论:0 查看评论

Read more
UbuntuTouch

当我们需要对我们的Model中的数据进行排序或进行过滤时,我们需要用到SortFilterModel。如果只是想对我们的数据进行过滤的话,我们可以参考问我的例程“从零开始创建一个Ubuntu应用--一个小的RSS阅读器”。在我的挑战部分,我们可以对我们的XmlListModel中的项进行搜索,从而得到新的ListModel。在这里的文章中,我们将使用SortFilterModel来过滤和排序我们的Model,从而可以更加精准地显示我们所需要的数据。


首先,我们创建一个比较简单的QML应用,并采用我们的API页面中的例程(里面有些部分是不工作的),可以做一个简单的例子:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

/*!
    \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: "sortfiltermodel.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("sortfiltermodel")

        ListModel {
            id: movies
            ListElement {
                title: "Esign"
                producer: "Chris Larkee"
            }
            ListElement {
                title: "Elephants Dream"
                producer: "Blender"
            }
            ListElement {
                title: "Big Buck Bunny"
                producer: "blender"
            }
        }

        ListView {
            model: movies
            anchors.fill: parent
            delegate: ListItem.Subtitled {
                text: title
                subText: producer
            }
            section.delegate: ListItem.Header { text: i18n.tr(section) }
            section.property: "title"
            section.criteria: ViewSection.FirstCharacter
        }
    }
}

如果我们没有使用到SortFilterModel的话,我们的显示的结果如下:




如果我们加上SortFilterModel的话:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

/*!
    \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: "sortfiltermodel.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("sortfiltermodel")

        ListModel {
            id: movies
            ListElement {
                title: "Esign"
                producer: "Chris Larkee"
            }
            ListElement {
                title: "Elephants Dream"
                producer: "Blender"
            }
            ListElement {
                title: "Big Buck Bunny"
                producer: "blender"
            }
        }

        SortFilterModel {
            id: sortedMovies
            model: movies
            sort.property: "title"
            sort.order: Qt.DescendingOrder
            filter.property: "producer"
            filter.pattern: /blender/i
        }

        ListView {
            model: sortedMovies
            anchors.fill: parent
            delegate: ListItem.Subtitled {
                text: title
                subText: producer
            }
            section.delegate: ListItem.Header { text: i18n.tr(section) }
            section.property: "title"
            section.criteria: ViewSection.FirstCharacter
        }
    }
}


注意这里的ListView的model是sortedMovies。sortedMovies的数据来源于movies。SortFilterModel把movies中的数据按字母的倒序排列,并同时对producer进行匹配。如果含有“blender”,将会被收入sortedMovies中。显示的结果如下:



从显示中可以看出来,只显示blender的项,并且是以字母倒序排列的。


整个项目的源码在:git clone https://gitcafe.com/ubuntu/sortfiltermodel.git



            
作者:UbuntuTouch 发表于2015/6/1 23:37:06 原文链接
阅读:313 评论:0 查看评论

Read more
UbuntuTouch

我们知道像微信那样的带有气球的对话框对于一些聊天的应用来说非常好。在很多即时通讯的应用中可以用到。在今天的文章中,我们将介绍如何使用QML来实现这样的界面。


为了方便,我们可以采用Ubuntu SDK中的“QtQuick App with QML UI (qmake)”这个模版来实现一个模版的应用。为了能够创建一个TextBalloon的控件,我们使用了C++代码:

textballoon.h


#ifndef TEXTBALLOON_H
#define TEXTBALLOON_H

#include <QtQuick>

class TextBalloon : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)

    public:
        TextBalloon(QQuickItem *parent = 0);
        void paint(QPainter *painter);

        bool isRightAligned();
        void setRightAligned(bool rightAligned);

    private:
        bool rightAligned;

    signals:
        void rightAlignedChanged();
};

#endif

textballoon.cpp


#include "textballoon.h"

TextBalloon::TextBalloon(QQuickItem *parent)
    : QQuickPaintedItem(parent)
    , rightAligned(false)
{
}

void TextBalloon::paint(QPainter *painter)
{
    QBrush brush(QColor("#007430"));
    QBrush white(QColor("#FFFFFF"));


    if (rightAligned)
    {
        painter->setBrush(brush);
        painter->setPen(Qt::NoPen);
        painter->setRenderHint(QPainter::Antialiasing);
        painter->drawRoundedRect(10, 0, boundingRect().width() - 19, boundingRect().height(), 10, 10);

        const QPointF points[3] = {
            QPointF(boundingRect().width() - 10.0, 10.0),
            QPointF(boundingRect().width(), 20.0),
            QPointF(boundingRect().width() - 10.0, 30.0),
        };

        painter->drawConvexPolygon(points, 3);
    }
    else
    {
        painter->setBrush(white);
        painter->setPen(Qt::NoPen);
        painter->setRenderHint(QPainter::Antialiasing);
        painter->drawRoundedRect(10, 0, boundingRect().width() - 19, boundingRect().height(), 10, 10);

        const QPointF points[3] = {
            QPointF(10,10),
            QPointF(0, 20),
            QPointF(10, 30),
        };

        painter->drawConvexPolygon(points, 3);
    }
}

bool TextBalloon::isRightAligned()
{
    return this->rightAligned;
}

void TextBalloon::setRightAligned(bool rightAligned)
{
    this->rightAligned = rightAligned;
}


在main.cpp中,注册该类:

   qmlRegisterType<TextBalloon>("TextBalloon", 1, 0, "TextBalloon");

这样这个TextBalloon就可以在QML中被利用。我们的QML界面非常简单:

import QtQuick 2.0
import Ubuntu.Components 1.1


Item {
    id: root

    property int contentWidth: width *.6

    ListModel {
        id: balloonModel
    }

    ListView {
        id: balloonView
        anchors.bottom: controls.top
        anchors.bottomMargin: 2
        anchors.top: parent.top
        clip:true

        delegate: MyDelegate {}

        model: balloonModel
        spacing: units.gu(4)
        width: parent.width
    }

    Rectangle {
        id: controls

        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.margins: 1
        anchors.right: parent.right
        border.width: 2
        color: "white"
        height: parent.height * 0.15

        Text {
            anchors.centerIn: parent
            text: "Add another balloon"
        }

        MouseArea {
            anchors.fill: parent
            hoverEnabled: true
            onClicked: {
                balloonModel.append({"balloonWidth": Math.floor(Math.random() * 200 + 100),
                                     "content": "this is cool"
                                     })
                balloonView.positionViewAtIndex(balloonView.count -1, ListView.End)
            }
            onEntered: {
                parent.color = "#8ac953"
            }
            onExited: {
                parent.color = "white"
            }
        }
    }

    Component.onCompleted: {
        console.log("contentWidth: " + root.contentWidth);
        balloonModel.append({"balloonWidth": root.contentWidth,
                             "content": "this is a text, this is a perfect world to play with, and I love to play the world"
                             });
        balloonModel.append({"balloonWidth": root.contentWidth,
                             "content": "this is a text, this is a perfect world to play with, and I love to play the world"
                             });

    }
}

上面是一个ListView,下面是一个按钮来动态生成一些ListView中的项。我们的ListView的delegate设计稍微麻烦一点:

import QtQuick 2.0
import TextBalloon 1.0
import Ubuntu.Components 1.1
import QtQuick.Layouts 1.1

Item {
    id: delegate
    width: ListView.view.width
    height: txt.contentHeight + 20
    property bool rightAligned: index % 2 == 0 ? false : true

    RowLayout {
        spacing: units.gu(2)
        anchors.right: index % 2 == 0 ? undefined : parent.right

        Image {
            id: leftImg
            width: root.contentWidth*.2
            height: width
            anchors.top:parent.top
            source: "images/pic1.jpg"
            visible: delegate.rightAligned ? false : true
            fillMode: Image.PreserveAspectCrop
            Layout.maximumWidth:root.contentWidth*.2
            Layout.maximumHeight: root.contentWidth*.2
        }

        Text {
            id: txt
            anchors.top: parent.top
            anchors.topMargin: units.gu(1)
            width: root.contentWidth
            wrapMode: Text.WordWrap
            text: content
//            horizontalAlignment: delegate.rightAligned ? Text.AlignRight : Text.AlignLeft
            font.pixelSize: units.gu(3)

            Layout.maximumWidth: root.contentWidth

            TextBalloon {
                anchors.fill: parent
                z: -1
                rightAligned: delegate.rightAligned
                anchors.margins: -units.gu(1.5)
            }
        }

        Image {
            id: rightImg
            anchors.top:parent.top
            width: root.contentWidth*.2
            height: width
            source: "images/pic2.jpg"
            visible: delegate.rightAligned ? true : false
            fillMode: Image.PreserveAspectCrop
            Layout.maximumWidth:root.contentWidth*.2
            Layout.maximumHeight: root.contentWidth*.2
        }
    }
}


这里我们使用RowLayout,也是非常tricky的一个设计。运行我们的应用:

   

整个项目的源码在: git clone https://gitcafe.com/ubuntu/weixin_new.git

作者:UbuntuTouch 发表于2015/5/19 19:51:38 原文链接
阅读:232 评论:0 查看评论

Read more
UbuntuTouch

我们知道对于一些应用来说,判断方位可以使得我们可以重新定位我们的应用的布局,以使得我们的应用在不同的方位中更加合理及好看。在这篇文章中,我们来介绍如何来侦测应用方位的变化。


我们首先来创建一个我们自己的简单的QML应用。对于大多数的QML应用来说,一般是含有一个“MainView”的:


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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "orientation.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)

    property bool isLandscape: pageStack.width > pageStack.height ? true : false

    onWidthChanged: {
        console.log("Main width is changed: " + width)
    }

 ...
}


为了能够使得我们的应用能够在水平及垂直方向变动,我们必须打开这个开关:


    automaticOrientation: true

我们也尝试使用:

    onWidthChanged: {
        console.log("Main width is changed: " + width)
    }

无论我们怎么晃动我们的手机,我们发现,上面的方法只被调用过一次,再也没有被调用过。所以用这种方法不可以。


我们也尝试在Page中使用同样的伎俩:


    Page {
        id: page1
        title: i18n.tr("Orientation")
        anchors.fill: parent

        onWidthChanged: {
            console.log("Page width is changed: " + width);
        }

    ...
    }

我们发现:


qml: PageStack height is changed: 768
qml: orientation: 1
qml: Page width is changed: 768
qml: PageStack width is changed: 768
qml: root width: 768
qml: PageStack height is changed: 1222
qml: orientation: 4
qml: Page width is changed: 1222

这个Page的width是有变化的。为了方便,我们使用了PageStack来侦测width的变化:


    PageStack {
        id: pageStack
        anchors.fill: parent

        onWidthChanged: {
            console.log("PageStack width is changed: " + width);
            console.log("root width: " + root.width);
        }

        onHeightChanged: {
            console.log("PageStack height is changed: " + height);
        }
    }

在我们的MainView中,我们可以定义一个变量:


 property bool isLandscape: pageStack.width > pageStack.height ? true : false

这样通过这个变量,我们很容知道我们的应用是在什么一个方位的。

另外,我们也可以通过OrientationSensor来侦测手机方位的变化:

    function displayOrientation(reading) {
        orientation.text = "unknown"
        console.log("orientation: " + reading.orientation);

        if ( reading.orientation === OrientationReading.TopUp) {
            orientation.text = "TopUp";
        } else if ( reading.orientation === OrientationReading.TopDown) {
            orientation.text = "TopDown";
        } else if ( reading.orientation === OrientationReading.LeftUp) {
            orientation.text = "LeftUp";
        } else if ( reading.orientation === OrientationReading.RightUp) {
            orientation.text= "RightUp";
        } else if ( reading.orientation === OrientationReading.FaceDown) {
            orientation.text = "FaceDown";
        }  else if ( reading.orientation === OrientationReading.FaceUp) {
            orientation.text = "FaceUp";
        }
    }

    OrientationSensor {        
        id: sensor
        active: true
        alwaysOn: true
        onReadingChanged: {
            displayOrientation(reading);
        }
    }


运行一下我们的应用,可以看到:


  


整个项目的源码在:git clone https://gitcafe.com/ubuntu/oirentation.git



作者:UbuntuTouch 发表于2015/5/20 11:41:17 原文链接
阅读:215 评论:0 查看评论

Read more
UbuntuTouch

Item是QML语言中最基本的元素。有时为了方便,我们可以列出它里面的所有的属性,信号及方法。我们可以通过这个方法来修改我们的属性等。在QML语言中,所有的可视的控件都是继承于Item的。


下面我们来通过一个例子来展示如何这么做。我们可以设计一个简单的QML应用如下:


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "properties.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(100)
    height: units.gu(75)

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

        Rectangle {
            id: rect
            x: 0; y: 0
            width: 100; height: 100
            color: "blue"

            Component.onCompleted: {
                var keys = Object.keys(rect);
                for(var i = 0; i < keys.length; i++) {
                    var key = keys[i];
                    // prints all properties, signals, functions from object
                    console.log(key + ' : ' + rect[key]);

                    if (key === "x") {
                        rect[key] = 100;
                    }
                }
            }
        }

    }
}


这里rect最初的坐标为(0,0)。在Component.onCompleted中,我们遍历所有的属性,信号及方法。我们把x的值修改为100。最后运行的结果如下:



在Qt Creator的“Application Output”窗口中,我们可以看到:


qml: objectName : 
qml: parent : Page11_QMLTYPE_42(0x1a55340)
qml: data : [object Object]
qml: resources : [object Object]
qml: children : [object Object]
qml: x : 0
qml: y : 0
qml: z : 0
qml: width : 100
qml: height : 100
qml: opacity : 1
qml: enabled : true
qml: visible : true
qml: visibleChildren : [object Object]
qml: states : [object Object]
qml: transitions : [object Object]
qml: state : 
qml: childrenRect : QRectF(0, 0, 0, 0)
qml: anchors : QQuickAnchors(0x1a49840)
qml: left : QVariant(QQuickAnchorLine)
qml: right : QVariant(QQuickAnchorLine)
qml: horizontalCenter : QVariant(QQuickAnchorLine)
qml: top : QVariant(QQuickAnchorLine)
qml: bottom : QVariant(QQuickAnchorLine)
qml: verticalCenter : QVariant(QQuickAnchorLine)
qml: baseline : QVariant(QQuickAnchorLine)
qml: baselineOffset : 0
qml: clip : false
qml: focus : false
qml: activeFocus : false
qml: activeFocusOnTab : false
qml: rotation : 0
qml: scale : 1
qml: transformOrigin : 4
qml: transformOriginPoint : QPointF(50, 50)
qml: transform : [object Object]
qml: smooth : true
qml: antialiasing : false
qml: implicitWidth : 0
qml: implicitHeight : 0
qml: layer : QQuickItemLayer(0x1b90010)
qml: color : #0000ff
qml: gradient : null
qml: border : QQuickPen(0x1b8bd50)
qml: radius : 0
qml: objectNameChanged : function() { [code] }
qml: childrenRectChanged : function() { [code] }
qml: baselineOffsetChanged : function() { [code] }
qml: stateChanged : function() { [code] }
qml: focusChanged : function() { [code] }
qml: activeFocusChanged : function() { [code] }
qml: activeFocusOnTabChanged : function() { [code] }
qml: parentChanged : function() { [code] }
qml: transformOriginChanged : function() { [code] }
qml: smoothChanged : function() { [code] }
qml: antialiasingChanged : function() { [code] }
qml: clipChanged : function() { [code] }
qml: windowChanged : function() { [code] }
qml: childrenChanged : function() { [code] }
qml: opacityChanged : function() { [code] }
qml: enabledChanged : function() { [code] }
qml: visibleChanged : function() { [code] }
qml: visibleChildrenChanged : function() { [code] }
qml: rotationChanged : function() { [code] }
qml: scaleChanged : function() { [code] }
qml: xChanged : function() { [code] }
qml: yChanged : function() { [code] }
qml: widthChanged : function() { [code] }
qml: heightChanged : function() { [code] }
qml: zChanged : function() { [code] }
qml: implicitWidthChanged : function() { [code] }
qml: implicitHeightChanged : function() { [code] }
qml: update : function() { [code] }
qml: grabToImage : function() { [code] }
qml: grabToImage : function() { [code] }
qml: contains : function() { [code] }
qml: mapFromItem : function() { [code] }
qml: mapToItem : function() { [code] }
qml: forceActiveFocus : function() { [code] }
qml: forceActiveFocus : function() { [code] }
qml: nextItemInFocusChain : function() { [code] }
qml: nextItemInFocusChain : function() { [code] }
qml: childAt : function() { [code] }
qml: colorChanged : function() { [code] }
qml: radiusChanged : function() { [code] }

这些都是我们可以用到的。通过这个方法,我们可以全面地了解rect的所有属性。特别适用于一些动态生产的控件。我们可以用来修改它们的一些属性等。

项目的源码在:git clone https://gitcafe.com/ubuntu/properties.git

作者:UbuntuTouch 发表于2015/5/21 9:47:19 原文链接
阅读:294 评论:0 查看评论

Read more
UbuntuTouch

我们在有些的时候,需要在触屏的时候感知到触觉。那么我们怎么在QML应用是实现这个功能呢?


在Ubuntu 15.04的Ubuntu.Component 1.2模块中,我们有如下的一个API:


https://developer.ubuntu.com/api/apps/qml/sdk-15.04/Ubuntu.Components.Haptics/


具体的描述可以参阅相应的API文档。这里我们只简单地把我们的测试程序列出来:


import QtQuick 2.0
import Ubuntu.Components 1.2

/*!
    \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: "haptics.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(100)
    height: units.gu(75)

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

        Column {

            Item {
                implicitWidth: units.gu(20)
                implicitHeight: units.gu(5)
                Label {
                    text: "Press me"
                    anchors.fill: parent
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: Haptics.play()
                }
            }

            Item {
                implicitWidth: units.gu(25)
                implicitHeight: units.gu(5)
                Label {
                    text: "Press me again"
                    anchors.fill: parent
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: Haptics.play({duration: 25, attackIntensity: 0.7})
                }
            }
        }
    }
}

这里我们使用了Haptics.play()来完成相应的功能。当我们触控“Press me”时,我们会感觉到屏幕的触动。当然这也依赖于我们在“系统设置”中的设置。我们也可以通过改变Haptics.play()其中的参数来完成不同时间长度及震动大小的触觉。




整个项目的源码在: git clone https://gitcafe.com/ubuntu/haptics.git


作者:UbuntuTouch 发表于2015/5/21 10:21:10 原文链接
阅读:139 评论:0 查看评论

Read more
UbuntuTouch

在Ubuntu平台里,有一个Clipboard API的接口。在这篇文章中,我们将介绍如何使用该API接口来复制和粘贴内容。


具体的API介绍,可以在网址:


https://developer.ubuntu.com/api/apps/qml/sdk-15.04/Ubuntu.Components.Clipboard/


为了测试Clipboard的功能,我们使用了如下的代码:


import QtQuick 2.0
import Ubuntu.Components 1.2

/*!
    \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: "clipboard.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("clipboard")

        Column {
            clip: true
            anchors.centerIn: parent
            spacing: units.gu(1)

            Image {
                id: image
                width: 50
                height: 50
                source: "images/pic1.jpg"
            }

            TextArea {
                id: editor
                text: "This is cool!"
            }
            MimeData {
                id: mimeData
                color: "green"
                text: editor.text
                urls: [image.source]
            }
            Button {
                text: "Copy method one"
                onClicked: Clipboard.push(mimeData)
            }
            Button {
                text: "Copy method two"
                onClicked: {
//                    Clipboard.push(editor.text);
//                    Clipboard.push(["application/x-color", "green"]);
                      Clipboard.push(["application/x-color", "red",
                                      "text/plain", editor.text,
                                     "text/plain", [image.source]
                                     ]);
                }
            }
            Button {
                text: "Copy method three"
                onClicked: {
                    var mimeData = Clipboard.newData();
                    mimeData.text = editor.text;
                    mimeData.color = "green";
                    mimeData.urls.push(image.source);
                    Clipboard.push(mimeData);
                }
            }

            Row {
                spacing: units.gu(1)
                Button {
                    text: "Paste"
                    onClicked: {
                        editor1.text = Clipboard.data.text;
                        editor1.color = Clipboard.data.color;
                        image1.source = Clipboard.data.urls[0];

                    }
                }
                Button {
                    text: "Clear the textarea below"
                    onClicked: {
//                        Clipboard.clear();
                        editor1.text = "";
                        image1.source = "";
                    }
                }
            }
            TextArea {
                id: editor1
            }

            Image {
                id: image1
                width: 50
                height: 50
            }
        }
    }
}


我们可以运行我们的应用:


   


我们可以点击“Copy method one”按钮,然后点击“Paste”。我们可以看到右边的图,把颜色及内容复制过来了。我们可以使用同样的方法一个Image等等。


项目的源码在:git clone https://gitcafe.com/ubuntu/clipboard.git

作者:UbuntuTouch 发表于2015/5/21 14:50:13 原文链接
阅读:190 评论:0 查看评论

Read more
UbuntuTouch

在QML应用中,我们经常要用到一个SplashScreen的画面来渲染我们的应用。那么我们怎么在自己的应用中做一个Splash Screen呢?


首先我们来设计一个自己的SplashScreen的QML模块:


SplashScreen.qml


import QtQuick 2.0

Item {
    id: splash
    anchors.fill: parent

    property int timeoutInterval: 2000
    signal timeout

    Image {
        id: splashImage
        anchors.fill: parent
        source: "images/splash.jpg"
    }

    Timer {
        interval: timeoutInterval; running: true; repeat: false
        onTriggered: {
            visible = false
            splash.timeout()
        }
    }
}


这里的设计非常简单。我们使用了一个图片来显示自己的画面。同时,我们使用了一个Timer。当Timer timeout时,我们就发生一个信号。这个信号,可以被外界所使用。这也是好一个好的方法让我们的模块和别的模块之间有一个好的隔离。我们可以在其它的模块中利用这个timout信号来实现我们想要做的事情。

Main.qml


在这个模块中,我们直接使用SplashScreen.qml:

import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "splashscreen.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("Splashscreen")

        MainWindow {
            id: mainwindow
            anchors.fill: parent
            visible: false
        }

        SplashScreen {
            onTimeout: {
                console.log("it times out!");
                mainwindow.visible = true;
            }
        }
    }
}

在SplashScreen中,我们捕获timeout信号,并使得MainWindow显现。当然我们也可以实现自己的一些特效。这样我们就可以实现我们想要的功能。

    

整个项目的源码在:git clone https://gitcafe.com/ubuntu/splashscreen.git

对于Qt C++比较熟悉的开发者来说,我们也可以使用如下的例子来完成相应的功能。我们可以使用“QtQuick App with QML UI (qmake)”模版。

SplashScreen.qml


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

    property int timeoutInterval: 2000
    signal timeout

    Page {
        title: i18n.tr("")

        Image {
            id: splashImage
            anchors.fill: parent
            source: "images/splash.jpg"
        }

    }
}


这里,我们使用了一个空的title,这样可以覆盖所有的页面。在main.cpp中,我们加入了一些新的代码:

main.cpp


#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QElapsedTimer>

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

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);

    view.setSource(QUrl(QStringLiteral("qrc:///SplashScreen.qml")));
    view.show();

    QElapsedTimer t;
    t.start();
    while(t.elapsed()<2000)
    {
        QCoreApplication::processEvents();
    }

    view.setSource(QUrl(QStringLiteral("qrc:///Main.qml")));

    view.show();
    return app.exec();
}
   

整个项目的源码在: git clone https://gitcafe.com/ubuntu/splashscreenqt.git




作者:UbuntuTouch 发表于2015/5/25 9:19:57 原文链接
阅读:354 评论:0 查看评论

Read more
UbuntuTouch

我们知道对于一些应用来说,比如导航,播放游戏或视频的应用来讲,手机经常进入到屏保的状态无疑对用户体验是一个很大的挑战。在这篇文章中,我们将介绍如何使用一些API来使得我们的应用不进入到屏保的状态。


我们有的开发者可能已经查看了我们的Ubuntu QML API网站:http://developer.ubuntu.com/api/qml/development/。在他的里面可能并没有发现我们想要的API接口。实际上我们已经有一个叫做QtSystemInfo的API,目前并没有太多的文档。并且其中很多的API必须是在unconfined下才可以被执行的。可喜的是,其中的ScreenSaver API并不需要这样做。它的使用非常地简单:


import QtSystemInfo 5.0

目前这个模块只有在手机或模拟器中才有。我们可以知己使用它里面提供的ScreenSaver来实现我们想要的功能:


        ScreenSaver {
            id: screenSaver
            screenSaverEnabled: true
        }


一旦我们把属性“screenSaverEnabled”设为true,我们的应用就不用进入到待机的画面中,并且它永远是亮屏的,只到你的手机电池消耗完为止 :)


source code: git clone https://gitcafe.com/ubuntu/sysinfo.git

作者:UbuntuTouch 发表于2015/5/25 11:33:58 原文链接
阅读:385 评论:0 查看评论

Read more
UbuntuTouch

对于一些应用来说,我们希望我们的手机的屏幕旋转时,它里面的内容也跟随着旋转。在iPhone里其实我们也可以看到这样类似的应用,无论你怎么旋转你的屏幕,在任何一个方向,你都可以玩你的游戏。


在Ubuntu平台里,有一个OrientationHelper的API。它实现了上面的要求。具体的API的接口地址为:


http://developer.ubuntu.com/api/apps/qml/sdk-14.10/Ubuntu.Components.OrientationHelper/


我们来通过一个实验来完成:


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "orientationhelper.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(100)
    height: units.gu(75)

    Page {
        title: i18n.tr("")

        Item {
            anchors.fill: parent

            OrientationHelper {
//                orientationAngle: 90

                Column {
                    spacing: units.gu(3)
                    Label {
                        text: "Automatically rotated"
                    }
                    Button {
                        text: "Automatically rotated"
                    }

                    Image {
                        width: units.gu(10)
                        height: units.gu(17)
                        source: "images/pic1.jpg"
                    }
                }
            }
        }
    }
}

在上面的OrientationHelper里面,我们放置了三个Control:Label, Button及Image。运行的效果如下:


  


   


在这里,我们关掉了Main.qml中的:


//    automaticOrientation: true

如果这个开关打开,可能不是这个效果。


整个项目的源码在:git clone https://gitcafe.com/ubuntu/orientationhelper.git





作者:UbuntuTouch 发表于2015/5/25 16:28:33 原文链接
阅读:252 评论:0 查看评论

Read more
UbuntuTouch

在Ubuntu QML设计中,我们可以使用Panel API来实现一个可以在屏幕边缘拖进或拖出的控制面板。用户只需要在屏幕的边缘滑动即可把Panel显现或影藏出来。


具体的设计非常简单:


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "panel.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("Panel")

        Panel {
            id: panel
            anchors {
                left: parent.left
                right: parent.right
                bottom: parent.bottom
            }
            height: units.gu(8)

            Rectangle {
                color: Theme.palette.normal.overlay
                anchors.fill: parent
                Row {
                    anchors.centerIn: parent
                    spacing: units.gu(2)

                    Repeater {
                        model: ["red", "blue", "green"]

                        Rectangle {
                            width: units.gu(8)
                            height: units.gu(4)
                            color: modelData
                            function trigger() {
                                print("Color rect is clicked");
                            }
                        }
                    }
                }
            }
        }

        Component.onCompleted: panel.open();
    }
}

在这里,我们设计了一个简单的在屏幕下方的一个Panel。我们简单地在里面划了三个自己方块。当然,我们也可以放上自己的Control,虽然在Control上面滑动时不能隐藏Panel (需要点击在非Control的地方就像在API文档中介绍的那样)。我们的Panel在应用启动时已经打开了。就像API文档介绍的那样,一般情况下,我们会选择MainView中的toolbar来实现相应的功能。


运行这个简单的程序:


    


整个项目的源码在:git clone https://gitcafe.com/ubuntu/Panel.git

作者:UbuntuTouch 发表于2015/5/26 10:09:10 原文链接
阅读:282 评论:0 查看评论

Read more
UbuntuTouch

在Ubuntu.Components 1.2中有一个新的控件叫做ListItem。它只出现在15.04的手机的Image中,所以对14.10手机的用户来说这个是不可以用的。就像API文档中提到的那样,它是为了为Ubuntu手机中的List及Grid来提供一个标准的设计而设计的。


说道ListItem,可能很多人和Ubuntu.Components.ListItems容易搞混。其实ListItem没有任何的layout,也就是说它可以很方便地让我们定制任何一个我们所需要的内容。相反,Ubuntu.Components.ListItems不可以让我们方便地定制我们所需要的高度。它的高度是固定,也有一些适合我们做List及Grid的layout。


为了说明问题,我们做了如下的测试应用:


import QtQuick 2.0
import Ubuntu.Components 1.2
import Ubuntu.Components.ListItems 1.0 as ListItems

/*!
    \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: "listitem.liu-xiao-guo"

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

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

    Page {
        title: i18n.tr("ListItem")
        clip:true


        Flickable {
            height: parent.height
            width: parent.width
            contentHeight: content.childrenRect.height

            Column {
                id: content
                anchors.fill: parent

                ListItems.Standard {
                    text: "Selectable standard list item"
                    selected: false
                    onClicked: selected = !selected
                }
                ListItems.Standard {
                    text: "List item with icon"
                    iconName: "compose"
                }
                ListItems.Standard {
                    text: "With a progression arrow"
                    progression: true
                }
                ListItems.Standard {
                    text: "Control"
                    control: Button {
                        text: "Click me"
                        width: units.gu(19)
                        onClicked: print("Clicked")
                    }
                    progression: true
                }

                ListItem {
                    id: listItem
                    height: 200
                    Row {
                        spacing: 20
                        Image {
                            width: 200
                            height: 200
                            source: "images/pic1.jpg"
                            fillMode: Image.PreserveAspectCrop
                        }

                        Button {
                            text: "Press me"
                            onClicked: {
                                console.log("Button is clicked");
                                listItem.destroy();
                            }
                        }
                    }
                    onClicked: console.log("clicked on ListItem")
                }

                ListItem {
                    leadingActions: ListItemActions {
                        actions: [
                            Action {
                                iconName: "delete"
                                onTriggered: {
                                    console.log("delete is triggered");
                                }
                            }
                        ]
                    }

                    Label {
                        text: "LeadingAction"
                    }

                    onClicked: console.log("clicked on ListItem with leadingActions set")
                }
                ListItem {
                    trailingActions: ListItemActions {
                        actions: [
                            Action {
                                iconName: "edit"

                                onTriggered: {
                                    console.log("edit is triggered!");
                                }
                            }
                        ]
                    }

                    Label {
                        text: "TrailingActions"
                    }

                    onClicked: console.log("clicked on ListItem with trailingActions set")
                }
                ListItem {
                    Label {
                        text: "onClicked implemented"
                    }
                    onClicked: console.log("clicked on ListItem with onClicked implemented")
                }
                ListItem {
                    Label {
                        text: "onPressAndHold implemented"
                    }
                    onPressAndHold: console.log("long-pressed on ListItem with onPressAndHold implemented")
                }
                ListItem {
                    Label {
                        text: "No highlight"
                    }

                    onClicked: console.log("clicked on No highlight");
                }


                ListView {
                    clip: true
                    width: parent.width
                    height: units.gu(50)

                    model: ListModel {
                        Component.onCompleted: {
                            for (var i = 0; i < 100; i++) {
                                append({tag: "List item #"+i});
                            }
                        }
                    }

                    delegate: ListItem {
                        Label {
                            text: modelData
                        }
                        color: dragMode ? "lightblue" : "lightgray"
                        onPressAndHold: ListView.view.ViewItems.dragMode =
                                        !ListView.view.ViewItems.dragMode
                    }
                    ViewItems.onDragUpdated: {
                        if (event.status == ListItemDrag.Moving) {
                            model.move(event.from, event.to, 1);
                        }
                    }
                    moveDisplaced: Transition {
                        UbuntuNumberAnimation {
                            property: "y"
                        }
                    }
                }
            }

        }

    }
}

在这里我们一定要注意的是:我们必须使用

import Ubuntu.Components 1.2

才可以使用ListItem。

在这里,我们也使用了“ListItems.Standard”来展示它和ListItem的不同。就像我们看到的那样:

                ListItem {
                    id: listItem
                    height: 200
                    Row {
                        spacing: 20
                        Image {
                            width: 200
                            height: 200
                            source: "images/pic1.jpg"
                            fillMode: Image.PreserveAspectCrop
                        }

                        Button {
                            text: "Press me"
                            onClicked: {
                                console.log("Button is clicked");
                                listItem.destroy();
                            }
                        }
                    }
                    onClicked: console.log("clicked on ListItem")
                }

ListItem可以自定义我们想要的高度200,但是ListItems做不到。这里我们画上一幅图,加上一个按钮。


另外,我们可以通过如下的代码:


                ListItem {
                    leadingActions: ListItemActions {
                        actions: [
                            Action {
                                iconName: "delete"
                                onTriggered: {
                                    console.log("delete is triggered");
                                }
                            }
                        ]
                    }

                    Label {
                        text: "LeadingAction"
                    }

                    onClicked: console.log("clicked on ListItem with leadingActions set")
                }
                ListItem {
                    trailingActions: ListItemActions {
                        actions: [
                            Action {
                                iconName: "edit"

                                onTriggered: {
                                    console.log("edit is triggered!");
                                }
                            }
                        ]
                    }

                    Label {
                        text: "TrailingActions"
                    }

                    onClicked: console.log("clicked on ListItem with trailingActions set")
                }

来实现leadingActions或trainingActions:


 

在应用的最后面,我们使用了一个ListView,并在ListView中使用ListItem。我们可以在ListView中长按列表中的内容,从而进入到可以drag-and-drop的模式,我们可以在列表中任意移动我们所需要的内容到我们想要的位置:

                ListView {
                    clip: true
                    width: parent.width
                    height: units.gu(50)

                    model: ListModel {
                        Component.onCompleted: {
                            for (var i = 0; i < 100; i++) {
                                append({tag: "List item #"+i});
                            }
                        }
                    }

                    delegate: ListItem {
                        Label {
                            text: modelData
                        }
                        color: dragMode ? "lightblue" : "lightgray"
                        onPressAndHold: ListView.view.ViewItems.dragMode =
                                        !ListView.view.ViewItems.dragMode
                    }
                    ViewItems.onDragUpdated: {
                        if (event.status == ListItemDrag.Moving) {
                            model.move(event.from, event.to, 1);
                        }
                    }
                    moveDisplaced: Transition {
                        UbuntuNumberAnimation {
                            property: "y"
                        }
                    }
                }
            }

  


整个项目的源码在:git clone https://gitcafe.com/ubuntu/listitem.git



作者:UbuntuTouch 发表于2015/5/26 14:00:18 原文链接
阅读:224 评论:0 查看评论

Read more
UbuntuTouch

我们知道JSON数据格式被广泛使用在很多的应用中,它可以帮我们保存我们应用的设置数据等。在QML中的ListViewGridView中,我们使用ListModel来显示它里面的数据。这个数据可以来源于xml或JSON。在ListView或GridView中,我们也可以动态修改ListModel中的数据。那么我们将如何保存这个数据呢?本篇文章也同样适用于xml格式的保存。这个练习就留个开发者自己了。当然保存ListModel中的数据也可以使用到SQLite数据库。这个因人而已!


为了方便我们的设计,我们使用了Ubuntu SDK中提供的“QML App with C++ plugin (qmake)”。这个项目的模版只适用于15.04及以上的target(在14.10中不被支持)。




在plugin中,我们设计了如下的fileio.cpp文件:


fileio.cpp

#include <QStandardPaths>
#include "fileio.h"

FileIO::FileIO(QObject *parent) : QObject(parent)
{
}

FileIO::~FileIO()
{
}

void FileIO::read()
{
    if(m_path.isEmpty()) {
        return;
    }

//    QFile file(m_path.toLocalFile());
    QFile file(m_path.path());

    if(!file.exists()) {
        qWarning() << "Does not exits: " << m_path.toLocalFile();
        return;
    }
    if(file.open(QIODevice::ReadOnly)) {
        QTextStream stream(&file);
        m_text = stream.readAll();
        emit textChanged(m_text);
        qDebug() << "Text has been successfully read!";
    }
}

void FileIO::write()
{
    if(m_source.isEmpty()) {
        return;
    }

    qDebug() << "filename: " << m_path.fileName();
    qDebug() << "path: " << m_path.path();

    QFile file(m_path.path());
    qDebug() << "File path: " << file.fileName();

    if(file.open(QIODevice::WriteOnly)) {       
        QTextStream stream(&file);
        stream << m_text;
        qDebug() << "Successfully write to file";
    } else {
        qWarning() << "Failed to write to the file: " << m_path;
    }
}

QString FileIO::source() const
{
    return m_source;
}

QString FileIO::text()
{
    qDebug() << "Going to read the text";
    read();
    return m_text;
}

void FileIO::setSource(QString source)
{
    if (m_source == source)
        return;

    m_source = source;
    emit sourceChanged(source);

    // at the same time update the path
    m_path = QUrl(getFilePath(source));
}

void FileIO::setText(QString text)
{
    if (m_text == text)
        return;

    m_text = text;
    write();
    emit textChanged(text);
}

QString FileIO::getFilePath(const QString filename) const
{
//    QString APP_ID = getenv("APP_ID");
//    QString app_pkgname = APP_ID.split('_')[0];
//    QString path = getenv("XDG_DATA_HOME") +
//            "/" + app_pkgname + "/" + filename;
//    qDebug() << "path: " << path;
//    return path;

    QString writablePath = QStandardPaths::
            writableLocation(QStandardPaths::DataLocation);
    qDebug() << "writablePath: " << writablePath;

    QString absolutePath = QDir(writablePath).absolutePath();
    qDebug() << "absoluePath: " << absolutePath;

    // We need to make sure we have the path for storage
    QDir dir(absolutePath);
    if ( dir.mkdir(absolutePath) ) {
        qDebug() << "Successfully created the path!";
    }

    QString path = absolutePath + "/" + filename;

    qDebug() << "path: " << path;

    return path;
}

在这里特别值得指出的是,由于Ubuntu应用的security,每个应用只有自己的独特的目录可以访问。在这个文件中,我们使用了Qt API QStandardPaths来获得应用的私有目录来访问。在以前的文章“如何使用Ubuntu手机平台中的照相机API来存储照片”中,我们也曾尝试使用环境变量的方法来获取这个目录,但是这些环境变量在电脑Desktop的环境中没有设置。


我们的主程序Main.qml也非常简单:

Main.qml

import QtQuick 2.4
import Ubuntu.Components 1.2
import Savejson 1.0
import "savedata.js" as Data

/*!
    \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: "savejson.liu-xiao-guo"

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

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

    Page {
        id: mainPage
        title: i18n.tr("savejson")
        property string path: ""

        FileIO {
            id: fileio
            source: "sample.json"
        }

        // The model:
        ListModel {
            id: fruitModel

            objectName: "fruitModel"

            ListElement {
                name: "Apple"; cost: 2.45
                image: "pics/apple.jpg"
                description: "Deciduous"
            }
            ListElement {
                name: "Banana"; cost: 1.95
                image: "pics/banana.jpg"
                description: "Seedless"
            }
            ListElement {
                name: "Cumquat"; cost: 3.25
                image: "pics/cumquat.jpg"
                description: "Citrus"
            }
            ListElement {
                name: "Durian"; cost: 9.95
                image: "pics/durian.jpg"
                description: "Tropical Smelly"
            }
        }

        Component {
            id: listDelegate

            ListItem {
                id: delegateItem
                width: listView.width; height: units.gu(10)
                onPressAndHold: ListView.view.ViewItems.dragMode =
                                !ListView.view.ViewItems.dragMode

                Image {
                    id: pic
                    height: parent.height - units.gu(1)
                    width: height
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
                    anchors.leftMargin: units.gu(0.5)
                    source: image
                }

                Column {
                    id: content
                    anchors.top: parent.top
                    anchors.left: pic.right
                    anchors.leftMargin: units.gu(2)
                    anchors.topMargin: units.gu(1)
                    width: parent.width - pic.width - units.gu(1)
                    height: parent.height
                    spacing: units.gu(1)

                    Label {
                        text: name
                    }

                    Label { text: description }

                    Label {
                        text: '$' + Number(cost).toFixed(2)
                        font.bold: true
                    }
                }


                trailingActions: ListItemActions {
                    actions: [
                        Action {
                            iconName: "add"

                            onTriggered: {
                                console.log("add is triggered!");
                                fruitModel.setProperty(index, "cost", cost + 0.25);
                            }
                        },
                        Action {
                            iconName: "remove"

                            onTriggered: {
                                console.log("remove is triggered!");
                                fruitModel.setProperty(index, "cost", Math.max(0,cost-0.25));
                            }
                        },
                        Action {
                            iconName: "delete"

                            onTriggered: {
                                console.log("delete is triggered!");
                                fruitModel.remove(index)
                            }
                        }
                    ]
                }

                color: dragMode ? "lightblue" : "lightgray"

                ListView.onAdd: SequentialAnimation {
                    PropertyAction { target: delegateItem; property: "height"; value: 0 }
                    NumberAnimation { target: delegateItem; property: "height"; to: delegateItem.height; duration: 250; easing.type: Easing.InOutQuad }
                }

                ListView.onRemove: SequentialAnimation {
                    PropertyAction { target: delegateItem; property: "ListView.delayRemove"; value: true }
                    NumberAnimation { target: delegateItem; property: "height"; to: 0; duration: 250; easing.type: Easing.InOutQuad }

                    // Make sure delayRemove is set back to false so that the item can be destroyed
                    PropertyAction { target: delegateItem; property: "ListView.delayRemove"; value: false }
                }
            }

        }

        ListView {
            id: listView
            anchors.fill: parent
            anchors.margins: 20
            model: fruitModel
            delegate: listDelegate

            ViewItems.onDragUpdated: {
                if (event.status === ListItemDrag.Moving) {
                    model.move(event.from, event.to, 1);
                }
            }
            moveDisplaced: Transition {
                UbuntuNumberAnimation {
                    property: "y"
                }
            }
        }

        Row {
            anchors.bottom: parent.bottom
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.bottomMargin: units.gu(1)
            spacing: units.gu(1)

            Button {
                id: save
                text: "Save JSON"

                onClicked: {
                    console.log("Going to save data!")
                    var data = fruitModel;
                    console.log("model data: " + JSON.stringify(fruitModel, null, 4));

                    var res = Data.serialize(data);
                    console.log("res: " + res);
                    fileio.text = res;
                }
            }

            Button {
                id: load
                text: "Load JSON"

                onClicked: {
                    var json = JSON.parse(fileio.text);
                    console.log("count: " + json.fruits.length);

                    fruitModel.clear();
                    var count = json.fruits.length;
                    for (var i in json.fruits) {
                        var fruit = json.fruits[ i ];
                        console.log("name: " + fruit.name);
                        console.log("image: " + fruit.image );
                        console.log("description: " + fruit.description);
                        console.log("cost: " + fruit.cost);
                        fruitModel.append( fruit );
                    }

                }
            }

        }
    }
}


运行我们的应用,我们的界面如下:


   


 


我们创建了一个ListView列表。在列表中,我们可以通过“+”及“-”来修改水果的价钱,我们也可以删除一个水果。当然,我们也可以长按列表并移动列表中的项来重新排序我们的水果顺序。


最重要的是,我们可以通过FileIO来存储或读取我们所需要的JSON文件。为了方便,我们设置了一个“sample.json”。它在电脑中的路径为“:~/.local/share/savejson.liu-xiao-guo$”。


我们使用了如下的方法来存储我们的JSON文件格式:


function serialize(model) {
    var res = "{ \"fruits\": [\n";

    console.log("count: " + model.count);

    for(var i = 0; i < model.count; ++i) {
        res += "\n{\t";
        var e = model.get(i);
        res += "\"name\": \""   + e.name + "\",\n\t";
        res += "\"image\": \"" + e.image + "\",\n\t";
        res += "\"description\": \"" + e.description + "\",\n\t";
        res += "\"cost\": " + e.cost + "\n\t";

        // The last one should not have the ending ","
        if ( i === model.count -1)
            res += "\n}";
        else
            res += "\n},";
    }

    res += "\n]}";

    console.log("res: " + res );
    return res;
}

存储的JSON文件例子为:


{ 
"fruits": [

	{       "name": "Banana",
	        "image": "pics/banana.jpg",
	        "description": "Seedless",
	        "cost": 2.2
	
	},
	{       "name": "Cumquat",
	        "image": "pics/cumquat.jpg",
	        "description": "Citrus",
	        "cost": 3.25
	
	},
	{       "name": "Durian",
	        "image": "pics/durian.jpg",
	        "description": "Tropical Smelly",
	        "cost": 9.95
	
	}
	]
}


在我们修改完设置后,我们可以选择退出应用。在下次启动应用后,我们可以选择“Load JSON”按钮,我们可以看到上次修改的内容被成功地重新装载到应用中。


整个项目的源码在: git clone https://gitcafe.com/ubuntu/savejson.git




作者:UbuntuTouch 发表于2015/5/27 13:32:42 原文链接
阅读:232 评论:0 查看评论

Read more
UbuntuTouch

在一些应用中我们需要判断键盘是否已经出现。如果出现的话,我们有时不希望有键盘。我们也可以通过软件的方法让键盘消失。在这篇文章中,我们来介绍如何来实现这个。


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "inputmethod.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("inputmethod")

        Column {
            spacing: units.gu(2)

            TextField {
                id: input
            }

            Text {
                text: "Input method: " + "<b>" + Qt.inputMethod.visible + "</b>"
            }

            Button {
                text: "Hide Input method"
                onClicked: {
                    Qt.inputMethod.hide();
                }
            }

        }

        Component.onCompleted: {
            var keys = Object.keys(Qt.inputMethod);
            for(var i = 0; i < keys.length; i++) {
                var key = keys[i];
                // prints all properties, signals, functions from object
                console.log(key + ' : ' + Qt.inputMethod[key]);

                if (key === "locale") {
                    console.log("Native lang: " + Qt.inputMethod[key].nativeLanguageName);
                }
            }

            var rect = Qt.inputMethod.keyboardRectangle;
            console.log("keyboard size: " + rect.width + " " + rect.height);
        }
    }
}


  


在上面的例子里,我们可以看到当键盘没有启动时:


Qt.inputMethod.visible 


为false。当键盘启动后,它的值变为true。当然我们也可以通过方法:


 Qt.inputMethod.hide();

来让键盘消失。


具体更多关于Qt.inputMethod的介绍,请参阅:http://doc.qt.io/qt-5/qinputmethod.html



作者:UbuntuTouch 发表于2015/5/27 13:57:16 原文链接
阅读:329 评论:0 查看评论

Read more
UbuntuTouch

在Qt中,我们可以利用Qt全局变量来获取一些对我们应用有用的信息。在下面的应用中,我们可以获取如下的信息:

  


在上面,我们可以看到应用的状态,运行的输入参数,应用的名称及操作系统等。


我们的应用设计非常简单:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItems

/*!
    \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: "application.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)

    property string locale: ""

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

        Flickable {
            anchors.fill: parent
            contentHeight: content.childrenRect.height

            Column {
                id: content
                anchors.fill: parent
                spacing: units.gu(0.5)

                ListItems.SingleValue {
                    text: "Active"
                    value: Qt.application.state == Qt.ApplicationActive
                }

                ListItems.SingleValue {
                    text: "State"
                    value: {
                        switch(Qt.application.state) {
                        case Qt.ApplicationActive:
                            return "Active";
                        case Qt.ApplicationInactive:
                            return "Inactive";
                        case Qt.ApplicationSuspended:
                            return "Suspended";
                        case Qt.ApplicationHidden:
                            return "Hidden";
                        default:
                            return "Unknown";
                        }
                    }
                }

                ListItems.SingleValue {
                    text: "Layout direction"
                    value: {
                        switch(Qt.application.layoutDirection) {
                        case Qt.LeftToRight:
                            return "Left to right";
                        case Qt.RightToLeft:
                            return "Right to left";
                        default:
                            return "Unknown";
                        }
                    }
                }

                ListItems.Subtitled {
                    text: "aruguments"
                    subText: {
                        console.log("arguments: " + Qt.application.arguments);
                        var arguments = Qt.application.arguments;
                        var content = arguments.join(" ");
                        return content;
                    }
                }

                ListItems.SingleValue {
                    text: "name"
                    value: Qt.application.name
                }

                ListItems.SingleValue {
                    text: "domain"
                    value: {
                        console.log("version: " + Qt.application.version);
                        return Qt.application.domain;
                    }
                }

                ListItems.SingleValue {
                    text: "support multiple windows"
                    value: Qt.application.supportsMultipleWindows
                }

                ListItems.SingleValue {
                    text: "OS"
                    value: Qt.platform.os
                }

                ListItems.SingleValue {
                    text: "Locale"
                    value: locale
                }
            }
        }

        Component.onCompleted: {
            var keys = Object.keys(Qt.application);
            for(var i = 0; i < keys.length; i++) {
                var key = keys[i];
                // prints all properties, signals, functions from object
                console.log(key + ' : ' + Qt.application[key]);
            }

            locale = Qt.inputMethod["locale"].nativeLanguageName;
            console.log("locale: " + locale);
        }
    }
}


整个应用的源码在:git clone https://gitcafe.com/ubuntu/application.git

作者:UbuntuTouch 发表于2015/5/28 10:25:59 原文链接
阅读:270 评论:0 查看评论

Read more
UbuntuTouch

我们知道JSON数据在很多web service中被广泛使用。它在我以前的文章中都有被提到:


- 如何读取一个本地Json文件并查询该文件展示其内容

- 如何在QML应用中使用Javascript解析JSON


在今天的这篇文章中,我来介绍一种类似像XmlListModel(解析XML)的方法来解析我们的JSON。这个方法更加简单直接。关于JSONListModel的介绍可以参照地址https://github.com/kromain/qml-utils


我们今天就利用JSONListModel的网址提供的例程来做说明。


我们首先来看一看JSONListModel的写法:


JSONListModel.qml


/* JSONListModel - a QML ListModel with JSON and JSONPath support
 *
 * Copyright (c) 2012 Romain Pokrzywka (KDAB) (romain@kdab.com)
 * Licensed under the MIT licence (http://opensource.org/licenses/mit-license.php)
 */

import QtQuick 2.0
import "jsonpath.js" as JSONPath

Item {
    property string source: ""
    property string json: ""
    property string query: ""

    property ListModel model : ListModel { id: jsonModel }
    property alias count: jsonModel.count

    onSourceChanged: {
        var xhr = new XMLHttpRequest;
        xhr.open("GET", source);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == XMLHttpRequest.DONE)
                json = xhr.responseText;
        }
        xhr.send();
    }

    onJsonChanged: updateJSONModel()
    onQueryChanged: updateJSONModel()

    function updateJSONModel() {
        jsonModel.clear();

        if ( json === "" )
            return;

        var objectArray = parseJSONString(json, query);
        for ( var key in objectArray ) {
            var jo = objectArray[key];
            jsonModel.append( jo );
        }
    }

    function parseJSONString(jsonString, jsonPathQuery) {
        var objectArray = JSON.parse(jsonString);
        if ( jsonPathQuery !== "" )
            objectArray = JSONPath.jsonPath(objectArray, jsonPathQuery);

        return objectArray;
    }


在这里,我们可以学习一些如何包装一个模块并使得我们的model被外界所使用。这个模块的写法很值得推荐。对于一些大型的软件来说,我们可以通过这样的方法来独立完成我们的model,并被其它模块而使用。可以实现UI和数据的分割。


首先,这个模块定义了一个source属性。当source一旦被设置后,onSourceChanged将被自动调用,从而发出请求去得到数据。当数据得到后, json的数值发生改变,进而onJsonChanged将被自动调用。最终在updateJSONModel()中,模块所定义的jsonModel将被更新,以被外面的ListView或其它的Control所使用。同样,每当query发生改变后,model的数据也将被重新修改。


就像我之前的例子一样,它使用了jsonpath.js模块来实现xpath的查询功能。


在例程里,我们可以直接使用JSONListModel来为我们的ListView填充数据:


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \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: "test1.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(100)
    height: units.gu(75)

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

        Column {
            spacing: units.gu(1)
            anchors {
                margins: units.gu(2)
                fill: parent
            }

            Label {
                id: label
                objectName: "label"

                text: i18n.tr("Hello..")
            }

            Button {
                objectName: "button"
                width: parent.width

                text: i18n.tr("Tap me!")

                onClicked: {
                    label.text = i18n.tr("..world!")
                }
            }
        }
    }
}


jsonData.txt


{ "store": {
    "book": [
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}



通过不同的查询,我们可以得到JSON数据中不同条件下的数据。应用的展示效果如下:




项目的源码在: git clone https://gitcafe.com/ubuntu/jsonlistmodeltest.git



作者:UbuntuTouch 发表于2015/5/28 14:48:14 原文链接
阅读:241 评论:0 查看评论

Read more