Canonical Voices

UbuntuTouch

[原]如何在QML中使用multitouch

在Qt QML中,它可以利用multitouch来做一些我们想做的事情。在今天的文章中,我们将介绍如何使用multitouch来做一些我们想做的事。


其实,在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: "multitouchtest.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("multitouchtest")

        MultiPointTouchArea {
            anchors.fill: parent
            touchPoints: [
                TouchPoint { id: point1 },
                TouchPoint { id: point2 },
                TouchPoint { id: point3 },
                TouchPoint { id: point4 }
            ]
        }

        Image {
            width: parent.width/5
            height: parent.height/5
            source: "images/image1.jpg"
            x: point1.x
            y: point1.y
        }

        Image {
            width: parent.width/5
            height: parent.height/5
            source: "images/image2.jpg"
            x: point2.x
            y: point2.y
        }

        Image {
            width: parent.width/5
            height: parent.height/5
            source: "images/image3.jpg"
            x: point3.x
            y: point3.y
        }

        Image {
            width: parent.width/5
            height: parent.height/5
            source: "images/image4.jpg"
            x: point4.x
            y: point4.y
        }

    }
}


如上面的介绍的那样,我们定义了一个“MultiPointTouchArea”。我们在Image中,绑定它们的坐标和TouchPoint在一起。这样,TouchPoint的坐标变化时,Image可以随时移动。 在它的里面,我们同时定义了4个TouchPoint。当我们只有一个手指触屏时,我们可以看见只有image1.jpg可以随着我们的手指移动:


  


当我们同时两个手指触屏时,我们可以看到同时可以有两个图片image1.jpg及image2.jpg来同时移动我们的画面:



同时3个手指触屏时,我们可以看到3个照片同时出现在屏幕上,并随我们的手指移动:




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


作者:UbuntuTouch 发表于2015/7/8 11:09:13 原文链接
阅读:273 评论:0 查看评论

Read more
UbuntuTouch

在游戏中动画的设计非常中要。在QML中,它提供了丰富的animation,但是有时我们需要对图像进行变化,就像放电影一样。在今天的这篇文章中,我们将设计一个可以变化图像的动画。我们可以通过Qt所提供的Sprite功能来实现。


为了设计的方便,我们先设计一个我们自己的bear动画,这个动画的图像大小为: 2048x256。它刚好是8副图256x256




在我们的Sprite设计中,我们想按照上述图像显示的顺序依次显示每个图像,这样就可以形成一个可以连续变化的动画效果。


直接把我们的动画设计文件BearSprite贴出来:


BearSprite.qml

import QtQuick 2.0

Item  {
    width: 256
    height: 256

    SpriteSequence {
        id: fishSprite
        anchors.fill: parent
        interpolate: false
        goalSprite: ""

        Sprite {
            name: "first"
            source: "./gfx/Bear2.png"
            frameWidth: 256
            frameHeight: 256
            frameCount: 1
            frameDuration: 800
            frameDurationVariation: 400
            to: { "second" : 1 }
        }

        Sprite {
            name: "second"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "third" : 1 }
        }

        Sprite {
            name: "third"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*2
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "fourth" : 1 }
        }

        Sprite {
            name: "fourth"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*3
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "fifth" : 1 }
        }

        Sprite {
            name: "fifth"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*4
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "sixth" : 1 }
        }

        Sprite {
            name: "sixth"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*5
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "seventh" : 1 }
        }

        Sprite {
            name: "seventh"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*6
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "eighth" : 1 }
        }

        Sprite {
            name: "eighth"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*7
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "first" : 1 }
        }

        Sprite { //WORKAROUND: This prevents the triggering of a rendering error which is currently under investigation.
            name: "dummy"
            source: "./gfx/Bear2.png"
            frameCount: 8
            frameWidth: 256
            frameHeight: 256
            frameX: 0
            frameDuration: 200
        }
    }

}



在上面的设计中,我们使用了一个SpriteSequence,里面放了一些我们所需要的Sprite。


        Sprite {
            name: "sixth"
            source: "./gfx/Bear2.png"
            frameCount: 1
            frameX: 256*5
            frameWidth: 256
            frameHeight: 256
            frameDuration: 800
            frameDurationVariation: 400
            to: { "seventh" : 1 }
        }

这里的每个Sprite的设计都几乎都差不多。每个Sprite都有一个自己的名字。这里注意frameX。它其实是在我们上面显示的图里的x坐标位置。比如256x5,表示的是滴5副图。另外,我们的frameHeight和frameWidth也是和原图的大小是一样的,虽然在实际的显示中这个大小可以在Main.qml中可以设置。


使用同样的方法,我们可以做一个FishSprite。


FishSprite.qml




import QtQuick 2.0
import QtMultimedia 5.0

Item {
    width: 64
    height: 64
    property real hp: 3

    SoundEffect {
        id: spawnSound
        source: "./audio/catch.wav"
        loops:SoundEffect.Infinite
    }

    SoundEffect {
        id: killedSound
        source: "./audio/catch-action.wav"
    }

    SpriteSequence {
        id: fishSprite
        anchors.fill: parent
        interpolate: false
        goalSprite: ""

        Sprite {
            name: "left"
            source: "./gfx/mob-idle.png"
            frameWidth: 64
            frameHeight: 64
            frameCount: 1
            frameDuration: 800
            frameDurationVariation: 400
            to: { "front" : 1 }
        }

        Sprite {
            name: "front"
            source: "./gfx/mob-idle.png"
            frameCount: 1
            frameX: 64
            frameWidth: 64
            frameHeight: 64
            frameDuration: 800
            frameDurationVariation: 400
            to: { "left" : 1, "right" : 1 }
        }

        Sprite {
            name: "right"
            source: "./gfx/mob-idle.png"
            frameCount: 1
            frameX: 128
            frameWidth: 64
            frameHeight: 64
            frameDuration: 800
            frameDurationVariation: 400
            to: { "front" : 1 }
        }


        Sprite { //WORKAROUND: This prevents the triggering of a rendering error which is currently under investigation.
            name: "dummy"
            source: "./gfx/melee-idle.png"
            frameCount: 8
            frameWidth: 64
            frameHeight: 64
            frameX: 0
            frameDuration: 200
        }

        NumberAnimation on x {
            id: fishSwim
            running: false
            property bool goingLeft: fishSprite.currentSprite == "right"
            to: goingLeft ? -360 : 360
            duration: 300
        }

        Component.onCompleted: {
            spawnSound.play()
        }
    }

    SpriteSequence {
        id: bubble
        width: 64
        height: 64
        scale: 0.4 + (0.2  * hp)
        interpolate: false
        goalSprite: ""

        Behavior on scale {
            NumberAnimation { duration: 150; easing.type: Easing.OutBack }
        }

        Sprite {
            name: "big"
            source: "./gfx/catch.png"
            frameCount: 1
            to: { "burst" : 0 }
        }

        Sprite {
            name: "burst"
            source: "./gfx/catch-action.png"
            frameCount: 3
            frameX: 64
            frameDuration: 200
        }

        Sprite { //WORKAROUND: This prevents the triggering of a rendering error which is currently under investigation.
            name: "dummy"
            source: "./gfx/melee-idle.png"
            frameCount: 8
            frameWidth: 64
            frameHeight: 64
            frameX: 0
            frameDuration: 200
        }
        SequentialAnimation on width {
            loops: Animation.Infinite
            NumberAnimation { from: width * 1; to: width * 1.1; duration: 800; easing.type: Easing.InOutQuad }
            NumberAnimation { from: width * 1.1; to: width * 1; duration: 1000; easing.type: Easing.InOutQuad }
        }
        SequentialAnimation on height {
            loops: Animation.Infinite
            NumberAnimation { from: height * 1; to: height * 1.15; duration: 1200; easing.type: Easing.InOutQuad }
            NumberAnimation { from: height * 1.15; to: height * 1; duration: 1000; easing.type: Easing.InOutQuad }
        }
    }
}


Main.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: "sprite.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: page
        title: i18n.tr("sprite")

        Column {
            anchors.fill: parent

            FishSprite {
                height: units.gu(30)
                width: units.gu(30)
            }

            BearSprite {
                id: bear
                height: units.gu(30)
                width: units.gu(30)

                NumberAnimation on x {
                    to: page.width
                    duration: 8*800

                    onRunningChanged: {
                        if ( running == false) {
                            bear.x = 0
                            start()
                        }
                    }
                }
            }
        }
    }
}


运行我们的QML应用:

     


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


作者:UbuntuTouch 发表于2015/7/8 13:38:39 原文链接
阅读:290 评论:0 查看评论

Read more
UbuntuTouch

在前面的文章“使用golang来设计我们的Ubuntu Scope”中,我们已经介绍了如何利用golang来开发Ubuntu Scope。在今天的文章中,我们来简单介绍一下如何使用golang来开发QML应用。这对于一些熟悉golang语言的,但是不是很熟悉C++的开发这来说,无疑是一个好的选择。虽然我们大多数的QML应用只需要QML加上一些Javascript的脚本即可,但是我们可以使用Qt C++或Go语言来拓展它的功能,来做一些需要计算或特殊功能的部分。


首先,我们来查看我们中国开发者dawndiy所做的一个repository:


https://github.com/dawndiy/ubuntu-go-qml-template


这个repository是基于另外一个repository: https://github.com/go-qml/qml


首先就像dawndiy在它的github里描述的那样:


安装Ubuntu SDK


我们按照连接“http://developer.ubuntu.com/start/ubuntu-sdk/installing-the-sdk/”来安装我们自己的SDK。我们也可以参照我的博客文章“Ubuntu SDK 安装”。


安装额外的包

$sudo apt-get install golang g++ qtdeclarative5-dev qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev qtdeclarative5-qtquick2-plugin

这些包是为了我们能够成功编译我们的Go+QML应用所必须的。


设置chroots


如果你已经在上面参照“Ubuntu SDK 安装”来安装自己的SDK的话,这部分的很多部分已经做了。我们执行如下的指令:

$git clone https://github.com/nikwen/ubuntu-go-qml-template.git
$cd ubuntu-go-qml-template
$chroot-scripts/setup-chroot.sh

我们可以在我们自己喜欢的目录中做上面的事情。在实际的操作中,我发现如果在没有VPN的情况下,安装可能不能成功,原因是它需要访问“storage.googleapis.com”网址来下载一些东西。不过这没关系,你们可以到dawndiy的github里下载。那里已经有你所需要的所有的东西。


我们来看一下setup-chroot.sh:

#!/bin/bash

DIR=$(dirname $(readlink -f "$0"))

echo "====================================="
echo "========== Creating chroot =========="
echo "====================================="
echo

sudo click chroot -a armhf -f ubuntu-sdk-14.10 -s utopic create
sudo click chroot -a armhf -f ubuntu-sdk-14.10 -s utopic upgrade

echo
echo "====================================="
echo "=== Installing packages in chroot ==="
echo "====================================="
echo

sudo click chroot -a armhf -f ubuntu-sdk-14.10 -s utopic maint apt-get install git qtdeclarative5-dev:armhf qtbase5-private-dev:armhf qtdeclarative5-private-dev:armhf libqt5opengl5-dev:armhf qtdeclarative5-qtquick2-plugin:armhf

GO_DIR=$DIR/../go-installation

mkdir -p $GO_DIR
cd $GO_DIR

$DIR/install-go-1-3-3.sh


在这里,我们可以看到它去下载ubuntu-sdk-14.10 的armhf。这个是为了来交叉汇编我们的应用,并编译ARM版本的可执行文件。当然我们可以设置为ubuntu-sdk-15.04。我们需要做一些改变。这个步骤一旦设置好了,就可以不用再做第二次了。


在Desktop下运行应用


$./run.sh

我们在Terminal中键入上面的命令,我们就可以在Desktop中看见如下的运行的应用:




应用中的qml文件可以在./share/ubuntu-go-qml-template/main.qml中找到:

main.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: "ubuntu-go-qml-template.nikwen"

    /*
     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("Simple")

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

            Label {
                id: label
                objectName: "label"

                text: ctrl.message
            }

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

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

                onClicked: ctrl.hello()
            }
        }
    }
}

在./src/ubuntu-go-qml-template/main.go,我们可以看到如下的代码:


package main

import (
        "gopkg.in/qml.v1"
        "log"
)

func main() {
        err := qml.Run(run)
        if (err != nil) {
                log.Fatal(err)
        }
}

func run() error {
        engine := qml.NewEngine()
        component, err := engine.LoadFile("share/ubuntu-go-qml-template/main.qml")
        if err != nil {
                return err
        }

        ctrl := Control{Message: "Hello from Go!"}
        context := engine.Context()
        context.SetVar("ctrl", &ctrl)

        win := component.CreateWindow(nil)
        ctrl.Root = win.Root()

        win.Show()
        win.Wait()
        return nil
}

type Control struct {
	Root    qml.Object
	Message string
}

func (ctrl *Control) Hello() {
        go func() {
                if (ctrl.Message == "Hello from Go!") {
                        ctrl.Message = "Hello from Go Again!"
                } else {
                        ctrl.Message = "Hello from Go!"
                }
                qml.Changed(ctrl, &ctrl.Message)
        }()
}

具体关于这些golang的介绍,大家可以参考http://godoc.org/gopkg.in/qml.v1


部署到手机中


$./install-on-device.sh

我们使用上面的命令来部署我们的Go应用到手机中:



我们可以看到它的运行和在Desktop上面的是完全一样的。我们可以在如下的目录中找到我们所需要的click安装文件:

bin/ubuntu-go-qml-template.nikwen_0.1_armhf.click

所有的源码包括下载的库等: git clone https://gitcafe.com/ubuntu/ubuntu-go-qml-template.git


作者:UbuntuTouch 发表于2015/7/10 13:36:25 原文链接
阅读:394 评论:0 查看评论

Read more
UbuntuTouch

[原]Qt跨平台的一个例程

我的同事penk在最近北京的Hackathon展示了一个在多平台的例程。很多开发者对这个挺感兴趣的。今天我就把这个资源介绍给大家。这是同一个用Qt写的应用,可以同时在Ubuntu Destkop,android, iOS接Ubuntu phone上可以同时运行的一个例程。


大家可以在地址:http://www.terrariumapp.com/。该项目的所有的源码也可以在地址:https://github.com/penk/terrarium-app。在github里有详细介绍如何编译并部署到不同的平台的指令。


今天,我也有幸在我的Ubuntu Desktop上实现了Qt在android手机上的运行。我从Qt SDK中移植过来一个应用samegame。它的源码在:


git clone https://gitcafe.com/ubuntu/samegame.git


在Ubuntu桌面上运行:





在Samsung Note3上运行:


    


在Android模拟器上运行:





在MX4上运行:




我们可以看出来。同样一个Qt/QML应用在不同的装置上不需要做任何的修改就可以在不同的平台上运行!这就是Qt/QML的魅力。由于硬件资源限制,我们不能尝试在iOS上的运行情况。有条件的开发者可以自己在自己的苹果装置中试一下!



作者:UbuntuTouch 发表于2015/7/13 14:23:13 原文链接
阅读:295 评论:0 查看评论

Read more
UbuntuTouch

在我们设计我们的QML应用时,我们想通过一个方法在一个地方来改变我们的设置文件,从而来修改整个应用的外观或使得所有的使用同一个设置的变量值得到修改。比如我们可以设置BaseUrl="http://api.map.baidu.com/telematics/v3/weather?” 属性,我们可能有几个QML文件都需要使用这个属性,那么我们怎么没做呢?一种办法就是在每个模块中都定义同样的属性。另外一种办法就是利用Singleton来集中定义在一个文件中,从而被所有的模块所使用。这样的方法同样适合我们style我们的应用。我们在一个地方修改设置,但是在所有的模块中都使用。这类似于C/C++中定义一些常量,在不同的.cpp文件中使用一样。


为了能够实现我们上面所需要的功能,我们设计了如下的Settings.qml文件:


Settings.qml


pragma Singleton
import QtQuick 2.0

QtObject {
    property int screenHeight: 960
    property int screenWidth: 640

    property string textSize: "x-large"
    property string textColor: "red"
}

首先,我们可以看到我们在文件的开始部分使用了pragam Singleton,表明这个文件在实例化时只能有一个实例。在它里面,我们做了一些小的设置。具体开发者需要什么样的设置,可以自己定义。

另外,我们必须在我们应用的根目录下添加如下的qmldir文件:


qmldir


singleton Settings 1.0 Settings.qml

这是一个声明文件。

为了展示我们如何使用,我们可以使用我先前创建的如下的项目:

git clone https://gitcafe.com/ubuntu/TabApp1.git

并对Tab1.qml和Tab2.qml做如下的修改:


Tab1.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
// Needed for singletons QTBUG-34418
import "."

Tab {
    title: i18n.tr("Tab 1")

    Action {
        id: reloadAction
        text: "Reload"
        iconName: "reload"
        onTriggered: {
            console.log("reload is clicked")
        }
    }

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page one")
            color: Settings.textColor
            fontSize: Settings.textSize
        }

        tools: ToolbarItems {
            ToolbarButton {
                action: reloadAction
            }
        }
    }
}


Tab2.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
// Needed for singletons QTBUG-34418
import "."

Tab {
    title: i18n.tr("Tab 2")

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page two")
            color: Settings.textColor
            fontSize: Settings.textSize
        }
    }
}


在这两个文件中,我们同时使用了同一个设置文件Settings.qml中所定义的textSize及textColor属性。当我们修个统一处的值时,所有使用它们的地方将自动被修改,而不需要在每一个文件中分别做修改,下面是我把文字设为“x-large”及“red”时的截图:

  


当我们在Settings.qml中设为“large”及“green”时的截图:

   


从上面可以看出来,我们在同一处修个可以改变整个应用的设置及外观。

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


嵌套QtObjects

如果你需要嵌套QtObjects以访问更多的属性,可以采用如下的模版:

// Settings.qml
.pragma Singleton
 
QtObject {
 
property QtObject window: QtObject{
 property color background: "white";
 }
 
property QtObject border: QtObject{
 property QtObject width: QtObject{
 property int normal: 1;
 property int big: 3;
 }
 
property QtObject color: QtObject{
 property color normal: "gray";
 property color focus: "blue";
 property color disabled: "red";
 }
 }
}




作者:UbuntuTouch 发表于2015/7/21 9:49:12 原文链接
阅读:227 评论:0 查看评论

Read more
UbuntuTouch

在今天的教程中,我们来显示一个QML的所有的属性。这个应用也上传到商店里去了。希望对所有的开发者有所帮助。我现在直接把我的代码贴出来:


Main.qml


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

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

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: "propertyviewer.xiaoguo"

    /*
     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 var obj: null
    property var datalist: []

    function createQmlObject(objName) {
        var objStr = "import QtQuick 2.4;
                      import QtQuick.XmlListModel 2.0;
                      import Qt.labs.folderlistmodel 2.1;
                      import Qt.labs.settings 1.0;
                      import QtQuick.Particles 2.0;
                      import QtQuick.Window 2.2;
//                      import QtTest 1.1;
                      import Ubuntu.Components 1.2;
                      import Ubuntu.Components.ListItems 1.0;
                      import Ubuntu.Components.Pickers 1.0;
                      import Ubuntu.Components.Popups 1.0;
                      import Ubuntu.Components.Styles 1.2;
                      import Ubuntu.Layouts 1.0;
                      import Ubuntu.PerformanceMetrics 1.0;
//                      import Ubuntu.Test 1.0;
                      import Ubuntu.Web 0.2;
                      import QtContacts 5.0;
                      import QtLocation 5.3;
                      import QtOrganizer 5.0;
                      import Ubuntu.Content 1.1;
                      import Ubuntu.DownloadManager 0.1;
                      import Ubuntu.OnlineAccounts 0.1;
                      import QtSensors 5.0;
//                      import QtAudioEngine 1.0;
                      import QtMultimedia 5.4;
                      import QtQml 2.2;"

        if ( pack.text != "" ) {
            objStr += "import " + pack.text + ";"
        }

        objStr += objName + "{ id: myid"
        objStr += "}";
        console.log("objStr: " + objStr);
        var obj = Qt.createQmlObject(objStr, page, "myobj");
        return obj
    }

    function getProperties() {
        console.log("it is clicked")
        if ( obj != undefined ) {
            obj.destroy();
        }

        obj = createQmlObject(type.text);

        var list = [];

        list.push(obj.toString());

        // Now get the properties of the obj
        var keys = Object.keys(obj);
        for( var i = 0; i < keys.length; i++ ) {
            var key = keys[ i ];
            var data = key + ' : ' + obj[ key ];
            list.push( data  );
        }

        console.log("Let's dump the data");
        datalist = list;

        for ( key in datalist) {
            console.log( datalist[key] );
        }
    }

    Page {
        id: page
        title: i18n.tr("PropertyViewer")

        property var datalist: ["good"]

        ListModel {
            id: mymodel
        }

        Column {
            anchors.fill: parent
            spacing: units.gu(1.5)

            RowLayout {
                id: search
                spacing: units.gu(1)
                width: parent.width

                TextField {
                    id: type
                    width: parent.width
                    Layout.preferredWidth: parent.width*0.7
                    placeholderText: "A QML type (eg. Rectangle)"
                    hasClearButton: true

                    onAccepted: {
                        getProperties()
                    }
                }

                Button {
                    text: "Get"

                    onClicked: {
                        getProperties()
                    }
                }
            }

            TextField {
                id: pack
                width: parent.width
                placeholderText: "Package name if not detected (eg. QtSensors 5.0) "
                hasClearButton: true
            }

            ListView {
                clip:true
                width: parent.width
                height: parent.height - search.height
                model: datalist;

                delegate: Label {
                    text: modelData
                    fontSize: "large"
                }
            }
        }
    }
}

在这里,我们通过Qt.createQmlObject来动态地创建一个QML的object,并使用这个Object来得到它所有的属性。

运行这个应用:

  


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





作者:UbuntuTouch 发表于2015/8/5 16:45:29 原文链接
阅读:34 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将介绍如何使用Loader来加载不同的QML文件来实现动态的UI。在之前的文章“如何使用Loader来动态载入一个基于item的Component”中,我们已经介绍了一些关于它的用法。Loader的好处是只有在我们需要的时候才装载我们所需要的QML文件,这样可以节省应用所需要的内存,也同时可以提高应用的启动时间(如果利用好的话)。下面我们以一个简单的例子来做一个介绍。更多关于动态生产QML UI的例子,请参阅“如何使用QML动态产生Component来完成我们的气球游戏(2)”。


MainScreen.qml


import QtQuick 2.0

Rectangle {
    id: root
    
    width: 600
    height: 400
    
    property int speed: 0
    
    SequentialAnimation {
        running: true
        loops: Animation.Infinite
        
        NumberAnimation { target: root; property: "speed"; to: 145; easing.type: Easing.InOutQuad; duration: 4000; }
        NumberAnimation { target: root; property: "speed"; to: 10; easing.type: Easing.InOutQuad; duration: 2000; }
    }
    // M1>>
    Loader {
        id: dialLoader
        
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: parent.top
        anchors.bottom: analogButton.top

        onLoaded: {
            binder.target = dialLoader.item;
        }
    }
    Binding {
        id: binder
        
        property: "speed"
        value: speed
    }
    // <<M1
    Rectangle {
        id: analogButton
        
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        
        color: "gray"
        
        width: parent.width/2
        height: 100
        
        Text {
            anchors.centerIn: parent
            text: "Analog"
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: root.state = "analog";
        }
    }
    
    Rectangle {
        id: digitalButton
        
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        
        color: "gray"
        
        width: parent.width/2
        height: 100
        
        Text {
            anchors.centerIn: parent
            text: "Digital"
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: root.state = "digital";
        }
    }
    
    state: "analog"
    
    // M3>>
    states: [
        State {
            name: "analog"
            PropertyChanges { target: analogButton; color: "green"; }
            PropertyChanges { target: dialLoader; source: "Analog.qml"; }
        },
        State {
            name: "digital"
            PropertyChanges { target: digitalButton; color: "green"; }
            PropertyChanges { target: dialLoader; source: "Digital.qml"; }
        }
    ]
    // <<M3
}

从上面的代码中可以看出来,在程序中,我们使用了一个dialLoader:

   Loader {
        id: dialLoader
        
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: parent.top
        anchors.bottom: analogButton.top

        onLoaded: {
            binder.target = dialLoader.item;
        }
    }

它的source没有被指定。在程序中,它是可以被动态设置的,从而达到改变UI的目的。另外我们要注意到“dialLoader.item”,它实际上是在QML被装载完后最顶层的那个Item。对我们来说,当Analog.qml被装载后,这个Item就是Ananlog.qml所代表的Item。每当Loader的source发生改变时,它先前创建的Item将会被自动地销毁。

在程序中,也设置了两个Rectangle,被用作按钮的用途。点击它时,可以改变当前Component的state,从而装载不同的qml,以达到改变UI的目的。在应用中,默认的状态是“analog”,而不是我们通常的“”状态。

在我们的手机上运行:

   

所有项目的源码在:git clone https://github.com/liu-xiao-guo/loaderclock.git



作者:UbuntuTouch 发表于2015/7/22 14:05:19 原文链接
阅读:221 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中介绍如何使用Javascript来动态生产画面。 我们在先前的例子中“如何使用QML动态产生Component来完成我们的气球游戏 (2)”已经对动态生产QML做了一些描述。也许那个项目比较复制,现在我来用一些简单的例子来说明一下,这样更加直观。更多的说明可以参阅文章“Dynamic QML Object Creation from JavaScript”。


1)创建我们的动态QML文件


这个文件将被用来被Javascript来动态生产。这是一个模版尽管每次生成的object的属性可能会不一样。

dynamic-image.qml


import QtQuick 2.0

Image {
    width: 400
    height: 225

    source: "images/image1.jpg"

    Image {
        id: overlay

        anchors.fill: parent

        source: "images/image2.jpg"

        opacity: 0;
        Behavior on opacity { NumberAnimation { duration: 300 } }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            if (overlay.opacity === 0)
                overlay.opacity = 1;
            else
                overlay.opacity = 0;
        }
    }
}


这是一个很简单的QML文件。它显示了一个画面,当我们点击画面时,overlay中的画面将会交叉显示或隐藏。



2)创建动态生产QML Object的Javascript


create-component.js


var component;

function createImageObject(x, y) {
    component = Qt.createComponent("dynamic-image.qml");
    if (component.status === Component.Ready || component.status === Component.Error)
        finishCreation(x, y);
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation(x, y) {
    if (component.status === Component.Ready)
    {
        var image = component.createObject(container, {"x": x, "y": y, width: 300, height:200});
        if (image == null)
            console.log("Error creating image");
    }
    else if (component.status === Component.Error)
        console.log("Error loading component:", component.errorString());
}


这个文件被用来动态生产我们所需要的QML Object。它采用的模版就是我们在上一节中所使用的“dynamic-image.qml”。我们可以设置它的位置参数。当然我们也可以设置它的其它的属性。这里的“container”是我们希望被生产的QML Object所希望放置的位置,也就是它的“父亲”。


3)在QML代码中调用Javascript来生产QML object


import QtQuick 2.0
import Ubuntu.Components 1.1
import "create-component.js" as ImageCreator

/*!
    \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: "dynamicqml.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: root
        title: i18n.tr("dynamicqml")
        property int position: 0

        Flickable {
            width: parent.width
            height: parent.height
            clip:true
            contentHeight: container.childrenRect.height

            Column {
                id: container
                anchors.centerIn: parent
            }
        }

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

            Button {
                text: "Create New"

                onClicked: {
                    ImageCreator.createImageObject(0, root.position);
                    root.position += 200;
                }
            }

            Button {
                text: "Create from string"

                onClicked: {
                   var newObject = Qt.createQmlObject('import QtQuick 2.0;
                        Image {source: "images/image3.jpg"; width: 300; height: 200}',
                        container, "dynamicSnippet1");
                    newObject.x = 0;
                    newObject.y = root.position;
                }

            }

        }

        Component.onCompleted: {
            ImageCreator.createImageObject(0, 0);
            root.position += 200
        }
    }
}


在上面的代码中:

   Button {
                text: "Create New"

                onClicked: {
                    ImageCreator.createImageObject(0, root.position);
                    root.position += 200;
                }
            }

这个代码被用来生产我们所需要的QML Object, 并放置于我们所需要的位置。就像我们上一节中所介绍的那样,我们定义了一个“container”。这里实际上是一个Column的布局管理器。

另外,我们也可以使用代码:

           Button {
                text: "Create from string"

                onClicked: {
                   var newObject = Qt.createQmlObject('import QtQuick 2.0;
                        Image {source: "images/image3.jpg"; width: 300; height: 200}',
                        container, "dynamicSnippet1");
                    newObject.x = 0;
                    newObject.y = root.position;
                }

            }

使用字符串的方式 ,使用Qt.createQmlObject来创建我们的QML object。这也是一种简洁的方式。

运行我们的应用:





我们可以利用屏幕下面的按钮来动态生产我们的QML object。


4)动态生产QML Object的管理


我们可以通过使用destroy来销毁已经生产的QML object。为了管理我们的object,我们来创建一个ListModel:

        ListModel {
            id: objectsModel
        }

我们可以通过如下的方式来添加我们生成的object:

 function itemAdded(obj, source) {
        objectsModel.append({"obj": obj, "source": source})
    }

这样,每当我们创建一个新的object时,我们也把它添加进来:

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

            Button {
                text: "Create New"

                onClicked: {
                    var image = ImageCreator.createImageObject(0, root.position);
                    itemAdded(image, "dynamic-image.qml");
                    root.position += 200;
                }
            }

最后,我们可以通过如下的方式来销毁我们所创建的所有的dynamic-image.qml的object。

            Button {
                text: "Clear objects"

                onClicked: {
                    while(objectsModel.count > 0) {
                        objectsModel.get(0).obj.destroy();
                        objectsModel.remove(0);
                    }
                }
            }

   


上面右图是按下“Clear objects”后的显示。


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

作者:UbuntuTouch 发表于2015/7/23 11:40:55 原文链接
阅读:250 评论:0 查看评论

Read more
UbuntuTouch

我们知道Cordova HTML5应用具有夸平台的特性,同时也具有访问本地一些资源的能力。在今天的这篇文章中,我们将介绍一下如何创建并运行一个Cordova HTML5的应用到我们的Ubuntu手机中。本文的英文原文在“http://developer.ubuntu.com/en/apps/html-5/guides/cordova-guide/”。


1)安装好我们的armhf chroot


如果开发者已经看过我以前的文章“Ubuntu SDK 安装”的话,你可能已经安装好自己的armhf chroot了。除了在SDK中可以帮我们安装我们所需要的chroot外,我们也可以通过如下的命令来简单地安装我们自己所需要的chroot。下面以15.04 framework为例:

$sudo click chroot -aarmhf -f ubuntu-sdk-15.04 create  

我们可以在命令行键入如上的命令就可以创建我们的15.04的armhf chroot。等安装完以后,我们就可以进行下一步的动作。开发者如果想为14.10的目标进行编译,也可以使用同样的方法来安装14.10的armhf chroot。


2)安装Cordova


在这一步,我们来安装Cordova环境。如果你以前已经安装过的,建议你使用如下的方法删除以前的安装(由于以前的安装有bug)。如果你从来没有安装过的话,请忽略这一步

$rm -rf ~/.cordova
$rm -rf ~/.cache

这是为了彻底删除以前已经在你的电脑中的安装。

然后,我们按照如下的步骤来安装Cordova:

$ sudo apt-add-repository ppa:cordova-ubuntu/ppa; sudo apt-get update
$ sudo apt-get install cordova-cli

到目前的这一步,我们基本上已经创建好我们的Cordova环境了。


3)创建一个简单的Cordova例程


目前14.10的架构是默认的开发架尽管将来会有变化。在如下的命令中,如果没有指定具体的架构,14.10架构将会被采用。

使用如下的命令来创建一个简单的Cordova应用:

$cordova create myapp  myapp.mycompany "My App"
$cd myapp
$cordova platform add ubuntu
$vi config.xml

注意:请在你的config.xml中加入如下的句子,以保证你的应用有一个icon图标:

  <icon src="www/img/logo.png" />

另外,请你在config.xml中加入自己的有效的邮件地址:

 <author email="myid@ubuntu.com" />

这样整个config.xml的文件如下:

config.xml



<?xml version='1.0' encoding='utf-8'?>
<widget id="myapp.mycompany" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>My App</name>
    <description>
        A sample Apache Cordova application that responds to the deviceready event.
    </description>
    <author email="myname@mycompany.com" href="http://cordova.io">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <plugin name="cordova-plugin-whitelist" version="1" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <platform name="android">
        <allow-intent href="market:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
    </platform>
    <icon src="www/img/logo.png" />
</widget>


这样我们整个的Cordova模版已经被建立好了。下面我们来具体描述怎么进行编译。


4)编译我们的模版Cordova应用


我们可以使用如下的命令为我们的手机进行编译:

$ cordova build --device

就如我们上面所说的一样,它选择默认的一个版本的armhf chroot进行编译。目前它指的是14.10。在第一次编译时,可能需要我们去安装一些额外的库才可以进行编译。它会提示如下所示的错误信息:




List of devices attached 
750ABLLH4897	device

Target Device: 750ABLLH4897
Building Phone Application...

Error: missing dependency inside armhf chroot
run:
sudo click chroot -a armhf -f ubuntu-sdk-14.10 install cmake libicu-dev:armhf pkg-config qtbase5-dev:armhf qtchooser qtdeclarative5-dev:armhf qtfeedback5-dev:armhf qtlocation5-dev:armhf qtmultimedia5-dev:armhf qtpim5-dev:armhf libqt5sensors5-dev:armhf qtsystems5-dev:armhf

就像上面显示的错误信息一样,我们必须在命令行中打入如下的命令来安装我们所需要的库:

$sudo click chroot -a armhf -f ubuntu-sdk-14.10 install cmake libicu-dev:armhf pkg-config qtbase5-dev:armhf qtchooser qtdeclarative5-dev:armhf qtfeedback5-dev:armhf qtlocation5-dev:armhf qtmultimedia5-dev:armhf qtpim5-dev:armhf libqt5sensors5-dev:armhf qtsystems5-dev:armhf

当然我们也可以使用如下的方法来安装:

$click chroot -aarmhf -fubuntu-sdk-14.10 maint

然后,再打入如下的命令:



等安装完后,我们打入exit命令,退出即可。

重新进入到我们的应用的根目录,再次打入如下的命令:

$ cordova build --device


我们可以在项目目录下找到我们所需要的click包文件:

liuxg@liuxg:~/web/myapp$ find ./ -name *.click
./platforms/ubuntu/ubuntu-sdk-14.10/armhf/prefix/myapp.mycompany_0.0.1_armhf.click

为了能够在手机上直接运行,我们可以直接运行一下的命令:

$ cordova run --device --debug

在手机上的运行结果:



对于使用基于ubuntu-sdk-15.04 chroot来说,我们必须使用如下的命令来完成我们的build:

$ cordova build --device -- --framework ubuntu-sdk-15.04 --verbose

当我们运行时,我们也必须使用如下的命令来完成:

$ cordova run --device --debug -- --framework ubuntu-sdk-15.04

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


5)如何在手机中调试自己的应用


为了能够输出一些有用的调试信息,我们可以在我们的index.js文件中加入如下的console.log语句:

var app = {
	
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

console.log("This shows how to output something to debug");

app.initialize();


当我们运行我们的HTML5应用到手机时,我们可以看到如下的输出:

$ cordova run --device --debug -- --framework ubuntu-sdk-15.04





就像上图中显示的那样,我们可以在电脑中打开chromium浏览器,并输入以上的http://127.0.0.1:9222地址:





从上面我们可以看出来我们添加的的调试信息:

This shows how to output something to debug

在Desktop上的调试也是一样的。我们只需要用如下的方法进行运行:

$ cordova run --debug

在chromium浏览器中输入地址http://127.0.0.1:9222即可。

更多想知道如何在手机上利用Cordova实现camera功能,请参阅文章“Cordova camera app tutorial”。开发者可以参考文章“在Ubuntu平台上创建Cordova Camera HTML5应用”来开发一个Cordova Camera HTML5应用。


作者:UbuntuTouch 发表于2015/7/24 13:18:10 原文链接
阅读:354 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将详细介绍如何使用Cordova Camera HTML5 应用。更多关于Cordova的开发指南,开发者可以参考文章“the Cordova Guide”。通过这个例程,我们可以学习在Ubuntu平台上如何利用Cordova API来完成一个我们所需要的照相机功能。关于如何创建一个Cordova架构的简单的应用,开发者可以参阅文章“如何在Ubuntu手机平台中开发Cordova HTML5应用”。在那篇文章中,它介绍了如何设置自己的环境。建议开发者先阅读该文章。


在我们做练习之前,我们可以在我们想要创建应用目录的下面,打入如下的命令:

$ bzr branch lp:ubuntu-sdk-tutorials

在上面的代码中,有一个已经完成好的Camera代码。


1)创建一个最基本的Cordova框架应用


由于一些原因,Cordova的开发环境没有集成到Qt Creator中去,所以我们只有通过命令行来完成我们的操作。我们可以参阅文章“如何在Ubuntu手机平台中开发Cordova HTML5应用”来创建一个叫做Camera的简单应用:

$ cordova create cordovacam cordovacam.mycompany "CordovaCam"
$ cd cordovacam


2)定义应用自己的图标



我们可以自己设计一个图标为我们想要创建的应用。为了方便,我们可以直接从我们已经下载好的应用ubuntu-sdk-tutorials/html5/html5-tutorial-cordova-camera目录中直接拷贝过来我们所需要的图标:

$ cp ../ubuntu-sdk-tutorials/html5/html5-tutorial-cordova-camera/www/icon.png ./www/img/logo.png

然后,我们需要修改我们的config.xml文件,添加如下的项到它里面去:

<icon src="www/img/logo.png" />

当然,我们也有必要修改作者自己的邮件地址。修改完后,config.xml的显示如下:

<?xml version='1.0' encoding='utf-8'?>
<widget id="cordovacam.mycompany" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>CordovaCam</name>
    <description>
        A sample Apache Cordova application that responds to the deviceready event.
    </description>
    <author email="myname@mycompany.com" href="http://cordova.io">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <plugin name="cordova-plugin-whitelist" version="1" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <platform name="android">
        <allow-intent href="market:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
    </platform>
    <icon src="www/img/logo.png" />
</widget>

注意: 这一步必不可少,否则我们的应用通过不了package的验证。


3)添加Ubuntu平台支持代码到项目中


我们可以利用如下的命令来添加我们的Ubuntu支持代码:

$ cordova platform add ubuntu

现在你的项目中将有如下的目录:

  • platforms/ubuntu/
由于我们需要使用到Camera的功能,所有我们需要在如下的文件中添加camera的security policy:

cordovacam/platforms/ubuntu/apparmor.json

{
 "policy_groups": ["networking", "camera”, "audio"],
  "policy_version":1
}

否则我们的照相机功能在手机中将不能工作。

4)添加Camera API支持


通过如下的命令:

$ cordova plugin add org.apache.cordova.camera

来添加Cordova runtime到你的项目中去。


5)运行我们的应用


$ cordova run --device --debug

就像在我们之前文章“如何在Ubuntu手机平台中开发Cordova HTML5应用”中介绍的那样,这个命令是在利用默认的平台进行运行的(目前是14.10)。在其它的平台上,你需要在它的后面加上平台的参数:

$ cordova run --device -- --framework ubuntu-sdk-15.04

如果没有任何问题的话,你将看到如下的画面:





到目前位置,我们已经完创建了我们的最基本的Cordova Camera框架应用,但是我们还是没有做任何的界面及Camera功能的调用。


6)定义HTML 5用户界面



在这节里,我们将设计我们的HTML 用户界面。我们修改index.html(cordovacam/www/index.html)文件如下:


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>An Ubuntu HTML5 application</title>
    <meta name="description" content="An Ubuntu HTML5 application">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">

    <!-- Ubuntu UI Style imports - Ambiance theme -->
    <link href="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/css/appTemplate.css" rel="stylesheet" type="text/css" />

    <!-- Ubuntu UI javascript imports - Ambiance theme -->
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/fast-buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/core.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/dialogs.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/page.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/pagestacks.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/tabs.js"></script>

    <!-- Cordova platform API access - Uncomment this to have access to the Javascript APIs -->
    <script src="cordova.js"></script>

    <!-- Application script and css -->
    <script src="js/app.js"></script>
    <link href="css/app.css" rel="stylesheet" type="text/css" />
  </head>

  <body>
    <div data-role="mainview">
      <header data-role="header">
        <ul data-role="tabs">
          <li data-role="tabitem" data-page="camera">Camera</li>
        </ul>
      </header>

      <div data-role="content">
        <div data-role="tab" id="camera">
            <div id="loading">
                <header>Loading...</header>
                <progress class="bigger">Loading...</progress>
            </div>
            <div id="loaded">
                <button data-role="button" class="ubuntu" id="click">Take Picture</button>
                <img id="image" src="" />
            </div>
        </div> <!-- tab: camera -->
      </div> <!-- content -->
    </div> <!-- mainview -->
  </body>
</html>


这里的界面非常简单,一个progress及一个可以按下照相的按钮。这里注意一下下面的代码:

  <!-- Cordova platform API access - Uncomment this to have access to the Javascript APIs -->
    <script src="cordova.js"></script>

    <!-- Application script and css -->
    <script src="js/app.js"></script>
    <link href="css/app.css" rel="stylesheet" type="text/css" />

这里有一个app.css文件,一个app.js文件。我们需要把原来的index.css及index.js文件换成它们。

app.css


#loading {
  position: absolute;
  left:45%;
}
#loaded {
  display: none;
}

app.js

/**
 * Wait before the DOM has been loaded before initializing the Ubuntu UI layer
 */

window.onload = function () {
    var UI = new UbuntuUI(); // This must be called after window is loaded
    UI.init();

    document.addEventListener("deviceready", function() {
        if (console && console.log)
            console.log('Platform layer API ready');

        //hide the loading div and display the loaded div
        document.getElementById("loading").style.display = "none";
        document.getElementById("loaded").style.display = "block";

        // event listener to take picture
        UI.button("click").click( function() {
            navigator.camera.getPicture(onSuccess, onFail, {
                quality: 100,
                targetWidth: 400,
                targetHeight: 400,
                destinationType: Camera.DestinationType.DATA_URL,
                correctOrientation: true
             });
           console.log("Take Picture button clicked");
        }); // "click" button event handler

      }, false); // deviceready event handler

}; // window.onload event handler

// show new picture in html and log
function onSuccess(imageData) {
    var image = document.getElementById('image');
    image.src = "data:image/jpeg;base64," + imageData;
    image.style.margin = "10px";
    image.style.display = "block";
}

// log failure message
function onFail(message) {
    console.log("Picture failure: " + message);
}


在这里,我们必须注意的是

var UI = new UbuntuUI(); // This must be called after window is loaded

必须是在window.onload里做,不可以放在它的前面去做。否则我们的UI就会有问题。


这里的设计非常简单。我不在累述。我们选择试着来运行我们的应用到手机上去。运行的显示如下:




显然,它没有我们所希望看到的结果。为什么呢?


我们回头来看看我们的UI设计:

 <body>
    <div data-role="mainview">
      <header data-role="header">
        <ul data-role="tabs">
          <li data-role="tabitem" data-page="camera">Camera</li>
        </ul>
      </header>

      <div data-role="content">
        <div data-role="tab" id="camera">
            <div id="loading">
                <header>Loading...</header>
                <progress class="bigger">Loading...</progress>
            </div>
            <div id="loaded">
                <button data-role="button" class="ubuntu" id="click">Take Picture</button>
                <img id="image" src="" />
            </div>
        </div> <!-- tab: camera -->
      </div> <!-- content -->
    </div> <!-- mainview -->
  </body>

这里采用了Ubuntu平台上的HTML5 UI Toolkit。而在我们的index.html head部分:

    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/fast-buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/core.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/dialogs.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/page.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/pagestacks.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/tabs.js"></script>

它依赖于手机系统提供的ambiance。这有可能造成我们的UI和目前的最新的ambiance不能够完全匹配。为了解决这个问题,我们参考连接https://code.launchpad.net/~dbarth/ubuntu-html5-theme/cmdline-tool/+merge/253498

来解决这个问题。在我的博客文章中“为HTML5应用创建独立于平台的Theme”我也做了详细的介绍。

我们做如下的步骤:

$ ubuntu-html5-theme install 14.10

然后

$ ubuntu-html5-theme convert

运行完后,我们再重新看一下我们的index.html文件:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>An Ubuntu HTML5 application</title>
    <meta name="description" content="An Ubuntu HTML5 application">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">

    <!-- Ubuntu UI Style imports - Ambiance theme -->
    <link href="ambiance/css/appTemplate.css" rel="stylesheet" type="text/css" />

    <!-- Ubuntu UI javascript imports - Ambiance theme -->
    <script src="ambiance/js/fast-buttons.js"></script>
    <script src="ambiance/js/core.js"></script>
    <script src="ambiance/js/buttons.js"></script>
    <script src="ambiance/js/dialogs.js"></script>
    <script src="ambiance/js/page.js"></script>
    <script src="ambiance/js/pagestacks.js"></script>
    <script src="ambiance/js/tabs.js"></script>

    <!-- Cordova platform API access - Uncomment this to have access to the Javascript APIs -->
    <script src="cordova.js"></script>

    <!-- Application script and css -->
    <script src="js/app.js"></script>
    <link href="app.css" rel="stylesheet" type="text/css" />
  </head>

  <body>
    <div data-role="mainview">
      <header data-role="header">
        <ul data-role="tabs">
          <li data-role="tabitem" data-page="camera">Camera</li>
        </ul>
      </header>

      <div data-role="content">
        <div data-role="tab" id="camera">
            <div id="loading">
                <header>Loading...</header>
                <progress class="bigger">Loading...</progress>
            </div>
            <div id="loaded">
                <button data-role="button" class="ubuntu" id="click">Take Picture</button>
                <img id="image" src="" />
            </div>
        </div> <!-- tab: camera -->
      </div> <!-- content -->
    </div> <!-- mainview -->
  </body>
</html>

我们可以看出如下的部分已经发生改变:

 <!-- Ubuntu UI javascript imports - Ambiance theme -->
    <script src="ambiance/js/fast-buttons.js"></script>
    <script src="ambiance/js/core.js"></script>
    <script src="ambiance/js/buttons.js"></script>
    <script src="ambiance/js/dialogs.js"></script>
    <script src="ambiance/js/page.js"></script>
    <script src="ambiance/js/pagestacks.js"></script>
    <script src="ambiance/js/tabs.js"></script>

并且在当前的cordovacam/www目录下多了一个叫做“ambiance”的目录。这样应用再也不依赖于系统所提供的ambiance了。

特别需要支持的是:如果你的界面不需要使用HTML5 UI Toolkit,你可以不做上面的步骤。

重新运行我们的应用:


$ cordova run --device --debug


    


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

本文英文阅读: Cordova camera app tutorial

作者:UbuntuTouch 发表于2015/7/27 12:13:19 原文链接
阅读:277 评论:0 查看评论

Read more
UbuntuTouch

QQuickImageProvider提供了一个可以供我们对QPixmap及多线程的Image请求。这个请求的文件甚至可以在网络上。它的好处是:

  • 在Image中装载一个QPixmap或QImage而不是一个具体的图像文件
  • 在另外一个thread异步装载图片
通过访问一个图片可以通过如下的方式:

Column {
    Image { source: "image://colors/yellow" }
    Image { source: "image://colors/red" }
}

显然,这里的yellow和red不是文件名。它的提供依赖于在QQuickImageProvider中的requestImage的具体实现。

下面我们来通过一个具体的例程来介绍如何使用QQuickImageProvider来从网路上请求一个我们需要的图像。


myimageprovider.h


#ifndef MYIMAGEPROVIDER_H
#define MYIMAGEPROVIDER_H

#include <QQuickImageProvider>
class QNetworkAccessManager;

class MyImageProvider : public QQuickImageProvider
{
public:
    MyImageProvider(ImageType type, Flags flags = 0);
    ~MyImageProvider();
    QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize);

protected:
    QNetworkAccessManager *manager;
};

#endif // MYIMAGEPROVIDER_H


myimageprovider.cpp


#include "myimageprovider.h"

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>

MyImageProvider::MyImageProvider(ImageType type, Flags flags) :
    QQuickImageProvider(type,flags)
{
    manager = new QNetworkAccessManager;
}

MyImageProvider::~MyImageProvider()
{
    delete manager;
}

QImage MyImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    qDebug() << "id: " << id;
    qDebug() << "reequestedSize: " << requestedSize.width() + " " + requestedSize.height();
    QUrl url("http://lorempixel.com/" + id);
    QNetworkReply* reply = manager->get(QNetworkRequest(url));
    QEventLoop eventLoop;
    QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
    eventLoop.exec();
    if (reply->error() != QNetworkReply::NoError)
        return QImage();
    QImage image = QImage::fromData(reply->readAll());
    size->setWidth(image.width());
    size->setHeight(image.height());
    return image;
}

上面我们从网路地址“http://lorempixel.com/”取得文件,并转化为一个QImage。QQuickImageProvider要求我们必须实现如下的一个virtual方法。

QQuickImageProvider(ImageType type, Flags flags = 0)
virtual	~QQuickImageProvider()
Flags	flags() const
ImageType	imageType() const
virtual QImage	requestImage(const QString & id, QSize * size, const QSize & requestedSize)
virtual QPixmap	requestPixmap(const QString & id, QSize * size, const QSize & requestedSize)
virtual QQuickTextureFactory *	requestTexture(const QString & id, QSize * size, const QSize & requestedSize)

我们可以在QML中通过如下的方式来访问一个图片:

 Image { source: "image://myprovider/500/500/" }

显然我们看到的source不是一个具体的文件。并且,它的source是以“image://”开始的。

我们在我们的main.cpp中做如下的实现:

#include "myimageprovider.h"

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

    QQuickView view;
    QQmlEngine *engine = view.engine();
    MyImageProvider *imageProvider = new MyImageProvider(QQmlImageProviderBase::Image);
    engine->addImageProvider("myprovider", imageProvider );
    view.setSource(QUrl(QStringLiteral("qrc:///Main.qml")));
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.show();
    return app.exec();
}

注意这里的“myprovider”和我们上面的Image中访问的对应起来。

我们的main.qml文件如下:

main.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: "imageprovider.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("imageprovider")

        Image {
            id: img
            anchors.centerIn: parent
            source: "image://myprovider/500/500/"
            anchors.fill: parent
            onStatusChanged: {
                if(status == Image.Ready)
                    indicator.running = false;
            }

            ActivityIndicator {
                id: indicator
                anchors.centerIn: parent
                running: false
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    indicator.running = true;
                    img.source = "image://myprovider/500/500/?seed=" + Math.random(1000)
                }
            }
        }
    }
}


我们在点击图片时,它会自动地随机地从网站取得下一个图片,并显示出来:

  

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

作者:UbuntuTouch 发表于2015/7/29 13:22:17 原文链接
阅读:177 评论:0 查看评论

Read more
UbuntuTouch

在先前的例程中“如何使用Ubuntu手机平台中的照相机API来存储照片”,我们已经展示了如何使用Item的属性来存储我们的照片。在这篇文章中,我们将使用Camera API来完成同样的功能。


我们来直接贴自己的代码:


main.qml

import QtQuick 2.0
import Ubuntu.Components 1.1
import QtMultimedia 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: "cameraapp.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 var resolution

    // This function is used to get the writable private directory of this app
    function getPriateDirectory() {
        var sharepath = "/home/phablet/.local/share/";
        var path = sharepath + applicationName;
        console.log("path: " + path);
        return path;
    }

    Page {
        id: page
        title: i18n.tr("cameraapp")

        Camera {
            id: camera

            imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

            flash.mode: Camera.FlashRedEyeReduction

            imageCapture {
                onImageCaptured: {
                    console.log("image captured! reqId: " + requestId)
                    image.source = preview  // Show the preview in an Image
                }

                onImageSaved: {
                    console.log("image has been saved: " + requestId);
                    console.log("saved path: " + path);
                    image.source = path;
                }
            }

            Component.onCompleted: {
                resolution = camera.viewfinder.resolution;
                console.log("resolution: " + resolution.width + " " + resolution.height);
            }
        }

        Row {
            id: container
            VideoOutput {
                id: video
                source: camera
                width: page.width
                height: page.height
                focus : visible // to receive focus and capture key events when visible
                orientation: -90

                //                Image {
                //                    id: photoPreview
                //                    anchors.fill: parent
                //                    rotation: -90
                //                }

                SwipeArea {
                    anchors.fill: parent
                    onSwipe: {
                        console.log("swipe happened!: " + direction)
                        switch (direction) {
                        case "left":
                            page.state = "image";
                            break
                        }
                    }
                }
            }

            Item {
                id: view
                width: page.width
                height: page.height

                Image {
//                    anchors.fill: parent
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.verticalCenter: parent.verticalCenter
                    width: parent.height
                    height: parent.width
                    id: image
                    rotation: 90
                    fillMode: Image.PreserveAspectFit
                }

                Text {
                    text: image.source
                    color:"red"
                    font.pixelSize: units.gu(2.5)
                    width: page.width
                    wrapMode: Text.WrapAtWordBoundaryOrAnywhere
                }

                SwipeArea {
                    anchors.fill: parent
                    onSwipe: {
                        console.log("swipe happened!: " + direction)
                        switch (direction) {
                        case "right":
                            page.state = "";
                            break
                        }
                    }
                }
            }
        }

        states: [
            State {
                name: "image"
                PropertyChanges {
                    target: container
                    x:-page.width
                }
                PropertyChanges {
                    target: capture
                    opacity:0
                }
            }
        ]

        transitions: [
            Transition {
                NumberAnimation { target: container; property: "x"; duration: 500
                    easing.type:Easing.OutSine}
                //                NumberAnimation { target: inputcontainer; property: "opacity"; duration: 200}
                NumberAnimation { target: capture; property: "opacity"; duration: 200}
            }
        ]

        Button {
            id: capture
            anchors.bottom: parent.bottom
            anchors.bottomMargin: units.gu(1)
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Capture"

            onClicked: {
                console.log("capture path: " + getPriateDirectory());
                camera.imageCapture.captureToLocation(getPriateDirectory());
                page.state = "image"
            }
        }
    }
}



在这里我们必须指出的是我们可以使用:

camera.imageCapture.captureToLocation(getPriateDirectory());

来把照片存储到我们指定的位置。这个位置可以是手机应用自己的私有的目录或它下面的子目录。

我们可以在如下的代码中得到照相机存储的文件信息:

           imageCapture {
                onImageCaptured: {
                    console.log("image captured! reqId: " + requestId)
                    image.source = preview  // Show the preview in an Image
                }

                onImageSaved: {
                    console.log("image has been saved: " + requestId);
                    console.log("saved path: " + path);
                    image.source = path;
                }
            }

通过得到的path信息,我们可以得到照片的位置,并显示它。

在我们的设计中,我们可以使用swipe向左或向右来切换viewFinder及拍照的相片。

运行我们的应用,我们可以看到如下的图片:

  

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

作者:UbuntuTouch 发表于2015/7/30 11:23:15 原文链接
阅读:246 评论:0 查看评论

Read more
UbuntuTouch

在前面的文章“如何在QML中使用camera API来拍照”中,我们介绍了如何使用Camera API来进行拍照。今天我们在这篇文章中来介绍如何使用Camera API来进行录像。


首先,还是和以前一样,我直接把自己的代码贴出来:


main.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtMultimedia 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: "videoapp.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 var resolution

    // This function is used to get the writable private directory of this app
    function getPriateDirectory() {
        var sharepath = "/home/phablet/.local/share/";
        var path = sharepath + applicationName;
        console.log("path: " + path);
        return path;
    }

    Page {
        id: page
        title: i18n.tr("videoapp")

        Camera {
            id: camera

            imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

            flash.mode: Camera.FlashRedEyeReduction

            videoRecorder {
                onRecorderStateChanged: {
                    console.log("onRecorderStateChanged: " + videoRecorder.recorderState);
                    if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
                        console.log("actualLocation: " + videoRecorder.actualLocation);
                        myvideo.source =  videoRecorder.actualLocation;
                    }
                }
            }

            videoRecorder.audioEncodingMode: videoRecorder.ConstantBitrateEncoding;
            videoRecorder.audioBitRate: 128000
            videoRecorder.mediaContainer: "mp4"
            videoRecorder.outputLocation: getPriateDirectory()

            captureMode: Camera.CaptureVideo

            Component.onCompleted: {
                resolution = camera.viewfinder.resolution;
                console.log("resolution: " + resolution.width + " " + resolution.height);
                console.log("deviceId: " + camera.deviceId)
            }
        }

        Row {
            id: container

            Item {
                width: page.width
                height: page.height

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

                SwipeArea {
                    anchors.fill: parent
                    onSwipe: {
                        console.log("swipe happened!: " + direction)
                        switch (direction) {
                        case "left":
                            page.state = "image";
                            break
                        }
                    }
                }
            }

            Item {
                id: view
                width: page.width
                height: page.height

                Video {
                    id: myvideo
                    anchors.fill: parent
                    autoPlay: true
                    focus: true

                    Component.onCompleted: {
                        myvideo.play();
                    }
                }

                Text {
                    text: myvideo.source
                    color:"red"
                    font.pixelSize: units.gu(2.5)
                    width: page.width
                    wrapMode: Text.WrapAtWordBoundaryOrAnywhere
                }

                SwipeArea {
                    anchors.fill: parent
                    onSwipe: {
                        console.log("swipe happened!: " + direction)
                        switch (direction) {
                        case "right":
                            page.state = "";
                            break
                        }
                    }
                }
            }
        }

        states: [
            State {
                name: "playvideo"
                PropertyChanges {
                    target: container
                    x:-page.width
                }
                PropertyChanges {
                    target: capture
                    opacity:0
                }
                PropertyChanges {
                    target: stop
                    opacity:0
                }
            }
        ]

        transitions: [
            Transition {
                NumberAnimation { target: container; property: "x"; duration: 500
                    easing.type:Easing.OutSine}
                //                NumberAnimation { target: inputcontainer; property: "opacity"; duration: 200}
                NumberAnimation { target: capture; property: "opacity"; duration: 200}
                NumberAnimation { target: stop; property: "opacity"; duration: 200}
            }
        ]

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

            Button {
                id: capture
                text: "Record"

                onClicked: {
                    console.log("capture path: " + getPriateDirectory());
                    camera.videoRecorder.record();
                }
            }

            Button {
                id: stop
                text: "Stop"

                onClicked: {
                    console.log("stop is clicked!");
                    camera.videoRecorder.stop();
                    page.state = "playvideo"
                }
            }

            Button {
                id: play
                text: "Play video"

                onClicked: {
                    console.log("filepath: " + myvideo.source);
                    console.log( "actual: " +  camera.videoRecorder.actualLocation);
                    myvideo.play();
                }
            }
        }

    }
}


在这里,QML Camera是被如下的方式使用的:

        Camera {
            id: camera

            imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

            flash.mode: Camera.FlashRedEyeReduction

            videoRecorder {
                onRecorderStateChanged: {
                    console.log("onRecorderStateChanged: " + videoRecorder.recorderState);
                    if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
                        console.log("actualLocation: " + videoRecorder.actualLocation);
                        myvideo.source =  videoRecorder.actualLocation;
                    }
                }
            }

            videoRecorder.audioEncodingMode: videoRecorder.ConstantBitrateEncoding;
            videoRecorder.audioBitRate: 128000
            videoRecorder.mediaContainer: "mp4"
            videoRecorder.outputLocation: getPriateDirectory()

            captureMode: Camera.CaptureVideo

            Component.onCompleted: {
                resolution = camera.viewfinder.resolution;
                console.log("resolution: " + resolution.width + " " + resolution.height);
                console.log("deviceId: " + camera.deviceId)
            }
        }

我们定义了capureMode为Camera.CaptureVideo。同时我们也指定了录像文件的路径:

videoRecorder.outputLocation: getPriateDirectory()

当我们使用CameraRecorder来进行如下的方式录像时:

 camera.videoRecorder.record();

我们可以通过:

            videoRecorder {
                onRecorderStateChanged: {
                    console.log("onRecorderStateChanged: " + videoRecorder.recorderState);
                    if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
                        console.log("actualLocation: " + videoRecorder.actualLocation);
                        myvideo.source =  videoRecorder.actualLocation;
                    }
                }
            }

来获取所录像的文件的实际路径。

我们可以通过:

               Video {
                    id: myvideo
                    anchors.fill: parent
                    autoPlay: true
                    focus: true

                    Component.onCompleted: {
                        myvideo.play();
                    }
                }

来播放已经录下的文件。

运行为我们的应用:

 

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



作者:UbuntuTouch 发表于2015/7/31 15:54:58 原文链接
阅读:93 评论:0 查看评论

Read more
UbuntuTouch

我们可以在QML应用中直接使用QML WebView来装载我们的HTML应用,但是如果我们的HTML文件中使用到alert及confirm,我们的应用可能并不能弹出我们需要的alert及confirm。这时,我们需要对WebView中的alertDialog及confirmDialog进行设计才可以起到作用。


我们在下面来展示如何去做:


index.html


<html>
   <head>
   
      <script type="text/javascript">
         <!--
            function getConfirmation(){
               var retVal = confirm("Do you want to continue ?");
               if( retVal == true ){
                  return true;
               }
               else{
                  return false;
               }
            }
         //-->

         <!--
            function getAlert(){
               alert("This is cool!");
            }
         //-->

        <!--
            function getPrompt(){
               var retVal = prompt("Enter your name : ", "your name here");
               // console.log("retVal: + " + retVal);
            }
         //-->
      </script>
      
   </head>
   <body>
      <p>Click the following button to see the result: </p>
      
      <form>
         <input type="button" value="Alert Dialog" onclick="getAlert();" /> <br>
         <input type="button" value="Confirm Dialog" onclick="getConfirmation();" /> <br>
         <input type="button" value="Prompt Dialog" onclick="getPrompt();" />
      </form>
      
   </body>
</html>


Main.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Web 0.2
import Ubuntu.Components.Popups 1.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: "webviewdialog.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("webviewdialog")

        Component {
            id: confirmDlg

            Dialog {
                title: i18n.tr("JavaScript Confirmation")

                Button {
                    text: i18n.tr("OK")
                    onClicked: model.accept()
                }

                Button {
                    text: i18n.tr("Cancel")
                    onClicked: model.reject()
                }

                Component.onCompleted: show()
            }
        }

        Component {
            id: alertDlg

            Dialog {
                title: model.message

                Button {
                    text: i18n.tr("OK")
                    onClicked: model.accept()
                }

                Component.onCompleted: show()
            }
        }

        Component {
            id: promptDlg

            Dialog {
                TextField {
                    id: input
                    text: model.defaultValue
                    onAccepted: model.accept(input.text)
                }

                Button {
                    text: i18n.tr("OK")
                    color: "green"
                    onClicked: model.accept(input.text)
                }

                Button {
                    text: i18n.tr("Cancel")
                    color: UbuntuColors.coolGrey
                    onClicked: model.reject()
                }

                Binding {
                    target: model
                    property: "currentValue"
                    value: input.text
                }

                Component.onCompleted: show()
            }
        }

        WebView {
            anchors.fill: parent
            url: "www/index.html"
            confirmDialog: confirmDlg
            alertDialog: alertDlg
            promptDialog: promptDlg
        }
    }
}



在这里,我们对alertDialog及confirmDialog进行了初始化,这样当我们在index.html中使用alert及confirm时,我们可以得到我们需要的Dialog:


   




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


作者:UbuntuTouch 发表于2015/8/3 10:31:52 原文链接
阅读:53 评论:0 查看评论

Read more
UbuntuTouch

[原]为QML动态生成Tab

在QML设计中,Tabs是构成QML应用的一种UI架构。在今天的这篇文章中,我们来尝试来动态创建一些Tab。


我们先把代码贴出来:


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtQuick.LocalStorage 2.0

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

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: "dynamictab.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)

    Component.onCompleted: {
        mainView.initializeDB();
        mainView.saveFeed("BBC News","http://feeds.bbci.co.uk/news/rss.xml");
        mainView.saveFeed("Jono Bacon","http://www.jonobacon.org/?feed=rss2");
        mainView.saveFeed("The Register", "http://www.theregister.co.uk/headlines.atom");
        fillTabs();
    }

    Tabs {
        id: initialtabs
        anchors.fill: parent


//        // First tab begins here
//        Tab {
//            id: tabFrontPage
//            objectName: "tabFrontPage"

//            title: i18n.tr("Front Page")

//            // Tab content begins here
//            page: Page {
//                Column {
//                     anchors.centerIn: parent
//                    Label {
//                        id: labelFrontPage
//                        text: i18n.tr("This will be the front page \n An aggregation of the top stories from each feed")
//                    }
//                }
//            }
//        }
    }


    function fillTabs() {
        initialtabs.destroy();
        var objStr = "import QtQuick 2.0; import Ubuntu.Components 1.1; import QtQuick.XmlListModel 2.0; Tabs{ id:tabs; anchors.fill:parent;"
        var db = getDatabase();
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT * FROM feeds;');
            if (rs.rows.length > 0) {
                for(var i = 0; i < rs.rows.length; i++) {
                    objStr += "Tab { id:tab" + i + ";anchors.fill:parent;title:'" + rs.rows.item(i).feedName + "';
                               property string source: '" + rs.rows.item(i).feedURL + "';
                               page: Page { anchors.margins: units.gu(2);Column {anchors.centerIn: parent;
                               Label{text:tab" + i + ".source;}}}}";
                }
                objStr += "}";

                console.log("objStr: " + objStr );
                var cmpTabs = Qt.createQmlObject(objStr, mainView, "tabsfile");
            } else {
                res = "Unknown";
            }
        })
    }

    //Create tabs for each feed
    function createTabs() {
        var feeds = getFeeds();
        for (var i = 0; i < feeds.length; i++){
            //Add tab for each feed.
            // Cannot be done with existing API

        }
    }

    //Storage API
    function getDatabase() {
        return LocalStorage.openDatabaseSync("news-feed","1.0","StorageDatabase",10000)
    }

    //Initialise DB tables if not already existing
    function initializeDB() {
        var db = getDatabase();
        db.transaction(function(tx) {
            //Create settings table if not existing
            tx.executeSql('CREATE TABLE IF NOT EXISTS settings(setting TEXT UNIQUE, value TEXT)');
            tx.executeSql('CREATE TABLE IF NOT EXISTS feeds(feedName TEXT UNIQUE, feedURL TEXT UNIQUE)')
        });
    }

    //Write setting to DB
    function setSetting(setting,value){
        //setting: string - setting name (key)
        //value: string - value
        var db = getDatabase();
        var res = "";
        db.transaction(function(tx) {
            var rs = tx.executeSql('INSERT OR REPLACE INTO settings VALUES (?,?);',[setting,value]);
            //console.log(rs.rowsAffected)
            if(rs.rowsAffected > 0) {
                res = "OK";
            } else {
                res = "Error";
            }
        })
        return res;
    }

    //Read setting from DB
    function getSetting(setting) {
        var db = getDatabase();
        var res="";
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT value FROM settings WHERE setting=?;', [setting]);
            if (rs.rows.length > 0) {
                res = rs.rows.item(0).value;
            } else {
                res = "Unknown";
            }
        })
        // The function returns “Unknown” if the setting was not found in the database
        // For more advanced projects, this should probably be handled through error codes
        return res;
    }

    function saveFeed(feedName, feedURL) {
        var db = getDatabase();
        var res = "";
        db.transaction(function(tx){
            var rs = tx.executeSql('INSERT OR REPLACE INTO feeds VALUES (?,?)',[feedName,feedURL]);
            //console.log(rs.rowsAffected)
            if (rs.rowsAffected > 0) {
                res = "OK";
            } else {
                res = "Error";
            }
        })
        return res;
    }

    //Return a single feed
    function getFeed(feedName) {
        var db = getDatabase();
        var res = "";
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT feedURL FROM feeds WHERE feedName=?;', [feedName]);
            if (rs.rows.length > 0) {
                res = rs.rows.item(0).feedURL;
            } else {
                res = "Unknown";
            }

        })
        return res;
    }

    //Return all feeds and urls
    function getFeeds() {
        var db = getDatabase();
        var res = "";
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT * FROM feeds;');
            if (rs.rows.length > 0) {
                return rs;
            } else {
                res = "Unknown";
            }
        })

        return res;
    }
}

这里,我们们先创建一个自己的数据库来存储一些RSS feed的值:


   Component.onCompleted: {
        mainView.initializeDB();
        mainView.saveFeed("BBC News","http://feeds.bbci.co.uk/news/rss.xml");
        mainView.saveFeed("Jono Bacon","http://www.jonobacon.org/?feed=rss2");
        mainView.saveFeed("The Register", "http://www.theregister.co.uk/headlines.atom");
        fillTabs();
    }

在上面的最后部分,我们调用fillTabs来动态创建一些我们想要的Tab:


    function fillTabs() {
        initialtabs.destroy();
        var objStr = "import QtQuick 2.0; import Ubuntu.Components 1.1; import QtQuick.XmlListModel 2.0; Tabs{ id:tabs; anchors.fill:parent;"
        var db = getDatabase();
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT * FROM feeds;');
            if (rs.rows.length > 0) {
                for(var i = 0; i < rs.rows.length; i++) {
                    objStr += "Tab { id:tab" + i + ";anchors.fill:parent;title:'" + rs.rows.item(i).feedName + "';
                               property string source: '" + rs.rows.item(i).feedURL + "';
                               page: Page { anchors.margins: units.gu(2);Column {anchors.centerIn: parent;
                               Label{text:tab" + i + ".source;}}}}";
                }
                objStr += "}";

                console.log("objStr: " + objStr );
                var cmpTabs = Qt.createQmlObject(objStr, mainView, "tabsfile");
            } else {
                res = "Unknown";
            }
        })
    }


这里我们使用了Qt.createQmlObject来创建不同的Tab。这些tab的具体的内容来自我们的数据库。


运行我们的应用:


  


当然我们也可以利用我们存储的rss feed的值来创建一个完全新的RSS reader。具体下面的开发就留给开发者吧。我们也可以使用Qt.createComponent从一个文件中来创建一个我们所需要的tab。


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

作者:UbuntuTouch 发表于2015/8/4 13:02:53 原文链接
阅读:31 评论:0 查看评论

Read more
UbuntuTouch

[原]在QML中使用alarm

我们可以QML应用中设置alarm来完成一些提醒的工作。为了使得它能够工作,我们必须加入calendar policy。由于calendar policy目前还是处于reserved状态,所以我们目前只能做测试使用。等将来正式发布后,就可以使用了。


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: "alarm.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("alarm")

        Alarm{
            id: alarm
        }

        Column {
            spacing: units.gu(1)

            Row {
                spacing: units.gu(1)

                Label {
                    id: date
                    text: "Date:"
                    anchors.verticalCenter: parent.verticalCenter
                }
                TextField {
                    text: alarm.date.toString()
                    onAccepted: {
                        console.log("this is cool!");
                        onAccepted: alarm.date = new Date(text)
                    }
                }
            }

            Row {
                spacing: units.gu(1)
                Label {
                    id: msg
                    text: "Message:"
                    anchors.verticalCenter: parent.verticalCenter
                }
                TextField {
                    text: alarm.message
                    onAccepted: {
                        console.log("Setting the alarm message!")
                        alarm.message = text
                    }
                }
            }

            Button {
                text: "Save"
                onClicked: {
                    var now = new Date();
                    var newdate = new Date(now.getTime() + 10*1000);
                    console.log("now: " + newdate.toLocaleDateString())
                    alarm.date = newdate;

                    alarm.save();
                    if (alarm.error !== Alarm.NoError) {
                        print("Error saving alarm, code: " + alarm.error);
                    } else {
                        print("There is no error!")
                    }

                }
            }
        }
    }
}


在我们的应用中,当我们设置alarm时,我们把当前的时间加上10秒,然后设置时间:


            Button {
                text: "Save"
                onClicked: {
                    var now = new Date();
                    var newdate = new Date(now.getTime() + 10*1000);
                    console.log("now: " + newdate.toLocaleDateString())
                    alarm.date = newdate;

                    alarm.save();
                    if (alarm.error !== Alarm.NoError) {
                        print("Error saving alarm, code: " + alarm.error);
                    } else {
                        print("There is no error!")
                    }

                }

这样,当10秒钟过后,就会有我们的alarm信息。


  


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






作者:UbuntuTouch 发表于2015/8/4 14:21:26 原文链接
阅读:23 评论:0 查看评论

Read more
UbuntuTouch

VisualItemModel可以让我们把QML Item变为我们的ListView的Model成为可能。这个Model可以既含有数据(data)也可以含有delegate。VisualItemModel含有的Item提供可以用来画数据内容的delete。这个Model不提供任何roles,也就是说我们不可以使用任何“model.xxxx”来引用我们的model数据。VisualItemModel适合于ListView中的每个delegate显示的情况都不太一样的情况,但是,我们可以照样使用ListView来显示我们的数据,并使用ListView的特性来展示并scroll数据。更多阅读ObjectModel。我们可以使用ObjectModel来代替VisualItemModel来实现同样的事情。


一个简单的例程如下:


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

        VisualItemModel {
            id: itemModel
            Rectangle { height: view.height/3; width: view.width; color: "red" }
            Rectangle { height: view.height/3; width: view.width; color: "green" }
            Rectangle { height: view.height/3; width: view.width; color: "blue" }
        }

        ListView {
            id: view
            anchors.fill: parent
            model: itemModel
        }

    }




代码:git clone https://gitcafe.com/ubuntu/visualitemmodel1.git


我们的另外一个例程代码展示如下:


Main.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: "visualitemmodel.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("visualitemmodel")

        Rectangle {
            color: "lightgray"
            anchors.fill: parent

            VisualItemModel {
                id: itemModel

                Rectangle {
                    width: view.width; height: view.height - footer.height
                    color: "#FFFEF0"
                    Text { text: "Page 1"; font.bold: true; anchors.centerIn: parent }
                }

                Rectangle {
                    width: view.width; height: view.height - footer.height
                    color: "#F0FFF7"
                    Text { text: "Page 2"; font.bold: true; anchors.centerIn: parent }
                }

                Rectangle {
                    width: view.width; height: view.height - footer.height
                    color: "#F4F0FF"
                    Text { text: "Page 3"; font.bold: true; anchors.centerIn: parent }
                }
            }

            UbuntuListView {
                id: view
                anchors { fill: parent; bottomMargin: 30 }
                model: itemModel
                preferredHighlightBegin: 0; preferredHighlightEnd: 0
                highlightRangeMode: ListView.StrictlyEnforceRange
                orientation: ListView.Horizontal
                highlightMoveVelocity : 5000
                snapMode: ListView.SnapOneItem
                highlightMoveDuration: 1000
                flickDeceleration: 2000
                Component.onCompleted: {
                    console.log("velocity: " + view.horizontalVelocity)
                    console.log("highlightMoveDuration: " + view.highlightMoveDuration)
                }
            }
        }

        Row {
            id: footer
            anchors { bottom: parent.bottom }
            anchors.bottomMargin: units.gu(1)
            // anchors.centerIn: parent
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: units.gu(4)
            height: units.gu(3)

            Repeater {
                model: itemModel.count

                Rectangle {
                    width: units.gu(3); height: width
                    radius: units.gu(1.5)
                    color: view.currentIndex == index ? "blue" : "yellow"

                    MouseArea {
                        width: units.gu(3); height: width
                        anchors.centerIn: parent
                        onClicked: view.currentIndex = index
                    }
                }
            }
        }
    }
}


  

在上面的例程中,我们可以点击下面的圆点来切换我们的page。我们也可以利用ListView的特性来左右swipe我们的画面来改变当前的Page号码。这种情况适合于展示不同页面,而且每个页面也不同的情况。

我们的源码在: git clone https://gitcafe.com/ubuntu/visualitemmodel.git



作者:UbuntuTouch 发表于2015/8/5 11:27:33 原文链接
阅读:48 评论:0 查看评论

Read more
UbuntuTouch

我们知道,在Ubuntu 手机平台中,我们使用了全新的安全机制。任何一个应用只能访问自己的私有空间,但是它不能访问不属于它的任何其它的空间。具体来说,如果我们想直接通过系统文件目录的方式来使用照相机所拥有的Pictures目录的话,那是不可以的。原因是那个目录只属于创建它的那个应用。那么我们该如何才能访问那个目录中的文件呢?答案就是ContentHub API。我们在先前的一个例子“利用ContentHub API来import图片”中也展示了如何这么做。


下面我们来通过一个例子来展示如何来导入一个从其它应用产生的图片:


Main.qml

import QtQuick 2.4
import Ubuntu.Components 1.2
import Ubuntu.Components.ListItems 0.1 as ListItem
import Ubuntu.Components.Popups 0.1
import Ubuntu.Content 0.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: "contenthub-picker.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)

    PageStack {
        id: pageStack
        Component.onCompleted: pageStack.push(root)

        Page {
            id: root
            title: i18n.tr("Peer Picker Example")
            visible: false

            property list<ContentItem> importItems
            property var activeTransfer

            Column {
                anchors.fill: parent

                ListItem.Standard {
                    id: peerListHeader
                    anchors {
                        left: parent.left
                        right: parent.right
                    }
                    text: i18n.tr("Sources")
                    control: Button {
                        text: i18n.tr("Select source")
                        onClicked: {
                            pageStack.push(picker);
                        }
                    }
                }

                ListItem.Header {
                    id: titleItem
                    anchors {
                        left: parent.left
                        right: parent.right
                    }
                    text: i18n.tr("Results")
                }

                GridView {
                    id: resultList
                    anchors {
                        left: parent.left
                        right: parent.right
                    }
                    height: childrenRect.height
                    cellWidth: units.gu(20)
                    cellHeight: cellWidth

                    model: root.importItems
                    delegate: Item {
                        id: result
                        height: units.gu(19)
                        width: height
                        UbuntuShape {
                            width: parent.width
                            height: width
                            source: Image {
                                id: image
                                source: url
                                sourceSize.width: width
                                sourceSize.height: height
                                height: parent.height
                                width: height
                                fillMode: Image.PreserveAspectFit
                                smooth: true
                            }
                        }
                    }
                }
            }

            ContentTransferHint {
                anchors.fill: root
                activeTransfer: root.activeTransfer
            }

            Connections {
                target: root.activeTransfer
                onStateChanged: {
                    console.log("StateChanged: " + root.activeTransfer.state);
                    switch (root.activeTransfer.state ) {
                    case ContentTransfer.Created:
                        console.log("StateChanged: Created")
                        break;

                    case ContentTransfer.Initiated:
                        console.log("StateChanged: Initiated")
                        break;

                    case ContentTransfer.InProgress:
                        console.log("StateChanged: InProgress")
                        break;

                    case ContentTransfer.Downloading:
                        console.log("StateChanged: Downloading")
                        break;

                    case ContentTransfer.Downloaded:
                        console.log("StateChanged: Downloaded")
                        break;

                    case ContentTransfer.Charged:
                        console.log("StateChanged: Charged")
                        break;

                    case ContentTransfer.Collected:
                        console.log("StateChanged: Collected")
                        break;

                    case ContentTransfer.Aborted:
                        console.log("StateChanged: Aborted")
                        break;

                    case ContentTransfer.Finalized:
                        console.log("StateChanged: Finalized")
                        break;
                    }

                    if (root.activeTransfer.state === ContentTransfer.Charged) {
                        root.importItems = root.activeTransfer.items;

                        for ( var key in root.importItems ) {
                            console.log("url: " + root.importItems[key].url)
                        }
                    }
                }
            }

        }

        Page {
            id: picker
            visible: false

            ContentPeerPicker {
                visible: parent.visible

                // Type of handler: Source, Destination, or Share
                handler: ContentHandler.Source
                // well know content type
                contentType: ContentType.Pictures

                onPeerSelected: {
                    console.log("onPeerSelected-----------------------------!")
                    root.activeTransfer = peer.request();
                    pageStack.pop();
                }

                onCancelPressed: {
                    console.log("onCancelPressed!---------------------------!")
                    pageStack.pop();
                }
            }
        }
    }
}


在上面的代码中,我们使用了ContentPeerPicker来进行选择我们所需要的图片的Peer。当我们一旦选择好Peer后,我们通过


 Connections {
                target: root.activeTransfer
                onStateChanged: {
                 ...
                 }
 }


来得到目前activeTransfer的具体的状态。当它的状态变为ContentTransfer.Charged时,我们就可以得到所选文件的url。下面是我们运行我们的应用时的截图。记住我们需要添加content_exchange policy来完成我们的操作:


  


   


在图片被import过来后,我们会发现照片所处的位置在:


qml: url: file:///home/phablet/.cache/contenthub-picker.liu-xiao-guo/HubIncoming/3/image20150806_080715636.jpg

显然这个位置是我们的应用可以访问的位置。我们可以对它进行我们所需要的任何操作。


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



作者:UbuntuTouch 发表于2015/8/6 16:37:19 原文链接
阅读:49 评论:0 查看评论

Read more
UbuntuTouch

我们在QML应用中有时需要调用系统设置(system settings)来完成我们的一些设置。比如,我们在使用GPS来定位时,可能GPS并没有打开,如果在我们的应用中直接打开系统中的GPS设置页面,这样我们就可以直接打开系统的GPS而不用单独设计一个页面。我们可以通过使用URL dispatcher的方法来打开另外一个应用。在先前的我们的文章中,我们已经讲述了很多关于URL dispatcher方面的东西:

  1. 怎么在Ubuntu手机上发送短信及拨打电话
  2. 使用URL dispatcher的范例

关于系统设置(system-settings)的源码,我们可以在地址找到。

如何查看系统的url dispatcher


通常情况下,我们可能很想知道系统中到底有那些的URL dispatcher。我们可以通过如下的命名来查看我们的手机系统的URL dispatcher:

 


上面列举了我们手机系统中已经有的URL dispatcher。比如我们可以查看system-settings的url dispatcher的protocol:


[
        {
                "protocol": "settings"
        }
]


这样我们在我们的QML应用中使用如下的方法来启动系统设置中的页面:

 Qt.openUrlExternally("settings:///system/about");

上面的方法就可以打开系统设置中的“关于”页面。

基于上面的理解,我设计了如下的例程来启动系统设置中的不同的页面:

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: "urldispatcher.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 var plugins: ["about", "phone", "battery", "bluetooth", "brightness",
        "cellular", "language", "background", "flight-mode",
        "notifications", "orientation-lock", "reset", "security-privacy",
        "sound", "system-update", "time-date", "wifi"]

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

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

            Column {
                id: content
                anchors.centerIn: parent
                spacing: units.gu(1)

                Repeater {
                    model: plugins
                    delegate: Button {
                        text: modelData
                        onClicked: {
                            Qt.openUrlExternally("settings:///system/" + modelData);
                        }
                    }
                }
            }

        }
    }
}

运行我们的应用:

  

应用的源码在: git clone https://gitcafe.com/ubuntu/urldispatcher.git

作者:UbuntuTouch 发表于2015/8/17 13:23:24 原文链接
阅读:175 评论:0 查看评论

Read more
UbuntuTouch

我们知道在Ubuntu手机中一个应用不能直接访问另外一个应用的空间。我们有时需要这么做。比如我们想把我们自己使用照相机API照下一个照片,并放入到我们自己应用的自己可以访问的空间。但是我们不能直接把我们所照的照片直接放入到Gallery应用所拥有的目录中。如果是这样做,直接就违反系统的平台安全性。更多关于平台安全性,可以阅读文章“Ubuntu OS应用Runtime Enviroment”。我们该如何把自己的照片存入到Gallery应用所拥有的目录并让Gallery应用来显示呢?答案是使用ContentHub API


下面我们来通过一个例程来完成展示如何完成这个功能。


首先,我们来完成一个Dialog:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.Popups 1.0
import Ubuntu.Content 1.1

PopupBase {
    id: downloadDialog
    anchors.fill: parent
    property var activeTransfer
    property var downloadId
    property alias contentType: peerPicker.contentType

    Rectangle {
        anchors.fill: parent
        ContentPeerPicker {
            id: peerPicker
            handler: ContentHandler.Destination
            visible: parent.visible

            onPeerSelected: {
                activeTransfer = peer.request()
                activeTransfer.downloadId = downloadDialog.downloadId
                activeTransfer.state = ContentTransfer.Downloading
                PopupUtils.close(downloadDialog)
            }

            onCancelPressed: {
                PopupUtils.close(downloadDialog)
            }
        }
    }
}

这个是用来显示一个Dialog。它是让我们选择我们想要放到什么地方去。这里的contentType是让我们选择我们所需要的内容的类型。


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.Popups 1.0
import Ubuntu.DownloadManager 0.1
import Ubuntu.Content 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: "contenthub-savefile.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: page
        title: i18n.tr("contenthub-savefile")

        Component {
            id: downloadDialog
            ContentSaveDialog { }
        }

        SingleDownload {
            id: downloader
            autoStart: false
            onDownloadIdChanged: {
                PopupUtils.open( downloadDialog, page, {"contentType" : ContentType.Pictures,
                                    "downloadId" : downloadId } )
            }

            onFinished: {
                print("download finished! saved path: " + path);
            }
        }


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

            Image {
                id: image
                source: "images/sample.jpg"
                width: page.width/2
                height: page.height/2
            }

            Button {
                anchors.horizontalCenter: parent.horizontalCenter
                text: "Save"
                onClicked: {
                    console.log("image source: " + image.source);
                    downloader.download(image.source)
                }
            }
        }
    }
}



在这里,我们使用了DownloadManger里的SingleDownload来把我们所想要存的照片image存到我们想要存的地方。这个地方有上面的Dialog所提供。


为了使得这个应用能够正常运行,我们必须添加content_exchange及content_exchange_source到apparmor文件中去。否则我们将收到security的一些错误信息。

contenthub-savefile.apparmor

{
    "policy_groups": [
        "networking",
        "webview",
        "content_exchange",
        "content_exchange_source"
    ],
    "policy_version": 1.3
}


运行我们的应用:


  


  


我们从上面可以看出,我们把我们自己images目录下的sample.jpg传到我们的Gallery应用拥有的目录里了。


qml: download finished! saved path: /home/phablet/.cache/com.ubuntu.gallery/HubIncoming/12/sample.jpg


所有源码在:git clone https://gitcafe.com/ubuntu/contenthub-savefile.git





作者:UbuntuTouch 发表于2015/8/17 17:15:54 原文链接
阅读:157 评论:0 查看评论

Read more