Canonical Voices

Daniel Holbach

Thanks to the tireless work of Oliver Grawert we now have a handy tool called node-snapper which very easily turns your node.js project into a .snap package. It will automatically take care of bundling required libraries, other related node.js projects and will make a multi-arch ("fat") package available, so it will immediately work on armhf and amd64 architectures.

Intrigued? Check out our tutorial Turning node.js projects into snap packages.

Read more
David Callé

Have you taken Vision Mobile’s developer survey? The survey is covering your development in Mobile, Desktop, Cloud, and IoT this year, there is something all devs can contribute towards and help shape the findings for this survey.

Participating is easy - take the 10-minute Developer Skill Census survey and enter a draw to win prizes such as the BQ Aquaris E4.5 Ubuntu Edition, iPhone 6, Apple Sports Watch, Oculus Rift Dev Kit, and many more. A free chapter from one of VisionMobile’s premium paid reports will also be given immediately upon completion, taking a close look at app profits & costs.

The survey closes on 5th June - enter the survey now

Thanks to everyone who has already completed the survey!

Read more
UbuntuTouch

在前面的文章“如何在QML中使用ListView并导航到其它页面中”中,我们已经介绍了各种在ListView中导航到其它页面的方法。在这篇文章中,我来介绍如何建立一个expandable的ListView。通过这样的方法,ListView可以不用导航到其它的页面中,但是它可以通过状态的控制占据整个页面,而得到显示。


首先我们可以使用Ubuntu SDK来创建一个最简单的“QML App with Simple UI (qmlproject)”项目。我们的Main.qml非常简单:

Main.qml

import QtQuick 2.4
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: "expandinglist.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 {
        id: mainpage
        title: i18n.tr("expandinglist")
        flickable: null

        ListView {
            id: listView
            anchors.fill: parent
            clip: true
            model: RecipesModel {}
            delegate: RecipesDelegate {}
        }
    }
}


就像上面的代码显示的那样,我们需要一个model。为此,我们创建了如下的RecipesModel.qml文件:

RecipesModel.qml


import QtQuick 2.0

ListModel {
    ListElement {
        title: "Pancakes"
        picture: "content/pics/pancakes.jpg"
        ingredients: "<html>
                       <ul>
                        <li> 1 cup (150g) self-raising flour
                        <li> 1 tbs caster sugar
                        <li> 3/4 cup (185ml) milk
                        <li> 1 egg
                       </ul>
                      </html>"
        method: "<html>
                  <ol>
                   <li> Sift flour and sugar together into a bowl. Add a pinch of salt.
                   <li> Beat milk and egg together, then add to dry ingredients. Beat until smooth.
                   <li> Pour mixture into a pan on medium heat and cook until bubbles appear on the surface.
                   <li> Turn over and cook other side until golden.
                  </ol>
                 </html>"
    }
    ListElement {
        title: "Fruit Salad"
        picture: "content/pics/fruit-salad.jpg"
        ingredients: "* Seasonal Fruit"
        method: "* Chop fruit and place in a bowl."
    }
    ListElement {
        title: "Vegetable Soup"
        picture: "content/pics/vegetable-soup.jpg"
        ingredients: "<html>
                       <ul>
                        <li> 1 onion
                        <li> 1 turnip
                        <li> 1 potato
                        <li> 1 carrot
                        <li> 1 head of celery
                        <li> 1 1/2 litres of water
                       </ul>
                      </html>"
        method: "<html>
                  <ol>
                   <li> Chop vegetables.
                   <li> Boil in water until vegetables soften.
                   <li> Season with salt and pepper to taste.
                  </ol>
                 </html>"
    }
    ListElement {
        title: "Hamburger"
        picture: "content/pics/hamburger.jpg"
        ingredients: "<html>
                       <ul>
                        <li> 500g minced beef
                        <li> Seasoning
                        <li> lettuce, tomato, onion, cheese
                        <li> 1 hamburger bun for each burger
                       </ul>
                      </html>"
        method: "<html>
                  <ol>
                   <li> Mix the beef, together with seasoning, in a food processor.
                   <li> Shape the beef into burgers.
                   <li> Grill the burgers for about 5 mins on each side (until cooked through)
                   <li> Serve each burger on a bun with ketchup, cheese, lettuce, tomato and onion.
                  </ol>
                 </html>"
    }
    ListElement {
        title: "Lemonade"
        picture: "content/pics/lemonade.jpg"
        ingredients: "<html>
                       <ul>
                        <li> 1 cup Lemon Juice
                        <li> 1 cup Sugar
                        <li> 6 Cups of Water (2 cups warm water, 4 cups cold water)
                       </ul>
                      </html>"
        method: "<html>
                  <ol>
                   <li> Pour 2 cups of warm water into a pitcher and stir in sugar until it dissolves.
                   <li> Pour in lemon juice, stir again, and add 4 cups of cold water.
                   <li> Chill or serve over ice cubes.
                  </ol>
                 </html>"
    }
}



在这里,我们可以看到在文字中,我们可以使用html格式来格式化我们的文字。这对我们多样的显示是非常有用的。


我们最关键的设计在于RecipesDelegate.qml文件:

RecipesDelegate.qml

import QtQuick 2.0
import Ubuntu.Components 1.2

// Delegate for the recipes.  This delegate has two modes:
    // 1. List mode (default), which just shows the picture and title of the recipe.
    // 2. Details mode, which also shows the ingredients and method.
//Component {
//    id: recipeDelegate
//! [0]
    Item {
        id: recipe

        // Create a property to contain the visibility of the details.
        // We can bind multiple element's opacity to this one property,
        // rather than having a "PropertyChanges" line for each element we
        // want to fade.
        property real detailsOpacity : 0
//! [0]
        width: ListView.view.width
        height: units.gu(10)

        // A simple rounded rectangle for the background
        Rectangle {
            id: background
            x: 2; y: 2; width: parent.width - x*2; height: parent.height - y*2
            color: "ivory"
            border.color: "orange"
            radius: 5
        }

        // This mouse region covers the entire delegate.
        // When clicked it changes mode to 'Details'.  If we are already
        // in Details mode, then no change will happen.
//! [1]
        MouseArea {
            anchors.fill: parent
            onClicked: {
                console.log("recipe.y: " + recipe.y );
                console.log("origin.y: " + listView.originY );
                recipe.state = 'Details';
            }
        }

        // Lay out the page: picture, title and ingredients at the top, and method at the
        // bottom.  Note that elements that should not be visible in the list
        // mode have their opacity set to recipe.detailsOpacity.

        Row {
            id: topLayout
            x: 10; y: 10; height: recipeImage.height; width: parent.width
            spacing: 10

            Image {
                id: recipeImage
                width: units.gu(8); height: units.gu(8)
                source: picture
            }
//! [1]
            Column {
                width: background.width - recipeImage.width - 20; height: recipeImage.height
                spacing: 5

                Text {
                    text: title
                    font.bold: true; font.pointSize: units.gu(2)
                }

                SmallText {
                    text: "Ingredients"
                    font.bold: true
                    opacity: recipe.detailsOpacity
                }

                SmallText {
                    text: ingredients
                    wrapMode: Text.WordWrap
                    width: parent.width
                    opacity: recipe.detailsOpacity
                }
            }
        }

//! [2]
        Item {
            id: details
            x: 10; width: parent.width - 20

            anchors { top: topLayout.bottom; topMargin: 10; bottom: parent.bottom; bottomMargin: 10 }
            opacity: recipe.detailsOpacity
//! [2]
            SmallText {
                id: methodTitle
                anchors.top: parent.top
                text: "Method"
                font.pointSize: 12; font.bold: true
            }

            Flickable {
                id: flick
                width: parent.width
                anchors { top: methodTitle.bottom; bottom: parent.bottom }
                contentHeight: methodText.height
                clip: true

                Text { id: methodText; text: method; wrapMode: Text.WordWrap; width: details.width }
            }

            Image {
                anchors { right: flick.right; top: flick.top }
                source: "content/pics/moreUp.png"
                opacity: flick.atYBeginning ? 0 : 1
            }

            Image {
                anchors { right: flick.right; bottom: flick.bottom }
                source: "content/pics/moreDown.png"
                opacity: flick.atYEnd ? 0 : 1
            }
//! [3]
        }

        // A button to close the detailed view, i.e. set the state back to default ('').
        TextButton {
            y: 10
            anchors { right: background.right; rightMargin: 10 }
            opacity: recipe.detailsOpacity
            text: "Close"

            onClicked: recipe.state = '';
        }

        states: State {
            name: "Details"

            PropertyChanges { target: background; color: "white" }
            PropertyChanges { target: recipeImage; width: 130; height: 130 } // Make picture bigger
            PropertyChanges { target: recipe; detailsOpacity: 1; x: 0 } // Make details visible
            PropertyChanges { target: recipe; height: listView.height } // Fill the entire list area with the detailed view

            // Move the list so that this item is at the top.
            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

            // Disallow flicking while we're in detailed view
            PropertyChanges { target: recipe.ListView.view; interactive: false }
        }

        transitions: Transition {
            // Make the state changes smooth
            ParallelAnimation {
                ColorAnimation { property: "color"; duration: 500 }
                NumberAnimation { duration: 300; properties: "detailsOpacity,x,contentY,height,width" }
            }
        }
//    }
//! [3]
}


在这个delegate里,它有两个状态:

  • 默认的List模式。在这种模式下,它只显示一个图片及title
  • 详细模式。在这种模式下,除了显示上面的图片和title以外,还显示model中的ingredients及method
在详细模式下的状态为:

       states: State {
            name: "Details"

            PropertyChanges { target: background; color: "white" }
            PropertyChanges { target: recipeImage; width: 130; height: 130 } // Make picture bigger
            PropertyChanges { target: recipe; detailsOpacity: 1; x: 0 } // Make details visible
            PropertyChanges { target: recipe; height: listView.height } // Fill the entire list area with the detailed view

            // Move the list so that this item is at the top.
            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

            // Disallow flicking while we're in detailed view
            PropertyChanges { target: recipe.ListView.view; interactive: false }
        }

在这里,一定要注意:

            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

可以帮我们把当前的项移到ListView的窗口中。

我们运行我们的应用:

    

在上面的第二个图中,点击“Close”按钮,就可以回到List模式。

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


作者:UbuntuTouch 发表于2015/6/5 9:37:23 原文链接
阅读:430 评论: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