Canonical Voices

What Ubuntu Touch Development in CSDN (Chinese) talks about

UbuntuTouch

[原]创建第一个Ubuntu Touch应用

如果你还没有安装好你的环境的话,请参考"Ubuntu SDK 安装"章节来安装好自己的SDK环境。这篇文章的主要目的是为了检查我们所安装的环境是否正确以确保我们所安装的环境是正确的。

1)  创建一个简单的QML应用
  • 启动Ubuntu SDK
  • 选中菜单"File" ==> "New File or Project"
  • 选中"App with Simple UI"


  • 选中"Choose",然后选择所需要创建的项目的名字接路经,如下:


  • 然后接受默认的设置,就可以完成一个简单的QML应该。如下:



2)在Desktop上面运行

我们这时可以选择在IDE左下角的绿色的三角按钮或同时按下Ctrl + R。这样我们就可以在默认的情况下在Desktop下运行该应用。如果我们能够看见如下的画面,说明我们的安装是没有问题的。



3)在模拟器上运行应用

为了能够在模拟器上运行我们的应用,我们可以按如下的操作进行:

  • 启动Ubuntu SDK
  • 选择IDE左侧的"Devices",并同时选中我们已经创建的模拟器(我先前已经创建好myinstance)。同时点击图中的绿色的按钮以启动模拟器。



  • 回到我们先前的界面,如果在创建项目时没有选择emulator Kit的话,我们可以通过如下的方式来再添加:

  • 同时我们设置选好运行时的emulator Kit

  • 使用快捷键Ctrl + R 或点击屏幕左下的三角型的运行按钮。 这样我们就可以看到如下的画面:



如果我们看见这样的画面,我们可以认为我们的模拟器环境是没有问题的。我们可以接下来让这个应用在手机中运行。

3)在手机中运行

为了在手机中运行该应用,我们首先得把自己的手机连接到自己的开发电脑。我们可以通过如下的步骤:

  • 启动Ubuntu SDK
  • 点击IDE 左侧的"Devices",并同时点击"Ubuntu Device" (这是一个默认的名字,该名字可以修改)这时我们在Qt Creator IDE中可以看到如下的界面:
  • 点击"AutoCreate"按钮,安装Device Kits。这个过程可能需要一些时间,需要耐心等待
  • 保持"Ubuntu Device"为当前选定的设备



  • 回到项目页面。如果先前在创建应用时没有选择手机的Kit,我们可以通过如下的方式加入

  • 同时选择运行时的Kit

  • 直接使用快捷键Ctrl + R或按下屏幕左下方的运行按钮(绿色的三角按钮)


这样就可以在手机上看到该应用的运行情况。



4)创建一个"App with QML extension Library" 应用

现在我们来创建一个带有QML extension Libray的应用,并运行它:







我们选择默认的设置,直至到如下的界面:



记得选中"Ubuntu Device (GCC armhf-ubuntu-sdk-14.10-utopic)",这样是为了可以在以后在手机上面直接运行。如果在创建的时候没有选上,可以在主界面中,选中"Projects",并选中”Add Kit".



为了使得该应用在模拟器中运行:
  • 点击"Devices", 然后点击自己先前创建的模拟器(对我的情况是myinstance)
  • 点击模拟器中绿色的按钮以启动模拟器
  • 如果"Device Kits"没有被添加,点击"AutoCreate"按钮进行安装。期间如果没有安装相应的chroot,系统会提示你安装相应的chroot。如果是这样的话,安装的过程可能需要一定的时间,请耐心等待



  • 等"Device Kits"安装完后,就是如下的画面:


  • 回到"Projects"界面,点击"Add Kit"。选中刚刚创建的"myinstance (GCC i386-ubuntu-sdk-14.10-utopic)" (这个名字可能会和你自己选择的名字不同)
  • 选择IDE左下角的桌面图标,然后选择不同的架构进行运行即可。对模拟器架构来说,选择”myinstance (GCC i386-ubuntu-sdk-14.10-utopic)"。这样就可以使得应用在模拟器中运行了

5)怎么import一个项目并运行它

我们知道,目前我们的SDK支持两种的项目文件:
  • 具有.qmlproject后缀的项目文件,比如“Flickr.qmlproject”。这种情况针对的是项目没有C++代码的纯QML/Javascript项目。目前在SDK中,”App with Simple UI“及”App with tabbed UI"都是这类的项目
  • 具有“CMakeLists.txt”的项目文件。这类项目通常是有C++代码的项目
无论对哪种项目来说,我们只需要打开项目的项目文件即可import整个项目。具体操作如下:



我们也可以直接使用热键Ctrl + O




一旦项目被import进来后,我们就可以直接按我们先前将的方法在不同的架构下运行我们的应用了。如果有的架构没有被加入,我们可以使用如下的方法加入:





一旦选择好我们的架构,可以按下IDE左下角的绿色按钮或热键Ctrl + R。



总结,在这编文章中,我们介绍了怎么创建一个最基本的应用及怎么在不同的框架中运行该应用。通过这样的实践,我们可以检验我们的安装环境是否正确,同时也达到熟悉整个的运行环境的目的。在下一个章节中,我们将介绍怎么生成一个click安装包,并如何安装它到手机中。
作者:UbuntuTouch 发表于2014-8-6 9:47:52 原文链接
阅读:74 评论:0 查看评论

Read more
UbuntuTouch

这里我们先设想你们已经把手机刷到Ubuntu Touch最新软件。下面我们来介绍怎么生成Click package,并安装到手机中。


1) 生成Click Package

  • 启动Ubuntu SDK
  • 打开已经创建的应用


  • 选择IDE左下方的目标架构为"Ubuntu Device (GCC armhf-ubuntu-sdk-14.10-utopic)"
  • 选中IDE 左侧的"Publish",在这个框中我们可以设置我们所需要的一些东西,比如说应用的Title等

  • 点击"Click Package",这样在和项目目录"test2"平行的一个目录中"build-test2-Ubuntu_Device_GCC_armhf_ubuntu_sdk_14_10_utopic-Default"生成一个叫做"com.ubuntu.developer.liu-xiao-guo.test2_0.1_all.click"的click文件。这个即是可以安装到手机的文件。

2)安装Click文件包到手机上

启动一个Terminal。我们可以通过如下的指令来完成安装的工作

$ adb push com.ubuntu.developer.liu-xiao-guo.test2_0.1_all.click /tmp
$ adb shell "sudo -iu phablet pkcon install-local /tmp/com.ubuntu.developer.liu-xiao-guo.test2_0.1_all.click"




这样在手机中的"应用”页面就可以找到我们的应用了。如果找不到的话,可以通过搜索的方式寻找它:



3)通过当前项目生成click包

我们也可以同过IDE的集成环境来完成应用的安装。具体的步骤如下:
  • 选中当前的项目(对纯QML项目,无C++代码)
  • 点击右键


我们可以在项目当前目录退后的一个目录找到所需要的click包。比如对我们的项目”balloon"来说,在目录build-balloon-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-default里可以找到"com.ubuntu.developer.liu-xiao-guo.balloon_0.1_all.click"包。一旦生成这个包,我们可以按上述讲的方法来安装我们生成的应用。


4)查看Click安装包中的内容。

有时我们想查看一下Click安装包中到底有那些的内容,我们可以打入如下的命令:

$ click contents com.ubuntu.developer.liu-xiao-guo.test2_0.1_all.click


我们也可以通过如下的命令来得到click包里所有的文件。把我下面的click包文件名换成你自己的包的名字即可以

dpkg -x myapp.click unpacked
file unpacked/path/to/your/binary

通过”file"命令来查看文件的特性,比如:

/tmp/unpacked/lib/arm-linux-gnueabihf/bin/filemanager: ELF 32-bit LSB  executable, ARM, . . 

可以看到确实,该文件是一个ARM的可执行文件。



关于click命令还有其他的很多的功能,我们可以通过:

$ click --help

来查看它的具体的用法。

5) 登陆到手机

我们可以通过如下的命令来登陆到手机

$ adb shell

我们也可以通过如下的命令来切换到"phablet"账号中

$ root@ubuntu-phablet:~# su - phablet

如果需要安装软件需要密码的话,密码是"phablet"



作者:UbuntuTouch 发表于2014-8-6 9:56:09 原文链接
阅读:66 评论:0 查看评论

Read more
UbuntuTouch

[原]用Qt Quick快速设计UI

这是一个视频教程。我先前的同事已经把它给录下来了。我觉得非常好。具体的地址 

是:http://v.youku.com/v_show/id_XMjM0NjczMjE2.html。现在分享给大家。可能大家不能找到源码。大家可以参考我的设计在如下地址:

https://code.launchpad.net/~liu-xiao-guo/debiantrial/flickr

你可以先安装bzr。具体步骤如下:

$ sudo apt-get install bzr

然后用如下的命令来下载我的代码:

$ bzr branch lp:~liu-xiao-guo/debiantrial/flickrfinal

具体运行效果图如下:






该代码在Ubuntu手机上可以运行。最终程序和演讲的是有些不同。供大家参考!

更多有用的的连接:


作者:UbuntuTouch 发表于2014-8-6 9:57:08 原文链接
阅读:68 评论:0 查看评论

Read more
UbuntuTouch

[转]QML入门教程(1)

这接下来的几篇关于Qt Quick的文章是我以前的同事在Qt官方网站上发表的文章翻译过来的,我觉得很好。这次我把它重新整理一下供大家一起来学习吧。


QML是什么?

QML是一种描述性的脚本语言,文件格式以.qml结尾。语法格式非常像CSS(参考后文具体例子),但又支持javacript形式的编程控制。 它结合了QtDesigner UI和QtScript的优点。QtDesigner可以设计出.ui界面文件,但是不支持和Qt原生C++代码的交互。QtScript可以和Qt原生代码进行交互,但是有一个缺点,如果要在脚本中创建一个继承于QObject的图形对象非常不方便,只能在Qt代码中创建图形对象,然后从QtScript中进行访问。而QML可以在脚本里创建图形对象,并且支持各种图形特效,以及状态机等,同时又能跟Qt写的C++代码进行方便的交互,使用起来非常方便。

如何使用?

不过本文不会去介绍如何在Qt C++中使用QML,而是把重点放在QML的语法上,不过别担心看不到.qml文件的效果。Qt提供了一个工具QML Viewer可以查看.qml文件生成的效果,该程序在Qt的bin目录下,应用名字叫qml(Windows下叫qml.exe)。所以你在看到别人提供的.qml文件时,你可以用下面命令qmlscene filename.qml 查看.qml的执行结果,咱们的第一个Hello,World生成界面如下



开始QML吧

上面的Hello,World源代码如下

1 import QtQuick 2.0
2
3 Rectangle {
4 id: page
5 width: 500; height: 200
6 color: “lightgray”
7
8 Text {
9 id: helloText
10 text: “Hello world!”
11 font.pointSize: 24; font.bold: true
12 y: 30; anchors.horizontalCenter: page.horizontalCenter
13 }
14 }

第1行是Qt QML的统一用法,指明当前QML会使用Qt-4.7里已经定义好的类型,比如第3行的Rectangle和第8行的Text。第3行开始至文章结束处则定义了一个矩形的图形区域对象,第4行则申明了该矩形区域对象的id为”page”可以被其它对象识别并通过该id访问其成员属性,另外几个属性width/height/color则很好理解,语法跟CSS类似,可以写在一行中用分号”;”隔开。第8行至第12行则是一个文本对象,看它代码的嵌套结构可以知道它是内嵌于Rectangle的。Text的属性除了anchors其它几个都能顾名思义。anchors描诉的是当前对象的位置和其它对象的相对关系,这里看到其水平中心位置在“page“的水平中心位置。如果想对anchors了解更多,请参考锚的解释

以上就是Hello,World的全部代码,将其存为hellowordl.qml,那么只要执行qmlscene hellowrold.qml就能看到文章前头的界面了。

bzr branch lp:~liu-xiao-guo/debiantrial/hello1

更多对象?

在上面的代码中我们用到了Rectangle和Text,那么我还有哪些对象以及这些对象有哪些属性呢。那么请访问QML-Item类,Item类是QML最基础的类,通过查看它的继承类以及这些继承类可用的属性,你可以添加更多你感兴趣的内容。

好吧, Happy QML。

作者:UbuntuTouch 发表于2014-8-6 9:49:10 原文链接
阅读:52 评论:0 查看评论

Read more
UbuntuTouch

[原]Ubuntu SDK 安装

在这篇文章里,你将学到如何安装Ubuntu SDK到你的系统中,并生成一个简单的应用以测试你的安装是否成功。对英文好的学习者,可以参考Ubuntu 网站中的英文地址来进行安装。


操作系统选择

Ubuntu Touch是在Ubuntu 14.10 (Utopic)。为了能够使得Scope应用的开发编译成功,Ubuntu SDK应该安装在Utopic的Ubuntu OS之中。如果你使用的操作系统不是这个版本的,你可以安装一个VM(比如VirtualBoxVMWare),在VM中再安装Ubuntu OS 14.10版本。

添加Phablet Tools PPA

Phablet Tools PPA 提供了一些额外的工具来对device进行安装。这个工具是安装在从Ubuntu OS 12.04以后的版本中的。

你可以在Ubunt 14.04 Trusty 中并不需要添加,因为它已经在Ubuntu通用的发布中。你可以通过如下的方式进行添加:

$ sudo add-apt-repository ppa:phablet-team/tools

添加Ubuntu SDK 发布 PPA中

按照一下方式添加Ubuntu SDK 发布 PPA (https://launchpad.net/~ubuntu-sdk-team/+archive/ppa)。
输入你的Linux管理员密码

$ sudo add-apt-repository ppa:ubuntu-sdk-team/ppa

安装 Ubuntu SDK

按一下方式安装SDK。在需要的时候输入Linux管理员密码

$ sudo apt-get update && sudo apt-get install ubuntu-sdk

提示:对一些人,特别是对那些安装Ubuntu 14.10 ( Utopic)的开发者来说,必须确保所有的安装的包更新到最新的
版本。这个可以通过如下的命令实现:

$ sudo apt-get update && sudo apt-get dist-upgrade

启动Ubuntu SDK IDE

  • 在Ubuntu "Unity Dash Applications lens"中寻找 "Ubuntu SDK
  • 点击找到的”Ubuntu SDK" 图标


你也可以在shell中启动Ubuntu SDK:

$ ubuntu-sdk 

提示:对一些开发者来说,他们可能很想让Ubuntu SDK IDE的图标出现在Ubuntu Unity 的启动面板中,这样可以每次很方便地启动。只要先启动SDK,然后在Ubuntu桌面的左侧的启动面板中,找到SDK的图标,并按下右键,然后选定"Lock to Launcher"。这样,SDK 就可以固定在启动的面板中了。

当我们第一次启动Ubuntu SDK时,可以看到如下的界面:



我们会在下面的步骤中介绍如何来安装不同的target及模拟器(emulator)。

安装Ubuntu SDK armhf chroot

这个步骤是为了交叉编译我们所开发的应用(armhf格式)并部署到手机上。我们可以通过如下的步骤进行安装:

  • 启动Ubuntu SDK
  • 选中IDE菜单中的"Tools",然后在选中"Options",然后再选中”Ubuntu"。就会看到如下的画面
  • 点击"Create Click Target",然后可以看到如图所示的对话框。选择"armhf/Framework-14.10"即可。之后你可以看到安装开始。依赖于你的网络的情况,安装需要一段时间。耐心等待。


在上图中,我们可以看到已经安装好的"utopic ubuntu-sdk ... armhf",这里我们可以点击"update"来更新我们所安装的包,同时,我们也可以看到"Maintain"这个按钮。这个是用来对我们的chroot来进行维护的。比如说我们所开发的应用中,可能需要一个库,但它不是标准的库,没有安装。这时我们想测试时,就可以点击这个按钮,并在shell中进行安装或删除某个包。当然我们必须也要记得在手机中进行安装这个库以使编译好的应用能够运行。

等安装完后,我们可以在shell中看到如下的信息:

~$ schroot -l
chroot:click-ubuntu-sdk-14.10-armhf
chroot:trusty-amd64-armhf
chroot:trusty-armhf
chroot:utopic-amd64-armhf
source:click-ubuntu-sdk-14.10-armhf
source:trusty-amd64-armhf
source:trusty-armhf
source:utopic-amd64-armhf

这里 "chroot:click-ubuntu-sdk-14.10-armhf"就是我们在这个步骤中安装的chroot。有了这个我们就可以为手机target生成目标安装文件进行部署了

安装Ubuntu SDK i386 chroot

这个安装是为了使得以后我们含有C++代码(比如说C++ plugins)的应用能够顺利编译并使得应用在模拟器中运行。我们可以一并安装,在以后需要的时候我们可以生下这个步骤。这个安装过程同样需要很长的时间。需要耐心等待。这个安装步骤和上面几乎是一样的,只是我们需要选择"i386"架构。



安装完后,我们可以在shell中通过如下的命令查看已经安装好的chroot:

~$ schroot -l
chroot:click-ubuntu-sdk-14.10-armhf
chroot:click-ubuntu-sdk-14.10-i386
chroot:trusty-amd64-armhf
chroot:trusty-armhf
chroot:utopic-amd64-armhf
source:click-ubuntu-sdk-14.10-armhf
source:click-ubuntu-sdk-14.10-i386
source:trusty-amd64-armhf
source:trusty-armhf
source:utopic-amd64-armhf

安装模拟器

这个步骤是为了安装一个在手机一个模拟器以仿真一个手机,这样开发者可以在电脑上进行开发及测试。等调试好了以后,就可以部署到我们的真手机中以进行下一步的测试。具体的安装步骤如下:

  • Ubuntu 启动SDK
  • 选择IDE左侧的"Devices",然后在所在的界面中点击图中的"+"。这样就可以看到如下的画面
  • 在所显示的对话框中,输入所需要的模拟器的名字。选择"i386",然后点击"Create"即可。整个过程可能会花很长的时间完成。请耐心等待。这个安装虽然也可以选择"armhf"来进行模拟,但目前建议的还是"i386"架构。


有了这个模拟器,我们就可以在模拟器中运行我们开发的应用了。我们可以选择刚才生成的模拟器(myinstance),并运行它:



实际运行的效果图如下,



开发者也可以参阅https://wiki.ubuntu.com/Touch/Emulator文章来安装自己的模拟器。

总结

至此,我们的开发安装环境基本上已经好了。在下一个章节中,我们来试着创建一个应用来检测一下我们的环境是否已经成功了。我们可以转到"开发第一个Ubuntu Touch应用"来检查我们的安装环境是否正确。

作者:UbuntuTouch 发表于2014-8-6 8:53:28 原文链接
阅读:58 评论:0 查看评论

Read more
UbuntuTouch

[转]QML入门教程(2)

QML入门教程(2)

在上一篇文章里我们使用了最基础的QML类型实现了文字Hello,World的显示。这篇文章中将会增加颜色选项面板,用户可以给Hello,World设置不同的颜色,如下图显示
Qml2.png

QML组件

从图中可以看到选项面板由6个颜色小块组成,它们唯一的区别就是颜色不一样。那么我们就可以用组件(Component)来实现一个颜色块,然后在需要的时候使用这个组件就可以了。组件其实和其它编程语言中的宏,函数,类,结构体等功能差不多,就是代码复用。作为程序员,我知道你懂的。 组件由一个单独的QML文件名组成,文件名总是以大写字母开头,要使用该组件的时候直接使用该文件名就可以了。关于如何定义自己的组件,请访问Defining new Components。我们为一个颜色块定义了一个Cell.qml文件,然后使用Cell作为一个去访问它。


Cell.qml的内容

import QtQuick 2.0
 
Item {
id: container
property alias cellColor: rectangle.color
signal clicked(color cellColor)
width: 40; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}
}

挨个看代码~

Item {
id: container
property alias cellColor: rectangle.color
signal clicked(color cellColor)
 
width: 40; height: 25

Item是最常使用的QML类型,一般用作其它类型的容器,可以理解成最顶级的父类,功能类似于QtGui中的QWidget。用一个属性别名访问其内嵌对象rectangle的color属性。在其它文件中可以用Cell对象的cellColor获得rectangle的color值。 signal clicked(color cellColor)则为对象定义了一个信号,在代码的其它部分可以发出这个信号。

Rectangle {
id: rectangle
border.color: “white”
anchors.fill: parent
}

这一部分没有特别好说的,在Item中内嵌了一个id为rectangle白边框的矩形区域,大小占满整个Item。

MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}

MouseArea则为Item增加了一块鼠标响应区,看它的anchors知道,在整个Item区域内都是鼠标活动区,都能侦听到鼠标事件。onClicked那一行则相当于为鼠标单击事件增加了一个处理行为,这里是发出了一个clicked()的信号。这个信号正是我们在Item里定义的那个signal。 Cell.qml写完了,再来看看程序的主文件。


main.qml的内容

import QtQuick 2.0
Rectangle {
id: page
width: 500; height: 200
color: "lightgray"
 
Text {
id: helloText
text: "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
}
Grid {
id: colorPicker
x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
rows: 2; columns: 3; spacing: 3
 
Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
}
}

这里在原来的基础上增加了一个Grid网格。x坐标是4,底部挨着page的底部,所以我们看到的是在左下角。 新增的6个Cell,名字和Cell.qml是一样的。通过cellColor属性将颜色传给了每个颜色块。 当Cell接收到onClicked事件的时候,关联的代码回去修改Hello,World上的颜色。细心的朋友可能会注意到Cell只是定义了clicked()的信号,并没有定义onClicked()啊,是的这就是Component的语法规则了。如果你在Cell.qml里定义的是clicked(),那么你在main.qml中引用的时候就该用onClicked()了。

好了,代码也不少了,随便改动改动,你会了解更多QML的秘密的:) 

这个练习的源码可以在如下地址下载:

bzr branch lp:~liu-xiao-guo/debiantrial/hello2

作者:UbuntuTouch 发表于2014-8-6 9:52:44 原文链接
阅读:56 评论:0 查看评论

Read more
UbuntuTouch

目前在Ubuntu Touch提供了丰富的开发途径供开发者进行开发。它覆盖了从本地开发到Web的应用开发。下面我来介绍一下有那些开发的语言及途径可以用来开发:





1)Scope 应用开发

Scope 这个词对很多的开发者来说可能比较陌生,到目前为止也没有一个相应的中文词来精确地描述它到底是什么。简单地说Scope是Ubuntu OS区别于其他系统的一个独特的UI显示及应用框架。它很容易使得应用的内容得以显示及按照不同的要求进行呈现。使用Scope,可以使得运营商,OEM及应用开发着可以很方便地把一些Web Service的内容方便简洁地呈现,并使得内容很容易被搜索和发现。常见的Scope有应用商店,音乐商店,视频商店等。我们也可以包Amazon及淘宝商店也用Scope进行实现。下面我们通过一些图片看一些Scope到底长的是什么样子:










就其实质,就是利用一些现有的Web Service API来通过返回的json或xml数据,Scope利用其自身已经建立起的显示框架(有许多不同的布局供选择)来呈现数据,同时利用Scope的框架很方便地进行搜寻。

Scope的开发可以使用Qt C++, C++11及Scope 相应的C++API进行开发。

更多关于Scope的资料可以在Ubuntu的网站上http://developer.ubuntu.com/scopes/查看到。在那里可以看到相应的Tutorials

2)利用Qt/QML (QtQuick)进行开发

Ubuntu的应用基本上都是使用Qt C++/QML进行开发的。这也是Ubuntu Touch的本地开发语言。使用Qt 语言可以开发出很炫很酷的应用。目前BlackBerry的平台也是使用Qt/QML来进行开发的。具体关于Qt/QML的更多的介绍,开发者可以在网站http://qt-project.org/找到。在Ubuntu 网站上,我们也可以看到关于Qt/QML的一些介绍,请查阅网站http://developer.ubuntu.com/apps/qml/tutorial/

3)利用HTML 5开发Web应用

Ubuntu Touch上面也可以使用HTML 5进行应用开发。通过使用ApacheCordova & Phonegap及HTML 5,Javascript, CSS,开发者可以开发出像本地应用一样界面的应用。通过Cordova API接口,应用可以访问系统的很多的应用接口,比如Camera, battery, geolocation, media-capture等。



更多的关于Web的应用开发可以在Ubuntu网站http://developer.ubuntu.com/apps/html-5/tutorials/


作者:UbuntuTouch 发表于2014-8-6 9:58:16 原文链接
阅读:74 评论:0 查看评论

Read more
UbuntuTouch

[转]QML入门教程(3)

经过前面两个教程,文字也能显示,也能处理鼠标事件了,来点动画吧。
Qml3.gif
这个教程实现了当鼠标按住的时候,Hello,World从顶部到底部的一个旋转过程,并带有颜色渐变的效果。

完整的源代码main.qml

import QtQuick 2.0
 
Rectangle {
id: page
width: 500; height: 200
color: "lightgray"
 
Text {
id: helloText
text: "Hello World!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
 
MouseArea { id: mouseArea; anchors.fill: parent }
 
states: State {
name: "down"; when: mouseArea.pressed == true
PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}
 
transitions: Transition {
from: ""; to: "down"; reversible: true
ParallelAnimation {
NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }
ColorAnimation { duration: 500 }
}
}
}
 
Grid {
id: colorPicker
x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
rows: 2; columns: 3; spacing: 3
 
Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
}
}

除了这个main.qml之外,还有一个Cell.qml也是需要的,和教程(2)中的完全一样。下面来看一看比起教程(2)的代码增加出来的内容。

    Text{
...
states: State {
name: "down"; when: mouseArea.pressed == true
PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}
 
transitions: Transition {
from: ""; to: "down"; reversible: true
ParallelAnimation {
NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }
ColorAnimation { duration: 500 }
}
}
...
}

states内嵌于Text之中,可以为Text元素添加多个状态,现在的这个例子只增加了一个状态。该状态的名为为”down”,然后由“when”指 定了什么时候触发这个状态。PropertyChanges则指定了哪个元素的哪些属性会发生什么样的变化。例子中PropertyChanges利用 “target”指定了id为”helloText”的元素会发生变化,包括其y,rotation,color等属性。
transitions 是用于增加动画效果的(如果把transitions这一段代码删去,Hello,World的文字也会发生变化,但是看不到中间动画渐变效果)。同样可 以看到transitions是复数形式,意味着可以添加多个动画过程。“from”和”to”指明了当前的动画作用于哪两个状态变化之间。 “from”和”to”的参数名来自于State中的”name”属性。
ParalleAnimation则指定了有多个 有多个动画并行发生。NumberAnimation用于qreal类型的属性变化,ColorAnimation则用于颜色变化。更多 Animation请在QML文档中查找”Animation and Transitions”。
好了,三篇教程到此结 束。

所有的源码可以在如下的地址下载:

bzr branch lp:~liu-xiao-guo/debiantrial/hello3

更多例程可以在Qt网站找到:https://qt-project.org/doc/qt-4.7/gettingstartedqml.html

作者:UbuntuTouch 发表于2014-8-6 9:54:03 原文链接
阅读:55 评论:0 查看评论

Read more
UbuntuTouch

[原]调试QML应用

Console API

Log

console.log, console.debug, console.info, console.warnconsole.error 可以用来打印输出调试信息到console.。比如:

function f(a, b) {
  console.log("a is ", a, "b is ", b);
}


这个输出是用使用Qt C++中的qDebug, qWarning, qCritical 方法(可以参考http://doc.qt.nokia.com/latest/debug.html#warning-and-debugging-messages)。

设置环境变量QML_CONSOLE_EXTENDED也将输出在源码中的调用。

Assert

console.assert 是用来测试表达式是真或是否。如果不是真,它将输出一个可选的输出信息到console中,并打印出stack trace。


function f() {

  var x = 12
  console.assert(x == 12, "This will pass");
  console.assert(x > 12, "This will fail");
}

Timer

console.time 及 console.timeEnd 用来记录以毫秒计算的在调用函数之间所花费的时间。两个都带有一个字符串参数来标示测量时间。比如:

function f() {
    console.time("wholeFunction");
    console.time("firstPart");
    // first part
    console.timeEnd("firstPart");
    // second part
    console.timeEnd("wholeFunction");
}

Trace

console.trace 用来打印Javascript在被执行时当前代码的stack trace。 stack trace 的信息含有函数的名称,文件名称,代码行的数字及列数。 stack trace 仅限于最后的10个stack frames.

Count

console.count 用来输出输出某个特别代码已经被执行多少次,同时显示一个信息。

function f() {
  console.count("f called");
}

上述代码将输出 f called: 1f called: 2 ... 无论 f() 在任何时候被执行。

Profile

console.profile 启动 QML 及 JavaScript profilers。嵌套的调用不被支持。如果是这样的情况,一个警告的信息将被输出到console。

console.profileEnd 关掉QML及JavaScript profilers。在没有调用console.profile的情况下,调用这个函数将输出一个警告信息到console。一个profiling的客户端应该在调用这个函数之前已经连接起来以使得这个函数能够接受并存储profiling的数据。比如:

function f() {
    console.profile();
    //Call some function that needs to be profiled.
    //Ensure that a client is attached before ending
    //the profiling session.
    console.profileEnd();
}

Exception

console.exception 用来输出一个错误的信息并生成在Javascript代码被执行处的stack trace。

Debugging module imports

QML_IMPORT_TRACE 环境变量可以用来被设置为启动输出来自QML的import loading机制的调试信息。

比如,一个简单的QML文件:

import QtQuick 2.0
Rectangle { width: 100; height: 100 }

如果你设置QML_IMPORT_TRACE=1,之后运行QML Scene (or或是你的QML C++应用),你将看到类似如一下的输出:

QQmlImportDatabase::addImportPath "/qt-sdk/imports"

QQmlImportDatabase::addImportPath "/qt-sdk/bin/QMLViewer.app/Contents/MacOS"
QQmlImportDatabase::addToImport 0x106237370 "." -1.-1 File as ""
QQmlImportDatabase::addToImport 0x106237370 "Qt" 4.7 Library as ""
QQmlImportDatabase::resolveType "Rectangle" = "QDeclarativeRectangle"

Debugging with Qt Creator

Qt Creator 提供了一个集成好的QML调试工具。QML项目及单独的C++应用程序(使用QML)可以在desktop或是在remote装置上(比如说是用USB连接的手机)。关于这方面的更多信息请参阅Qt Creator使用手册。


作者:UbuntuTouch 发表于2014-8-25 8:38:33 原文链接
阅读:7 评论:0 查看评论

Read more
UbuntuTouch

目前Ubuntu Touch支持的手机的列表可以在如下的网址找到:

http://developer.ubuntu.com/start/ubuntu-for-devices/devices/


Device

Android Image Name

Android Driver Name

Nexus 4

occam

mako

Nexus 7 2013 WiFi

razor

flo

Nexus 10

mantaray

manta


从这个表格中可以看到目前Ubuntu Touch所支持的装置。这时目前官方所支持的装置。


另外,BQMeizu也将于2014年的下半年出正式的商业版的Ubuntu Touch装置。让我们翘首以待吧!


如果你已经有一个Nexus的装置,请按如下的连接提供的方式进行刷机,就可以直接体验Ubuntu Touch的精彩了!


http://developer.ubuntu.com/start/ubuntu-for-devices/installing-ubuntu-for-devices/


只要你严格按照上面的指令进行刷机,就不会有问题。我曾经把手机给刷死了。最终我严格按照上面的步骤进行刷机,总能让我的Nexus 4起死回生!

作者:UbuntuTouch 发表于2014-8-6 16:33:12 原文链接
阅读:5 评论:0 查看评论

Read more
UbuntuTouch

我们知道QML虽然是很强大的,但是有时我们觉得它有些功能还是需要C++来拓展的话,这时我们可以使用IDE提供的plugin架构来创建一个新的plugin。这个plugin可以在QML里直接调用。它的使用就像先前定义好的控件一样。首先我们来看一下我们最终设计的界面。这里是一个交通灯的示范例程:


                 

这里显示的是几个交通灯变化的画面。它们也同时对应我们现实生活中的交通灯变化的几个状态。


在这篇文章里,我们将学到如下的东西:

  • 学习怎么制作一个plugin
  • 学习property binding
  • 学习QML的状态(state)及过渡(transition)
  • 学习怎么制作Component

1) 创建一个基本的应用

首先我们启动Qt Creator IDE,然后选择如下所示的template (App with QML extension Library):



创建一个称作"TrafficLight"的项目:



同时也把应用的名字设定为“Light":






然后,我们按IDE提示的步骤完成以后的过程创建出我们的最原始的一个template应用。我们可以开始在desktop及"armhf"的环境中运行。如果我们还有问题,这可能是我们的安装程序有些问题。请参考我们的SDK安装文章进行修正。

2) 完成TrafficLight plugin


我们知道QML提供了很好的图形控件。QML有一个称作LinearGradient的Gradient,但他不是我们想要的RadialGradient。经过寻扎,我们在C++中找到相应的QRadialGradient类,它可以实现我们想要的功能。我们怎来把它使用到我们的C++应用中呢?

首先我们找到项目中的文件目录“TrafficLight/backend/modules/Light”,创建“trafficlight.h"及“trafficlight.cpp"文件,并输入如下的代码

#ifndef TRAFFICLIGHT_H
#define TRAFFICLIGHT_H

#include <QtQuick/QQuickPaintedItem>
#include <QColor>

class TrafficLight : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)

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

    QColor color() const;
    void setColor(const QColor &color);

signals:
    void colorChanged();

public slots:

private:
    QColor m_color;
};

#endif // TRAFFICLIGHT_H


<pre style="margin-top: 0px; margin-bottom: 0px;"><!--StartFragment--><span style=" color:#000080;"></span>

#include <QPainter>
#include <QRadialGradient>

#include "trafficlight.h"

TrafficLight::TrafficLight(QQuickItem *parent)
    : QQuickPaintedItem(parent)
{
}

QColor TrafficLight::color() const
{
    return m_color;
}

void TrafficLight::setColor(const QColor &color)
{
    if ( color == m_color )
        return;
    else {
        m_color = color;
        update();   // repaint with the new color
        emit colorChanged();
    }
}

void TrafficLight::paint(QPainter *painter)
{
    QRectF rect(boundingRect());

    QPen pen;
    pen.setWidthF( 0 );
    pen.setColor(m_color);
    painter->setPen( pen );

    QRadialGradient g( rect.width()/2, rect.height()/2,
                       rect.width()/2, rect.width()/2, height()/2 );

    g.setColorAt( 0.0, Qt::white );
    g.setColorAt( 1.0, m_color );
    painter->setBrush( g );

    painter->drawEllipse(rect);
}

这里我们定义了一个称作为“TrafficLight”的类。根据不同的颜色,用它来画一个“QRadialGradient”。我们可以看到在这个类中我们定义了一个称作“color"的property。
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)

在下面的QML语言中,我们可以直接通过绑定这个property,也可以对它进行修改从而使得界面发生改变。

我们找到"TrafficLight/backend/CMakeLists.txt"文件,并加入如下的语句:

set(
    Lightbackend_SRCS
    modules/Light/backend.cpp
    modules/Light/mytype.cpp
<strong>    modules/Light/trafficlight.cpp</strong>
)

这时,我们可以关闭项目再打开项目,或者直接在项目框中点击右键,并运行“Run CMake”以使刚加入的文件在项目框中可见。


找到“backend.cpp"文件,并加入如下语句:

#include <QtQml>
#include <QtQml/QQmlContext>
#include "backend.h"
#include "mytype.h"
<strong>#include "trafficlight.h"</strong>

void BackendPlugin::registerTypes(const char *uri)
{
    Q_ASSERT(uri == QLatin1String("Light"));

    qmlRegisterType<MyType>(uri, 1, 0, "MyType");
<strong>    qmlRegisterType<TrafficLight>(uri, 1, 0, "TrafficLight");</strong>
}

void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
    QQmlExtensionPlugin::initializeEngine(engine, uri);
}

这里注册的目的是为了让“TrafficLight”类在QML文件中可以被实例化。第二个"TrafficLight"可以是任何你喜欢的名字,只要它可以和QML中的引用对应即可。

这样我们基本上完成了对plugin的设计。我们这时可以编译一下看一下有什么问题。如果没有的话,我们可以直接进入下面的章节。

3)在QML中引用TrafficLight类型


我们下面来做一个简单的实验,看TrafficLight是否被正确地调用。修改“TrafficLight.qml”文件如下:

import QtQuick 2.0
import Ubuntu.Components 0.1
import "ui"
<strong>import Light 1.0</strong>

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

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

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

    width: units.gu(100)
    height: units.gu(75)

  TrafficLight{
        id: redlight
        width: 100
        height: 100
        color:"red"
    }
}

在Desktop上运行,我们可以看到:



我们可以看到plugin是真的被调用了。我们可以改变图形的位置或颜色来看看有什么变化等。

4)应用设计

通过修改"TrafficLight.qml"文件,我们首先来看一看我们设计的程序如下:

import QtQuick 2.0
import Ubuntu.Components 0.1
import "ui"
import Light 1.0

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

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

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

    width: units.gu(100)
    height: units.gu(75)

    Page {
        id:main
        anchors.fill: parent
        property int radius: 155

        Image{
            anchors.horizontalCenter: parent.horizontalCenter
            height:parent.height
            source: "light2.png"
            fillMode: Image.PreserveAspectFit

            Column {
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.verticalCenter: parent.verticalCenter

                spacing: 28

                TrafficLight{
                    id: redlight
                    width: main.radius
                    height: main.radius
                    color:"red"
                }

                TrafficLight{
                    id: yellowlight
                    width: main.radius
                    height: main.radius
                    color:"yellow"
                }

                TrafficLight{
                    id: greenlight
                    width: main.radius
                    height: main.radius
                    color:"green"
                }
            }
        }
    }
}

在这里我们使用了一个"Column"的layout管理器,它可以使得在它之内部件(Component)从上到下进行显示和管理。如果屏幕的尺寸发生变化,它也会帮我们自动适配及调整。你可以看出我们创建了从上到下的三个“红”,“黄”及“绿”的交通灯。你们也可看到我在这里也使用了一个背景的图片以使得显示效果更加逼真。如果开发者没有这个图片也没有关系。我在下面贴出来(右边的图)。如果确实没有,也没关系。运行时可能看不到背景的图片而已。开发者可以把图片考到和“trafficlight.qml”相同的目录。

   


5)加入状态及过渡

我们知道,上面显示的情况显然不是我们常见的交通灯的情况。现在我们来加入状态来在不同的情况下,让各个等在不同的状态下进行关掉或熄灭。程序如下:


                states: [
                    State {
                        name: "red_on"
                        PropertyChanges {
                            target: redlight
                            color:"red"
                            scale: 1.0
                        }
                        PropertyChanges {
                            target: greenlight
                            color: "black"
                        }
                        PropertyChanges {
                            target: yellowlight
                            color: "black"
                        }
                    },

                    State {
                        name: "red_yellow_on"
                        PropertyChanges {
                            target: redlight
                            color: "red"
                        }
                        PropertyChanges {
                            target: yellowlight
                            color: "yellow"
                        }
                        PropertyChanges {
                            target: greenlight
                            color: "black"
                        }
                    },

                    State {
                        name: "green_on"
                        PropertyChanges {
                            target: redlight
                            color: "black"
                        }
                        PropertyChanges {
                            target: yellowlight
                            color: "black"
                        }
                        PropertyChanges {
                            target: greenlight
                            color: "green"
                        }
                    },

                    State {
                        name: "yellow_on"
                        PropertyChanges {
                            target: redlight
                            color: "black"
                        }

                        PropertyChanges {
                            target: yellowlight
                            color: "yellow"
                        }

                        PropertyChanges {
                            target: greenlight
                            color: "black"
                        }
                    }
                ]

在这里我们定义了4个不同的状态"red_on", "red_yellow_on", "yellow_on" 及“green_on"。这几个状态显然我们常见的几个交通灯的状况。在每个状态下,我们可以看到各个灯的”开”及”关"的情况。

QML也同时提供给我们在不同状态之间进行转换时的动画效果。我们可以通过过渡来实现:

                transitions: [
                    Transition {
                        from: "*"
                        to: "*"

                        PropertyAnimation {
                            target: redlight
                            properties: "scale, color"
                            duration: 1000
                            easing.type: Easing.InQuad
                        }
                        PropertyAnimation {
                            target: greenlight
                            properties: "scale, color"
                            duration: 1000
                            easing.type: Easing.InQuad
                        }
                        PropertyAnimation {
                            target: yellowlight
                            properties: "scale, color"
                            duration: 1000
                            easing.type: Easing.InQuad
                        }
                    }
                ]

在这里我们定义了无论从哪种状态“*”到哪种状态“*”,我们变化的时间是1000毫秒,同时要使得它的scale,及颜色发生相应的渐变。这样可以产生我们所希望的动画效果。

注意这两段代码必须是加到"Column"所在的块中。

6)加入逻辑使得他们在不同的状态之间转换


我们知道只通过简单的状态定义并不能使得应该在不同的状态之间转换。我们必须定义一个逻辑或事件使得它们在某种条件下转换。对于我们的例程,我们可以使用一个Timer来实现:

                Timer {
                    interval: 1000
                    running: true
                    repeat: true
                    property int count: 0

                    onTriggered: {
                        if (parent.state == "red_on" && count >= 5)
                        {
                            parent.state = "red_yellow_on"
                            count = 0
                        }
                        else if ( parent.state == "red_yellow_on" )
                        {
                            parent.state = "green_on"
                            count++
                        }
                        else if ( parent.state == "green_on" && count >= 5 )
                        {
                            parent.state = "yellow_on"
                            count ++
                        }
                        else if ( parent.state == "yellow_on" ) {
                            parent.state = "red_on"
                            count = 0
                        }
                        else {
                            count++
                        }
                    }
                }


这个Timer每一秒触发一次,onTriggered是以个callback方法。通过这个事件我们可以使得程序在不同的状态下转换。




至此,我们这个部分的程序已经设计完成。整个完整的代码可以在如下的地址下载:

bzr branch lp:~liu-xiao-guo/debiantrial/trafficlight

7)为程序设计Component


我们刚才设计的程序在某种程度上能够完成我们的功能。但是,设想一下,如果我们想在程序中需要更多的交通灯怎么办呢?我们可以把刚才设计的程序改一下,重新包装成一个Component。这样这个控件在许多的程序中可以复用这个控件。

在"TrafficLight.qml"所在的目录中,我们重新生成一个新的文件叫做”MyLight.qml"。这里记得文件的名称一定要大写。同时我们删除在"TrafficLight.qml"中相应的部分的设计。把相关的代码移过去。
这样重新生成的代码如下:

import QtQuick 2.0
import Ubuntu.Components 0.1
import "ui"

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

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

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

    width: units.gu(120)
    height: units.gu(80)

    Page {
        id:main
        anchors.fill: parent

        Row {
            id: myrow
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            spacing: units.gu(5)

            MyLight {
                id:light1
                width: main.width/5
                height: width*3
            }

            MyLight {
                id:light2
                width: main.width/5
                height: width*3
            }

            MyLight {
                id:light3
                width: main.width/5
                height: width*3
            }
        }

    }
}


MyLight.qml 代码

import QtQuick 2.0
import Ubuntu.Components 0.1
import Light 1.0

Item {    
    width: units.gu(100)
    height: units.gu(75)

    Rectangle {
        id: background
        anchors.fill: parent
        color: "black"
        property int size: width*0.7

        Column {
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter

            spacing: units.gu(3)

            TrafficLight{
                id: redlight
                width: background.size
                height: background.size
                color:"red"
            }

            TrafficLight{
                id: yellowlight
                width: background.size
                height: background.size
                color:"yellow"
            }

            TrafficLight{
                id: greenlight
                width: background.size
                height: background.size
                color:"green"
            }

            state: "red_on"

            states: [
                State {
                    name: "red_on"
                    PropertyChanges {
                        target: redlight
                        color:"red"
                        scale: 1.0
                    }
                    PropertyChanges {
                        target: greenlight
                        color: "black"
                    }
                    PropertyChanges {
                        target: yellowlight
                        color: "black"
                    }
                },

                State {
                    name: "red_yellow_on"
                    PropertyChanges {
                        target: redlight
                        color: "red"
                    }
                    PropertyChanges {
                        target: yellowlight
                        color: "yellow"
                    }
                    PropertyChanges {
                        target: greenlight
                        color: "black"
                    }
                },

                State {
                    name: "green_on"
                    PropertyChanges {
                        target: redlight
                        color: "black"
                    }
                    PropertyChanges {
                        target: yellowlight
                        color: "black"
                    }
                    PropertyChanges {
                        target: greenlight
                        color: "green"
                    }
                },

                State {
                    name: "yellow_on"
                    PropertyChanges {
                        target: redlight
                        color: "black"
                    }

                    PropertyChanges {
                        target: yellowlight
                        color: "yellow"
                    }

                    PropertyChanges {
                        target: greenlight
                        color: "black"
                    }
                }
            ]

            transitions: [
                Transition {
                    from: "*"
                    to: "*"

                    PropertyAnimation {
                        target: redlight
                        properties: "scale, color"
                        duration: 1000
                        easing.type: Easing.InQuad
                    }
                    PropertyAnimation {
                        target: greenlight
                        properties: "scale, color"
                        duration: 1000
                        easing.type: Easing.InQuad
                    }
                    PropertyAnimation {
                        target: yellowlight
                        properties: "scale, color"
                        duration: 1000
                        easing.type: Easing.InQuad
                    }
                }
            ]

            Timer {
                interval: 1000
                running: true
                repeat: true
                property int count: 0

                onTriggered: {
                    if (parent.state == "red_on" && count >= 5)
                    {
                        parent.state = "red_yellow_on"
                        count = 0
                    }
                    else if ( parent.state == "red_yellow_on" )
                    {
                        parent.state = "green_on"
                        count++
                    }
                    else if ( parent.state == "green_on" && count >= 5 )
                    {
                        parent.state = "yellow_on"
                        count ++
                    }
                    else if ( parent.state == "yellow_on" ) {
                        parent.state = "red_on"
                        count = 0
                    }
                    else {
                        count++
                    }
                }
            }
        }
    }
}

运行效果如下:



所有的源码可以在如下地方找到:

bzr branch lp:~liu-xiao-guo/debiantrial/trafficlightwith3lights

作者:UbuntuTouch 发表于2014-8-15 7:34:37 原文链接
阅读:19 评论:0 查看评论

Read more
UbuntuTouch

[原]在Ubuntu Touch上的C++及QML混合编程

在这篇文章中,我讲述如果使用QML来调用C++来扩展我们的应用。我们知道,在许多的场景中,QML虽然很强大,在UI方面是非常突出的,但是,有的时候,我们可能会想到应用我们的C++代码。这有一下方面的原因:

1)我们已经有成熟的C++引擎设计或协议等。比如我们已经用C++设计好了我们的Fetion协议代码。我们没要再用另外一个低性能的语言来重新写一边

2)有些功能我们没有办法使用Javascript来完成,必须使用系统或专用的一些功能包来完成。这时我们可以使用C++语言来完成我们需要的功能

3)有些代码对计算的要求非常高,需要使用大量的CPU时间,这时,我们可以考虑使用C++来完成这部分的工作


事实上,在Ubuntu Touch上,我们可以可以使用QML/Javascript来创建非常炫的UI,同时我们也可以使用C++来完成我们特有的一些功能(比如需要大量计算)。两者之间的结合是非常自然的和密切的。你可以想像C++是可以用来拓展QML的功能的。


在这里,我们来创建一个应用读取Linux的一些环境变量。通过这个练习,我们可以掌握怎么在QML文件中调用C++的代码。

1)创建一个基本的应用

  • 启动Ubuntu IDE,选定"App with QML extension Library"模版应用



  • 设定应用的设置


  • 选择所需要运行的架构。这里我们选择"Desktop"及在手机上能运行的“armhf"架构


至此,我们基本上已经产生了一个可以在手机和电脑上运行的应用。选择合适的架构,点击IDE左下角的运行键



如果你们在运行到这里,还有什么问题的话,请参照我先前的文章正确安装你的环境。

2)添加C++功能模块


首先我们找到项目的子目录“ReadEnv/backend/modules/ReadEnv”,在这里我们创建两个文件“readenv.cpp”及“readenv.h”。他们的内容如下:


#ifndef READ_ENV_H
#define READ_ENV_H

#include <QObject>

class ReadEnv : public QObject
{
    Q_OBJECT
public:
    explicit ReadEnv(QObject *parent = 0);
    Q_INVOKABLE QString getenv(const QString envVarName) const;

private:
};

#endif // READ_ENV_H

#include "readenv.h"
#include <QDebug>

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

QString ReadEnv::getenv(const QString envVarName) const
{
    QByteArray result = qgetenv(envVarName.toStdString().c_str());
    QString output = QString::fromLocal8Bit(result);
    qDebug() << envVarName << " value is: "  << output;
    return output;
}

这是一个非常简单的C++类。这个类必须是从QObject类继承,这样可以使得这个类可以使用Qt的signal-slot机制。如果大家对这个还不熟的话,可以参阅一些资料。对一个这样的类来说,如果它的函数或方法可以被QML调用的话,必须定义为“Q_INVOKABLE”。另外所有被定义为"slot"的函数或方法也可以被QML中的JS直接调用。我们可以通过更多的例子来说明这个问题。“getenv"方法很简单。它直接读取所需要的环境变量的值,并通过转换返回。

我们也同时打开该目录下的另外一个文件"backend.cpp"。经过修改的文件显示如下:

#include <QtQml>
#include <QtQml/QQmlContext>
#include "backend.h"
#include "mytype.h"
#include "readenv.h"


void BackendPlugin::registerTypes(const char *uri)
{
    Q_ASSERT(uri == QLatin1String("ReadEnv"));

    qmlRegisterType<MyType>(uri, 1, 0, "MyType");
    qmlRegisterType<ReadEnv>(uri, 1, 0, "ReadEnv");
}

void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
    QQmlExtensionPlugin::initializeEngine(engine, uri);
}

这里其实也没有做什么。加入了一句

 qmlRegisterType<ReadEnv>(uri, 1, 0, "ReadEnv");

这里的第一个“ReadEnv"对应于我们所定义的类的名称,而不是项目的名称。第二个“ReadEnv"究其实是在QML使用的来实例化一个这样的类。它的名称可以和实际的类不同名字,也可以是任何一个你喜欢的名称。不过你必须也要在QML文件中做相应的修改。通过这样的注册,该类就可以在QML中进行实例化,并引用!等我们已经把源码已经做好,我们必须把它加入到我们的项目中进行编译。我们打开位于"backend"目录下的”CMakeLists.txt"文件中,加入如下的语句:

set(
<pre style="margin-top: 0px; margin-bottom: 0px;"><!--StartFragment-->    ReadEnvbackend_SRCS<!--EndFragment-->

<span style="font-family: Arial, Helvetica, sans-serif; color: rgb(192, 192, 192);">        </span><span style="font-family: Arial, Helvetica, sans-serif;">modules/ReadEnv/backend.cpp</span>
    modules/ReadEnv/mytype.cpp
    modules/ReadEnv/readenv.cpp
)

我们可以通过关掉项目再重新打开应用的方式,使得项目中的文件目录得到更新。也可以使用点击右键的方式使新加入的文件得到正确的显示:
这样,我们的C++部分的代码基本上就已经完成了。

3)修改QML部分的代码

上面我们已经完成了基本的C++部分的代码,我们在这里继续来完成QML部分的界面。首先我们来熟悉一下QML的layout。我们知道layout可以很方便地帮我们管理我们的控件,并在适当的时候自动适配合适的屏幕尺寸及手机方向的改变。QML中有Column及Row来帮我们管理我们的控件:
        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10

            Label {
                text: "Value:  "
                fontSize: "large"
            }

            TextArea {
                id:value
                readOnly: true
                contentWidth: units.gu(40)
                contentHeight: units.gu(80)
            }

        }


这里我们并排放上两个控件,一个是Label,另外一个是"TextArea"。通过这样的组合,这整个又可以看做是一个复合的控件,里面有两个控件。它整个又可以被放到它外面的布局管理器(Row, Grid, Flow, Column等)中。通过我的修改,我们创建了如下的界面及界面。




我们通过修改“ReadEnv.qml” 来实现界面。具体的代码如下:

import QtQuick 2.0
import Ubuntu.Components 0.1
import ReadEnv 1.0
import "ui"

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

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

    anchorToKeyboard: true

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

    width: units.gu(100)
    height: units.gu(75)

    ReadEnv {
        id: readEnv
    }

    Column {
        // anchors.fill: parent
        //        anchors.verticalCenter: parent.verticalCenter
        //        anchors.horizontalCenter: parent.horizontalCenter
        anchors.centerIn: parent
        spacing: 20

        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10
            Label {
                text: "Variable:"
                fontSize: "large"
            }

            TextField {
                id:input
                text:"PWD"
                focus: true

                placeholderText: "Please input a env variable"

                Keys.onPressed: {
                    if (event.key === Qt.Key_Return) {
                        print("Enter is pressed!")
                        value.text = readEnv.getenv(input.text)
                    }
                }
            }
        }

        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10

            Label {
                text: "Value:  "
                fontSize: "large"
            }

            TextArea {
                id:value
                readOnly: true
                contentWidth: units.gu(40)
                contentHeight: units.gu(80)
            }

        }

        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Get"
            onClicked: {
                print("Button is clicked!")
                value.text = readEnv.getenv(input.text)
            }
        }
    }
}



特别注意的是:
  • 在调用backend的库时,我们必须使用如下的动作来完成。
	import ReadEnv 1.0
这里的“ReadEnv",实际上对应于在“backend.cpp"中的如下语句中的“ReadEnv"。如果该处发生变化,import语句也得变化

    Q_ASSERT(uri == QLatin1String("ReadEnv"));
我们也可以在手机的安装环境中可以看出。"ReadEnv"同时也是在“lib"下的一个目录名称。其实这个理解是非常重要的。所有的import中的文字其实就是表示的是路经。它标示了库所在的位置




  • 我们通过如下的语句来对"ReadEnv"类进行实例化。我们在实例化的同时,也给了一个ID,这样就可以在其他的地方引用了。在本应用中,我们在Button中调用
      ReadEnv {
          id: readEnv
      }

        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Get"
            onClicked: {
                print("Button is clicked!")
                value.text = readEnv.getenv(input.text)
            }
        }


这个应用的运行效果为:


好了今天我们已经完成了一个练习。它实现了从QML中调用C++代码。我们以后还可以添加更多的功能到这个应用中。本例程的代码可以在如下网站找到:

bzr branch lp:~liu-xiao-guo/debiantrial/readenv

作者:UbuntuTouch 发表于2014-8-14 13:28:23 原文链接
阅读:13 评论:0 查看评论

Read more
UbuntuTouch

信号及槽(signal-slot)是Qt语言最基本的,也是最有用的一个机制。这是它区分和其他一起语言的一个显据的标志。


我们先来下载我已经写好的应用:


bzr branch lp:~liu-xiao-guo/debiantrial/trafficlightwith3lights


使用Ubuntu SDK来打开我们已经创建好的应用。然后再打开文件“MyLight.qml”。在文件的开始部分加入如下的语句:


Item {
    <strong>id: root</strong>
    width: units.gu(100)
    height: units.gu(75)

    signal redLightOn
    signal greenLightOn
    signal yellowLightOn

    Rectangle {
        id: background
        anchors.fill: parent
        color: "black"
        property int size: width*0.7

我们可以看到我们已经定义了三个信号。它们分别是"redLightOn", "greenLightOn"及“yellowLightOn”。我们定义这三个信号的目的是为了当红色,黄色及绿色的灯亮的时候,我们用这些信号来发送一个通知。这样只要有兴趣的代码可以对它进行截获,并处理。这里必须注意的是信号的第一个字母为小写


接下来,我们在我们的JS代码中来发送这些信号。代码如下:


            Timer {
                interval: 1000
                running: true
                repeat: true
                property int count: 0

                onTriggered: {
                    if (parent.state == "red_on" && count >= 5)
                    {
                        parent.state = "red_yellow_on"
                       <strong> root.redLightOn();
                        root.yellowLightOn();</strong>
                        count = 0
                    }
                    else if ( parent.state == "red_yellow_on" )
                    {
                        parent.state = "green_on"
                        <strong>root.greenLightOn();</strong>
                        count++
                    }
                    else if ( parent.state == "green_on" && count >= 5 )
                    {
                        parent.state = "yellow_on"
                       <strong> root.yellowLightOn();</strong>
                        count ++
                    }
                    else if ( parent.state == "yellow_on" ) {
                        parent.state = "red_on"
                        redLightOn();
                        count = 0
                    }
                    else {
                        count++
                    }
                }
            }


发送信号其实非常简单。直接发送,就像调用一个方法一样。


为了在我们的代码部分截取这个应用,我们可以使用如下的方法。在“TrafficLight.qml”中,修改代码为:

import QtQuick 2.0
import Ubuntu.Components 0.1
import "ui"

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

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

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

    width: units.gu(120)
    height: units.gu(80)

    Page {
        id:main
        anchors.fill: parent

        Row {
            id: myrow
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            spacing: units.gu(5)

            MyLight {
                id:light
                width: main.width/5
                height: width*3

                onRedLightOn: {
                    console.log("red light is on")
                }
            }

           Connections {
               target: light
               onYellowLightOn: {
                   console.log("yellow light is on")
               }
           }
        }

        function greenLightOn() {
                 console.log("green light is on")
         }

        Component.onCompleted: {
            light.greenLightOn.connect(greenLightOn);
        }

    }
}

为了说明清楚,我写了三种方法。一种是直接把信号的第一个字母变为大写, 并同时在前面加上"on“。第二种方法使用”Connections"来实现槽的连接。第三种方法,我们可以直接 把信号连接到一个JS的函数上。运行程序,我们可以在应用的输出窗口看到如下的输出:

green light is on
yellow light is on
red light is on
red light is on
yellow light is on
green light is on
yellow light is on
red light is on
red light is on
yellow light is on


事实上所有的控件的property都可以发出一个信号。让我们来看一下我们先前完成的“color” property。


void TrafficLight::setColor(const QColor &color)
{
    if ( color == m_color )
        return;
    else {
        m_color = color;
        update();   // repaint with the new color
        emit colorChanged();
    }
}

从这里可以看出,每当property的值发生改变时,就会发生一个叫做“colorChanged”的信号。我们可以在我们的QML中截获这个信号。比如在我们的代码中,我们可以使用如下的代码:


            TrafficLight{
                id: redlight
                width: background.size
                height: background.size
                color:"red"

                onColorChanged: {
                    console.log("Color is changed to " + color);
                }
            }

当我们运行时,我们可以看到好多的颜色变化的事件。这是由于颜色在transition时发生很多的颜色的变化。同样我们也可以对任何一个property进行事件的捕获。比如:


            TrafficLight{
                id: redlight
                width: background.size
                height: background.size
                color:"red"

                onColorChanged: {
                    console.log("Color is changed to " + color);
                }

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

这段代码可以对"width"的变化进行捕获!


更多阅读可以在连接找到http://qt-project.org/doc/qt-4.8/qmlevents.html



作者:UbuntuTouch 发表于2014-8-15 14:37:41 原文链接
阅读:9 评论:0 查看评论

Read more
UbuntuTouch

我们知道在Ubuntu Touch上面,我们可以创建Qt/QML的应用,同时,我们也可以使用Ubuntu SDK来创建一些跨平台的HTML 5的应用。这些应该虽然是在Ubuntu Touch的SDK上面创建的,但也是可以修改在其它的平台上运行。下面,我们来简单介绍一些怎么在SDK中创建并部署.


在Ubuntu Touch上面,我们可以使用HTML 5的一些tag来些,我们的应用,同时我们也可以使用Cordova API接口来扩展我们的功能。更多的阅读,可以阅读我们的官方网站


你可以在Ubuntu的开发者网站找到更多的关于这些API的介绍:http://developer.ubuntu.com/api/html5/development/

1) 创建一个基本的框架


首先我们打开我们的SDK,可以看到如下的界面:




接下来我们可以创建一个称作“WebSysInfo"的应用。这个应用的目的是为了显示一些系统的一些信息。



紧接着,按照SDK的提示完成剩下的步骤来创建一个最基本的框架应用。由于HTML 5的应用是跨平台的,我们可以直接在电脑上运行这个最基本的应用,也可以直接部署到手机上。如果你在这个步骤不能正确地运行你的应用,请查看我的SDK安装指南
如果你运行程序正常,你可以看到如下的画面:





至此,我们已经完成了一个最基本的应用。下面我们来学习如何添加Cordova API 到我们的应用中来扩展我们的一些功能。

2)添加Cordova API到应用中

我们知道Cordova API有很多有用的功能。可以是我们的应用更加丰富。下面我们来学习怎么把Cordova API加到我们的应用中去。首先,我们选中我们的项目,同时选择菜单“ Tool”==>"Ubuntu" ==>"Add Cordova runtime to HTML 5 project"。



这时应用开始下载最新的Cordova API的包,并安装到我们所在的应用中。目前由于一些原因,需要关闭项目(最终的版本应该解决这个问题),再重新打开项目来查看在项目栏更新后的目录结构。




为了使用这些API,我们可以对"index.html"的代码做如下的修改。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>An Ubuntu HTML5 application</title>
    <meta name="description" content="An Ubuntu HTML5 application">
    <!-- due to a bug in the SDK, the following line will be used in the final SDK
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> -->
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- 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/tab.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 Cordova Javascript APIs -->
    <!-- NOTE:
         Make sure that you have imported the Cordova native runtime and JS libraries into your project first.
	 In order to do that, in QtCreator, select the "Add Cordova runtime to HTML5 project" menu item
         in Tools > Ubuntu.
      -->
    <script src="cordova/cordova.js"></script>

    <!-- Application script -->
    <script src="js/app.js"></script>
  </head>

  <body>

    <div data-role="mainview">

      <header data-role="header">
        <ul data-role="tabs">
          <li data-role="tabitem" data-page="mainpage">System Info</li>
        </ul>
      </header>

      <div data-role="content">

          <!-- The application main page -->

          <div data-role="tab" id="mainpage">

            <section data-role="list" id="info">
                <header class="large-font">System Info</header>
            </section>

          </div>

      </div>
      
    </div>
  </body>
</html>

这里我们定义了一个“System Info”的tab。它对应着"mainpage"。当它被点击的时候,对应的"mainpage"就会被打开。在“mainpage"中我们定义了一个section, 在section中,定义了一个list。我们为list也定义了一个叫做"System Info”的"header"。当然我么也可以定义更多的"header"。值得注意的是我们通过一下语句

    <script src="cordova/cordova.js"></script>

来加载Cordova API以使我们能够正常使用们。同时,

    <!-- Application script -->
    <script src="js/app.js"></script>

我们可以使用"app.js"来对我们的HTML进行操作。


3)怎么调试应用

当我们运行应用的时候,我们可以在"Application output"窗口看到如下的信息:

unity::action::ActionManager::ActionManager(QObject*):
	Could not determine application identifier. HUD will not work properly.
	Provide your application identifier in $APP_ID environment variable.
Using "/home/liuxg/cases/WebSysInfo/www" as working dir 
"file:///home/liuxg/cases/WebSysInfo/www/index.html" 
Using "/home/liuxg/cases/WebSysInfo/www" as working dir 
"file:///home/liuxg/cases/WebSysInfo/www/index.html" 
Inspector server started successfully. Try pointing a WebKit browser to http://192.168.199.228:9221

显然它告诉我们我们可以在Chrome浏览器中打开http://192.168.199.228:9221地址查看我们所需要的信息。我们不妨在“app.js"文件里输入如下的句子

window.onload = function () {
    var UI = new UbuntuUI();
    UI.init();

    console.log("we are so glad to see it works!")

    // Add an event listener that is pending on the initialization
    //  of the platform layer API, if it is being used.
    document.addEventListener("deviceready", function() {
        if (console && console.log)
            console.log('Platform layer API ready');
    }, false);
};


运行程序,同时在Chrome中打开http://192.168.199.228:9221。可以看到如下的画面:



我们可以在“Console”里看到我们需要的输出。必须注意的是,如果一个应用有两个同时运行的实例,那么最新的实例将不会有任何的输出。在Console里只能看到第一个实例的输出。当你运行第二个实例的时候,你可以在Application Output 窗口中看到如下的输出:

Couldn't start the inspector server on bind address "192.168.199.228" and port "9221". In case of invalid input, try something like: "12345" or "192.168.2.14:12345"

另外应用必须先启动,然后再看地址http://192.168.199.228:9221。否则你将进不去。

4) 加入JS代码实现Cordova API的调用

现在我们来修改项目中“app.js”文件来对UI进行改变。首先我们在"index.html"中加入如下的句子:

    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/list.js"></script>

这样,我们就可以在"app.js"中使用如下的句子来得到我们的list了:

    var info = UI.list('#info');

有了变量info,我们就可以向其中添加我们所需要的东西了。

处理window.onload事件

对于Web开发者来讲,window.onload事件并不陌生。当DOM被完全载入时,这个事件会被发生。这个事件对于处理一些需要在DOM完全载入紧接着执行一些代码。

window.onload = function () {
....
}

处理deviceready事件

对于使用Cordova API的应用来说,我们需要使用deviceready事件来完成对Cordova API的使用。这个事件是用来告诉Cordova runtime已经准备好了可以使用了。一般来说,它的使用是这样的

/**
 * Wait before the DOM has been loaded before initializing the Ubuntu UI layer
 */
window.onload = function () {
    var UI = new UbuntuUI();
    UI.init();

    console.log("we are so glad to see it works!")

    var info = UI.list('#info');


    // Add an event listener that is pending on the initialization
    //  of the platform layer API, if it is being used.
    document.addEventListener("deviceready", function() {
        if (console && console.log)
            console.log('Platform layer API ready');

        info.append("Cordova version: " + device.cordova, null, null, null);
        info.append("Device model: " + device.model, null, null, null);
        info.append("Version: " + device.version, null, null, null);
        info.append("Platform: " + device.platform, null, null, null);
        info.append("UUID: " + device.uuid, null, null, null);
        info.append("Available: " + device.available, null, null, null);
        info.append("Screen width: " + screen.width);
        info.append("Screen height: " + screen.height);
        info.append("Colordepth: " + screen.colorDepth);
        info.append("UserAgent: " + navigator.userAgent);
    }, false);
};

这样我们的一个简单的应用就已经做好了。在Nexus 4上面我们可以看到如下的画面



至此,我们一个最基本的HTML 5的应用已经做好了。源码可以在如下的地址找到。

bzr branch lp:~liu-xiao-guo/debiantrial/websysinfo

作者:UbuntuTouch 发表于2014-8-19 16:05:13 原文链接
阅读:11 评论:0 查看评论

Read more
UbuntuTouch

前面我们已经学习了如何在Ubuntu Touch上面制作一个Scope应用。Scope也是Ubuntu上面一个非常重要的,又和其他平台区分的一种应用。它能很好地把web services整合到手机平台中,就像是系统的一部分。


值得指出的是:由于一些原因,目前所有的Scope的开发必须是在Ubuntu OS Utopic (14.10)版本之上的。在Ubuntu OS 14.04上是不可以的。

1)创建一个最基本的Scope应用


首先打开我们的Ubuntu SDK。选择“Unity Scope"模版。



然后选择好项目的路径,并同时选好自己的项目名称"dianping"。



接下来,我们就完成剩下的步骤来完成一个最基本的Scope应用。我们可以直接在电脑上运行。当然我们也可以把它运行到手机中。




如果你能运行到这里,说明你的安装环境是没有问题的。如果有问题的话,请参阅我的Ubuntu SDK安装文章。这个最基本的应用其实没有什么内容。在下面的章节中我们来向这里添加一些东西以实现我们所需要的一些东西。

2)代码讲解

src/dianping-scope.cpp

这个文件定义了一个unity::scopes::ScopeBase的类。它提供了客户端用来和Scope交互的起始接口。

  • 这个类定义了“start", "stop"及"run"来运行scope。绝大多数开发者并不需要修改这个类的大部分实现。在我们的例程中,由于需要,我们将做简单的修改
  • 它也同时实现了另外的两个方法:search 和 preview。我们一般来说不需要修改这俩个方法的实现。但是他们所调用的函数在具体的文件中必须实现

注意:我们可以通过研究Scope API的头文件来对API有更多的认识。更多的详细描述,开发者可以在http://developer.ubuntu.com/api/scopes/sdk-14.10/查看。

src/dianping-query.cpp

这个文件定义了一个unity::scopes::SearchQueryBase类。

这个类用来产生由用户提供的查询字符串而生产的查询结果。这个结果可能是基于json或是xml的。这个类可以用来进行对返回的结果处理并显示。

  • 得到由用户输入的查询字符串
  • 向web services发送请求
  • 生成搜索的结果(根据每个应用不能而不同)
  • 创建搜索结果category(比如不同的layout-- grid/carousel)
  • 根据不同的搜寻结果来绑定不同的category以显示我们所需要的UI
  • 推送不同的category来显示给最终用户

基本上所有的代码集中在"run"方法中。这里我们加入了一个”QCoreApplication”变量。这主要是为了我们能够使用signal/slot机制。

#ifndef DEMOSCOPE_H
#define DEMOSCOPE_H

#include <unity/scopes/ScopeBase.h>
#include <unity/scopes/QueryBase.h>
#include <unity/scopes/ReplyProxyFwd.h>
#include <unity/scopes/QueryBase.h>
#include <unity/scopes/PreviewQueryBase.h>

#include <QCoreApplication>

class DianpingScope : public unity::scopes::ScopeBase
{
public:
    virtual void start(std::string const&) override;

    virtual void stop() override;

    unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result& result,
                                                  unity::scopes::ActionMetadata const& metadata) override;
    virtual unity::scopes::SearchQueryBase::UPtr search(unity::scopes::CannedQuery const& q,
                                                        unity::scopes::SearchMetadata const& metadata) override;
    void run() override;

private:
   QCoreApplication *app;
};

#endif

#include "dianping-scope.h"
#include "dianping-query.h"
#include "dianping-preview.h"
#include <unity-scopes.h>

using namespace unity::scopes;

void DianpingScope::start(std::string const&)
{
}

void DianpingScope::stop()
{
    delete app;
}

void DianpingScope::run()
{
    // an instance of QApplication is needed to make Qt happy
    int zero = 0;
    app = new QCoreApplication(zero, nullptr);
}

SearchQueryBase::UPtr DianpingScope::search(CannedQuery const &q, SearchMetadata const& metadata)
{
    SearchQueryBase::UPtr query(new DianpingQuery(q, metadata));
    return query;
}


PreviewQueryBase::UPtr DianpingScope::preview(Result const& result, ActionMetadata const& metadata) {
    PreviewQueryBase::UPtr preview(new DianpingPreview(result, metadata));
    return preview;
}

#define EXPORT __attribute__ ((visibility ("default")))

extern "C"
{

EXPORT
unity::scopes::ScopeBase*
// cppcheck-suppress unusedFunction
UNITY_SCOPE_CREATE_FUNCTION()
{
    return new DianpingScope();
}

EXPORT
void
// cppcheck-suppress unusedFunction
UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
{
    delete scope_base;
}

}

如果这时我们来试图来编译应用,可能会出现如下的错误。问题在于我们并没有加载相应的Qt库。

In file included from /home/liuxg/Examples/dianping/src/dianping-scope.cpp:1:0:
/home/liuxg/Examples/dianping/src/dianping-scope.h:10:28: fatal error: QCoreApplication: No such file or directory
 #include <QCoreApplication>

由于这个是一个需要网路连接的应用,我们可以把如下的Qt5Network加入到我们的CMake (src/CMakeLists.txt)文件中:


add_definitions(-DQT_NO_KEYWORDS)
find_package(Qt5Network REQUIRED)


add_library(
  ${SCOPE_LIB_TARGET_NAME} SHARED
  dianping-preview.cpp
  dianping-query.cpp
  dianping-scope.cpp
)

qt5_use_modules(${SCOPE_LIB_TARGET_NAME} Network)


target_link_libraries(${SCOPE_LIB_TARGET_NAME} ${UNITY_SCOPES_LDFLAGS})
set_property(TARGET ${SCOPE_LIB_TARGET_NAME} PROPERTY COMPILE_FLAGS ${UNITY_SCOPES_CFLAGS})

install(TARGETS ${SCOPE_LIB_TARGET_NAME}
LIBRARY DESTINATION "${SCOPE_INSTALLDIR}"
)

这里我们必须注意的是也要同时加上add_definitions(-DQT_NO_KEYWORDS)。否则我们接下来当我们使用signal/slot时会出现一些错误。我们再编译我们的应用。确保这个时候没有任何的编译错误。在CMakeLists.txt中加入这些行后,可以在项目中点击右键再重运行“Run CMake"以确保新的变化已经起作用。


#include "dianping-query.h"
#include <unity/scopes/Annotation.h>
#include <unity/scopes/CategorisedResult.h>
#include <unity/scopes/CategoryRenderer.h>
#include <unity/scopes/QueryBase.h>
#include <unity/scopes/SearchReply.h>

#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QCoreApplication>


using namespace unity::scopes;

//Create a JSON string to be used tro create a category renderer - uses grid layout
std::string CR_GRID = R"(
    {
        "schema-version" : 1,
        "template" : {
            "category-layout" : "grid",
            "card-size": "small"
        },
        "components" : {
            "title" : "title",
            "art" : {
                "field": "art",
                "aspect-ratio": 1.6,
                "fill-mode": "fit"
            }
        }
    }
)";

//Create a JSON string to be used tro create a category renderer - uses carousel layout
std::string CR_CAROUSEL = R"(
    {
        "schema-version" : 1,
        "template" : {
            "category-layout" : "carousel",
            "card-size": "small"
        },
        "components" : {
            "title" : "title",
            "art" : {
                "field": "art",
                "aspect-ratio": 1.6,
                "fill-mode": "fit"
            }
        }
    }
)";


DianpingQuery::DianpingQuery(CannedQuery const& query, SearchMetadata const& metadata) :
    SearchQueryBase(query, metadata)
{
}

DianpingQuery::~DianpingQuery()
{
}

void DianpingQuery::cancelled()
{
}

const QString appkey = "3562917596";
const QString secret = "xxxxxxxxxxxx";  // PLEASE supply your secret here
const QString BASE_URI = "http://api.dianping.com/v1/business/find_businesses?";

QString DianpingQuery::getQueryString(QString query) {
    QMap<QString, QString> map;

    map["category"] = "美食";
    map["city"] = query;
    map["sort"] = "2";
    map["limit"] = "20";
    map["platform"] = "2";

    QCryptographicHash generator(QCryptographicHash::Sha1);

    QString temp;
    temp.append(appkey);
    QMapIterator<QString, QString> i(map);
    while (i.hasNext()) {
        i.next();
        qDebug() << i.key() << ": " << i.value();
        temp.append(i.key()).append(i.value());
    }

    temp.append(secret);

    qDebug() << temp;

    qDebug() << "UTF-8: " << temp.toUtf8();

    generator.addData(temp.toUtf8());
    QString sign = generator.result().toHex().toUpper();

//    qDebug() << sign;

    QString url;
    url.append(BASE_URI);
    url.append("appkey=");
    url.append(appkey);

    url.append("&");
    url.append("sign=");
    url.append(sign);

    i.toFront();
    while (i.hasNext()) {
        i.next();
        qDebug() << i.key() << ": " << i.value();
        url.append("&").append(i.key()).append("=").append(i.value());
    }

    qDebug() << "Final url: " << url;
    return url;
}

void DianpingQuery::run(unity::scopes::SearchReplyProxy const& reply)
{
    // Firstly, we get the query string here.
    CannedQuery query = SearchQueryBase::query();
    QString queryString = QString::fromStdString(query.query_string());

    QString uri;
    if ( queryString.isEmpty() ) {
        queryString = QString("北京");
        uri = getQueryString(queryString);
    } else  {
        uri = getQueryString(queryString);
    }

    qDebug() << "queryString: " << queryString;

    CategoryRenderer rdrGrid(CR_GRID);
    CategoryRenderer rdrCarousel(CR_CAROUSEL);

    QString title = queryString + "美味";

    auto topCar = reply->register_category("dianpingcarousel", title.toStdString(), "", rdrCarousel);
    auto topGrid = reply->register_category("dianpinggrid", "", "", rdrGrid);

    QEventLoop loop;

    QNetworkAccessManager manager;

    QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));

    QObject::connect(&manager, &QNetworkAccessManager::finished,
            [reply, topCar, topGrid, this](QNetworkReply *msg){
                QByteArray data = msg->readAll();
                QString json = data;
                // qDebug() << "Data:" << json;
                QJsonParseError err;
                QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err);

                if (err.error != QJsonParseError::NoError) {
                    qCritical() << "Failed to parse server data: " << err.errorString();
                } else {
                    // Find the "payload" array of results
                    QJsonObject obj = doc.object();
                    QJsonArray results = obj["businesses"].toArray();

                    // for each result
                    const std::shared_ptr<const Category> * top;

                    bool grid = false;
                    //loop through results of our web query with each result called 'result'
                    for(const auto &result : results) {
                        if ( grid ) {
                          top = &topGrid;
                          grid = false;
                        } else {
                          grid = true;
                          top = &topCar;
                        }
                        //create our categorized result using our pointer, which is either to out
                        //grid or our carousel Category
                        CategorisedResult catres((*top));

                        //treat result as Q JSON
                        QJsonObject resJ = result.toObject();

                        // load up vars with from result
                        auto name = resJ["name"].toString();
                        auto business_uri = resJ["business_url"].toString();
                        qDebug() << "business_uri: " << business_uri;

                        auto s_photo_uri = resJ["s_photo_url"].toString();
                        auto photo_uri = resJ["photo_url"].toString();
                        auto rating_s_img_uri = resJ["rating_s_img_url"].toString();

                        //set our CateogroisedResult object with out searchresults values
                        catres.set_uri(business_uri.toStdString());
                        catres.set_dnd_uri(business_uri.toStdString());
                        catres.set_title(name.toStdString());
                        catres.set_art(photo_uri.toStdString());

                        //push the categorized result to the client
                        if (!reply->push(catres)) {
                            break; // false from push() means search waas cancelled
                        }
                    }
                }
            }
            );

    qDebug() << "Uri:" << uri ;
    manager.get(QNetworkRequest(QUrl(uri)));
    loop.exec();
}

我们可以参阅http://developer.dianping.com/来注册成为dianping网站的开发者。在网址http://developer.dianping.com/app/tutorial可以找到开发指南。可以通过getQueryString()方法来得到所需要请求的uri。

创建并注册CategoryRenderers

在本例中,我们创建了两个JSON objects. 它们是最原始的字符串,如下所示,它有两个field:template及components。template是用来定义是用什么layout来显示我们所搜索到的结果。这里我们选择的是”grid"及小的card-size。components项可以用来让我们选择预先定义好的field来显示我们所需要的结果。这里我们添加了"title"及“art"。


std::string CR_GRID = R"(
    {
        "schema-version" : 1,
        "template" : {
            "category-layout" : "grid",
            "card-size": "small"
        },
        "components" : {
            "title" : "title",
            "art" : {
                "field": "art",
                "aspect-ratio": 1.6,
                "fill-mode": "fit"
            }
        }
    }
)";

更多关于 CategoryRenderer 类的介绍可以在 docs找到。

我们为每个JSON Object创建了一个CategoryRenderer,并同时向reply object注册:

    CategoryRenderer rdrGrid(CR_GRID);
    CategoryRenderer rdrCarousel(CR_CAROUSEL);

    QString title = queryString + "美味";

    auto topCar = reply->register_category("dianpingcarousel", title.toStdString(), "", rdrCarousel);
    auto topGrid = reply->register_category("dianpinggrid", "", "", rdrGrid);


我们可以运行我们所得到的程序,看看我们的结果。



src/dianping-preview.cpp

这个文件定义了一个unity::scopes::PreviewQueryBase类。

这个类定义了一个widget及一个layout来展示我们搜索到的结果。这是一个preview结i果,就像它的名字所描述的那样。

  • 定义在preview时所需要的widget
  • 让widget和搜索到的数据field一一对应起来
  • 定义不同数量的layout列(由屏幕的尺寸来定)
  • 把不同的widget分配到layout中的不同列中
  • 把reply实例显示到layout的widget中

大多数的代码在“run&quot;中实现。跟多关于这个类的介绍可以在http://developer.ubuntu.com/api/scopes/sdk-14.10/previewwidgets/找到。

Preview

Preview需要来生成widget并连接它们的field到CategorisedResult所定义的数据项中。它同时也用来为不同的显示环境(比如屏幕尺寸)生成不同的layout。根据不同的显示环境来生成不同数量的column。

Preview Widgets

这是一组预先定义好的widgets。每个都有一个类型。更据这个类型我们可以生成它们。你可以在这里找到Preview Widget列表及它们提供的的field类型。

这个例子使用了如下的widgets

  • header:它有title及subtitle field
  • image:它有source field有来显示从哪里得到这个art
  • text:它有text field
  • action:用来展示一个有"Open"的按钮。当用户点击时,所包含的URI将被打开

如下是一个例子,它定义了一个叫做“headerId"的PreviewWidget。第二个参数是它的类型"header"。

 PreviewWidget w_header("headerId", "header");

最终的程序如下:


#include"dianping-preview.h"

#include <QString>
#include <QDebug>
#include<unity/scopes/PreviewWidget.h>
#include<unity/scopes/ColumnLayout.h>
#include<unity/scopes/PreviewReply.h>
#include <unity/scopes/VariantBuilder.h>

using namespace unity::scopes;

DianpingPreview::DianpingPreview(Result const& result, ActionMetadata const& metadata) :
    PreviewQueryBase(result, metadata)
{
}

DianpingPreview::~DianpingPreview()
{
}

void DianpingPreview::cancelled()
{
}

void DianpingPreview::run(unity::scopes::PreviewReplyProxy const& reply)
{
    // Client can display Previews differently depending on the context
    // By creates two layouts (one with one column, one with two) and then
    // adding widgets to them differently, Unity can pick the layout the
    // scope developer thinks is best for the mode
    ColumnLayout layout1col(1), layout2col(2);

    // add columns and widgets (by id) to layouts.
    // The single column layout gets one column and all widets
    layout1col.add_column({"headerId", "artId", "infoId", "actionsId"});
    // The two column layout gets two columns.
    // The first column gets the art and header widgets (by id)
    layout2col.add_column({"artId", "headerId"});
    // The second column gets the info and actions widgets
    layout2col.add_column({"infoId", "actionsId"});

    // Push the layouts into the PreviewReplyProxy intance, thus making them
    // available for use in Preview diplay
    reply->register_layout({layout1col, layout2col});

    //Create some widgets
    // header type first. note 'headerId' used in layouts
    // second field ('header) is a standard preview widget type
    PreviewWidget w_header("headerId", "header");
    // This maps the title field of the header widget (first param)  to the
    // title field in the result to be displayed in this preview, thus providing
    // the result-specific data to the preview for display
    w_header.add_attribute_mapping("title", "title");
    // Standard subtitle field here gets our 'artist' key value
    w_header.add_attribute_mapping("subtitle", "artist");

    PreviewWidget w_art("artId", "image");
    w_art.add_attribute_mapping("source", "art");

    PreviewWidget w_info("infoId", "text");
    w_info.add_attribute_mapping("text", "description");

    Result result = PreviewQueryBase::result();
    QString urlString(result["uri"].get_string().c_str());
    qDebug() << "[Details] GET " << urlString;
   // QUrl url = QUrl(urlString);

    // Create an Open button and provide the URI to open for this preview result
    PreviewWidget w_actions("actionsId", "actions");
    VariantBuilder builder;
    builder.add_tuple({
            {"id", Variant("open")},
            {"label", Variant("Open")},
            {"uri", Variant(urlString.toStdString())} // uri set, this action will be handled by the Dash
        });
    w_actions.add_attribute_value("actions", builder.end());

    // Bundle out widgets as required into a PreviewWidgetList
    PreviewWidgetList widgets({w_header, w_art, w_info, w_actions});
    // And push them to the PreviewReplyProxy as needed for use in the preview
    reply->push(widgets);
}

运行的效果图如下:


   


在手机上的运行情况如下:

       


整个完整的代码在如下的网址可以看到:

bzr branch lp:~liu-xiao-guo/debiantrial/dianpingtraining


作者:UbuntuTouch 发表于2014-8-20 16:35:02 原文链接
阅读:22 评论:0 查看评论

Read more
UbuntuTouch

[原]在Ubuntu上的传感器

我们知道传感器在现代手机中非常重要,我们需要使用它做一些有创新的应用。这里我们来显示怎么在Ubuntu上使用它所提供的传感器。


1)显示所有的传感器

在这里我们来做一个例子来显示所有传感器的列表。我们知道QML提供了一个便利的方法可以很方便地列车所有已有的传感器。

        Component.onCompleted: {
            var types = QmlSensors.sensorTypes();
            console.log(types.join(", "));
        }

为了很方便地显示,我们用一个列表来显示所有被支持的sensor。

            ListView {
                width: parent.width
                height: parent.height/2

                delegate: Text {
                    text: modelData
                }

                model:QmlSensors.sensorTypes()
            }

这里是个非常简单的列表,直接使用QmlSensors的方法sensorTypes()来作为一个model数据。我们以使用一个最简单的delegate来显示数据。

2)使用传感器数据

Accelerometer

我们可以使用如下的方法来得到传感器的数据:

        Accelerometer {
            id: accel
            active: true
            dataRate: 20

            onReadingChanged: {
                accelLabel.text = "Accel " + "x: " + reading.x.toFixed(1) +" y: " + reading.y.toFixed(1) + " z: " + reading.z.toFixed(1)
            }
        }

TiltSensor

我们可以使用如下的方法来得到传感器的数据:

        TiltSensor {
            id: tilt
            active: false

            onReadingChanged: {
                tiltLabel.text = "Tilt " + "x " + tilt.reading.xRotation.toFixed(1) + " y " + tilt.reading.yRotation.toFixed(1);
            }
        }

AmbientLigthSensor

我们可以使用如下的方法来得到传感器的数据:

        AmbientLightSensor {
            active: true
            onReadingChanged: {
                if (reading.lightLevel === AmbientLightReading.Dark) {
                    lightLabel.text = "It is dark"
                }  else if ( reading.lightLevel === AmbientLightReading.Twilight) {
                    lightLabel.text = "It is moderately dark";
                } else if ( reading.lightLevel === AmbientLightReading.Light) {
                    lightLabel.text = "It is light (eg. internal lights)";
                } else if ( reading.lightLevel === AmbientLightReading.Bright) {
                    lightLabel.text = "It is bright (eg. shade)";
                } else if ( reading.lightLevel === AmbientLightReading.Sunny) {
                    lightLabel.text = "It is very bright (eg. direct sunlight)";
                }else if ( reading.lightLevel === AmbientLightReading.Undefined) {
                    lightLabel.text = "It is unknown";
                }
            }
        }

OrientationSensor

我们可以使用如下的方法来得到传感器的数据:

        OrientationSensor {
            active: true
            onReadingChanged: {
                orientationLabel.text = "something happened"
                if ( reading.orientation === OrientationReading.TopUp) {
                    orientationLabel.text = "TopUp";
                } else if ( reading.orientation === OrientationReading.TopDown) {
                    orientationLabel.text = "TopDown";
                } else if ( reading.orientation === OrientationReading.LeftUp) {
                    orientationLabel.text = "LeftUp";
                } else if ( reading.orientation === OrientationReading.RightUp) {
                    orientationLabel.text= "RightUp";
                } else if ( reading.orientation === OrientationReading.FaceDown) {
                    orientationLabel.text = "FaceDown";
                }  else if ( reading.orientation === OrientationReading.FaceUp) {
                    orientationLabel.text = "FaceUp";
                }
            }
        }

RotationSensor

我们可以使用如下的方法来得到传感器的数据:

        RotationSensor {
            id: rotation
            onReadingChanged: {
                rotationLabel.text = "Rotation x: " + rotation.reading.x.toFixed(1) + " y: "
                        + rotation.reading.y.toFixed(1) + " z: " + rotation.reading.z.toFixed(1);
            }
        }

3) 源码

整个测试程序的源码可以在如下的网址找到:

bzr branch lp:~liu-xiao-guo/debiantrial/sensors

在手机上的运行结果如下:






作者:UbuntuTouch 发表于2014-8-29 9:01:55 原文链接
阅读:10 评论:0 查看评论

Read more
UbuntuTouch

[原]在Ubuntu上使用Map和Position APIs

我们知道Map和Position API是现代手机中非常重要的接口。那么我们如何在Ubuntu手机上使用它们呢?关于更多的Map及Position方面的资料可以在我们的developer网站找到:http://developer.ubuntu.com/api/qml/sdk-1.0/QtLocation.PositionSource/


1)使用MAP接口

我们可以直接使用如下的QML Map来实现MAP的功能:

            Map {
                id: map
                plugin : Plugin {
                    name: "osm"
                }
                anchors.fill: parent
                zoomLevel: 15
                center: QtPositioning.coordinate(39.9289 , 116.3883)

                Label {
                        anchors { top: parent.top; left: parent.left; margins: units.gu(2) }
                        text: "Position is: (" + me.position.coordinate.latitude + ", " +
                                me.position.coordinate.longitude + ")";
                        fontSize: "large"
                        color: "red"
                    }
            }

在这里我们使用了我们伟大祖国的北京地址经纬度坐标(39.9289,116.3883)。同时我们必须指出的是,我们必须使用名叫"osm"的plugin接口。以前一下诺基亚的手机,使用的是名为"nokia"的plugin。为了正确使用这个接口,我们必须在QML文件中调用如下的库:

import QtLocation 5.0
import QtPositioning 5.0

如果要在手机上运行,我们必须加入相应的安全policy。



运行我们的程序,我们可以看到如下的结果:




2)使用Position接口

我们可以通过如下的方式得到我们所在的位置:

            PositionSource {
                 id: me
                 active: true
                 updateInterval: 1000
                 onPositionChanged: {
                     console.log("lat: " + position.coordinate.latitude + " longitude: " +
                                 position.coordinate.longitude);
                     console.log(position.coordinate)
                 }
             }

为了能够显示我们所在的位置,我们也可以把我们上面的程序做一个修改。我们可以把把地图的中心位置设为我们得到的当前位置。修改后的程序为:

            Map {
                id: map
                plugin : Plugin {
                    name: "osm"
                }
                anchors.fill: parent
                zoomLevel: 12
                center: me.position.coordinate
//                center: QtPositioning.coordinate(39.9289 , 116.3883)

                Label {
                        anchors { top: parent.top; left: parent.left; margins: units.gu(2) }
                        text: "Position is: (" + me.position.coordinate.latitude + ", " +
                                me.position.coordinate.longitude + ")";
                        fontSize: "large"
                        color: "red"
                    }
            }

同时我们在Map里用红色的字体显示当前的位置。 运行的结果显示在如下的图中。我们可以改变zoomLevel来看不同的大小:



3)使用MapCircle来显示中心点

虽然我们在上面已经使用了地图来显示当前的位置,但我们还是想用一个明显的标志来显示当前的位置。这里我们用MapCircle来做这件事。这样我们的代码如下:

           Map {
                id: map
                plugin : Plugin {
                    name: "osm"
                }
                anchors.fill: parent
                zoomLevel: 12
                center: me.position.coordinate
                //                center: QtPositioning.coordinate(39.9289 , 116.3883)

                MapCircle {
                    center: me.position.coordinate
                    radius: units.gu(20)
                    color: "red"
                }

                Label {
                    anchors { top: parent.top; left: parent.left; margins: units.gu(2) }
                    text: "Position is: (" + me.position.coordinate.latitude + ", " +
                          me.position.coordinate.longitude + ")";
                    fontSize: "large"
                    color: "red"
                }
            }

运行的结果如下:



源码在如下网址可以找到:

bzr push lp:~liu-xiao-guo/debiantrial/map




作者:UbuntuTouch 发表于2014-8-29 9:48:47 原文链接
阅读:7 评论:0 查看评论

Read more
UbuntuTouch

保存数据对于一些应用来说非常重要。比如在游戏闯关的时候,我们需要保存当前的关及一些应用的设置等。

1)创建数据库及文档

我们用Qt SDK来创建一个简单的应用。同时加入如下的库:

import U1db 1.0 as U1db

为了能够在手机上创建我们所需要的数据库文件,我们必须定义好自己的应用名称“com.ubuntu.developer.liu-xiao-guo.u1db”:

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

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

    anchorToKeyboard: true


首先我们来创建一个数据库:

   U1db.Database {
        id: aDatabase
        path: "aDatabase"
    }

创建一个数据库非常容易,它需要一个id及一个path。这个path用来标示数据库文件被创建的路经。一个数据库就是一个model。它可以被其他的元素所引用。比如说listview。

另外我们也需要创建一个document:

    U1db.Document {
        id: aDocument
        database: aDatabase
        docId: 'helloworld'
        create: true
        defaults: { "hello": "Hello World!" }
    }

这个document除了一个id及一个docId以外并没有什么特别的地方。这俩个定义有些时候并不是必须的,虽然在某种情况下使得引用变得更加容易。document可以在runtime时动态地创建。在上面的例子里,我们设置create为true。在没有contents定义的情况下,defaults所定义的值将被设置为默认的值。

如果我们有如下的定义:

    U1db.Document {
        id: aDocument
        database: aDatabase
        docId: 'helloworld'
        create: true
        defaults: { "hello": "Hello World!" }
        contents: {"hello" : "nin hao!" }
    }

那么定义的值将被contents所定义的“nin hao!”所取代。


2)显示及修改数据库

为了显示数据库中的数据,我们使用一个listview。

            ListView {
                width: units.gu(45)
                height: units.gu(30)

                /*
                Here is the reference to the Database model mentioned earlier.
                */
                model: aDatabase

                /* A delegate will be created for each Document retrieved from the Database */
                delegate: Text {
                    text: {
                        /*!
                        The object called 'contents' contains a string as demonstrated here. In this example 'hello' is our search string.

                        text: contents.hello
                        */
                        text: contents.hello
                    }
                }
            }

为了显示修改我们的数据库数据,我们加入如下的代码:

            TextField {
                id: value
                placeholderText: "please input a new value"
                text:"good"
            }

            Button {
                id: modify
                text: "Modify"
                height: units.gu(5)
                width: units.gu(25)

                onClicked: {
                    aDocument.contents = { "hello": value.text }
                }
            }

这里我们通过aDocument来直接对"hello"中的数据进行修改。运行效果如下:

    

在手机上运行后,我们可以查看在手机上生成的文件及路经:



整个代码可以在如下地址找到:

bzr branch lp:~liu-xiao-guo/debiantrial/u1db

作者:UbuntuTouch 发表于2014-8-29 15:15:45 原文链接
阅读:17 评论:0 查看评论

Read more
UbuntuTouch

在前面的一篇文章中,我们已经使用了一种方法U1db来存储我们的应用的设置。这里我们使用另外的一种方法来做同样的事。在这篇文章中,我们使用SQLite的API来存储我们想要存储的东西。事实上这个方法早已经被coreapps里的weather, rss reader及music应用所使用。开发者可以查看https://launchpad.net/ubuntu-phone-coreapps/来更详细地了解如何使用这个方法在实际的例子里存储设置的。下面我们来详细的解释如何这么做


1)创建一个基本的应用


我们使用Qt Create来创建一个“App with Simple UI”的简单template应用。导入如下的库:

import QtQuick.LocalStorage 2.0

这样我们就可以使用SQLite API接口来进行数据库的操作了。

另外很重要的一点,我们必须使用如下的方法定义一个应用的名称:

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.liu-xiao-guo.settings"
 ...
}

这样做的目的是为了能够在应用的自己可以访问的目录里创建一个数据库文件。如果不这样做,数据库文件可能会被创建到其他的地方而导致不能被本应用访问。

下面我们来创建数据库方法来创建,修改,并存储设置:

    function openDB() {
        if(db !== null) return;

        // db = LocalStorage.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
        db = LocalStorage.openDatabaseSync("example-app", "0.1", "Simple example app", 100000);

        try {
            db.transaction(function(tx){
                tx.executeSql('CREATE TABLE IF NOT EXISTS settings(key TEXT UNIQUE, value TEXT)');
                var table  = tx.executeSql("SELECT * FROM settings");
                // Seed the table with default values
                if (table.rows.length == 0) {
                    tx.executeSql('INSERT INTO settings VALUES(?, ?)', ["distro", "Ubuntu"]);
                    tx.executeSql('INSERT INTO settings VALUES(?, ?)', ["foo", "Bar"]);
                    console.log('Settings table added');
                };
            });
        } catch (err) {
            console.log("Error creating table in database: " + err);
        };
    }


    function saveSetting(key, value) {
        openDB();
        db.transaction( function(tx){
            tx.executeSql('INSERT OR REPLACE INTO settings VALUES(?, ?)', [key, value]);
        });
    }

    function getSetting(key) {
        openDB();
        var res = "";
        db.transaction(function(tx) {
            var rs = tx.executeSql('SELECT value FROM settings WHERE key=?;', [key]);
            res = rs.rows.item(0).value;
        });
        return res;
    }

2)创建UI来修改,存储设置


    Page {
        id: app
        title: i18n.tr("Settings")

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

            OptionSelector {
                id: distroToggle
                text: i18n.tr("Favorite Distro")
                model: [i18n.tr("Ubuntu"), i18n.tr("Debian")]
            }

            OptionSelector {
                id: fooToggle
                text: i18n.tr("Foo")
                model: [i18n.tr("Bar"), i18n.tr("Baz")]
            }

            Button {
                text: i18n.tr("Save settings")
                onClicked: {
                    var distro = (distroToggle.selectedIndex === 0) ? "Ubuntu" : "Debian";
                    console.log("Saved " + distro);
                    saveSetting("distro", distro);

                    var foo = (fooToggle.selectedIndex === 0) ? "Bar" : "Baz";
                    console.log("Saved " + foo);
                    saveSetting("foo", foo);
                }
            }
        }

        Component.onCompleted: {
            var distro = getSetting('distro');
            distroToggle.selectedIndex = (distro === "Debian") ? 1 : 0;
            var foo = getSetting('foo');
            fooToggle.selectedIndex = (foo === "Baz") ? 1 : 0;
        }
    }

我们来运行该应用:



在手机上的数据库文件:



源码可以在如下地址下载:

bzr branch lp:~liu-xiao-guo/debiantrial/settings





作者:UbuntuTouch 发表于2014-9-1 10:26:16 原文链接
阅读:11 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章里,我们创建一个小的RSS阅读器。当我们完成这整个过程,我们将学会如何使用最基本的控件来展示内容,并使用不同的layout。


developernews.png


让我们开始吧。

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

首先,我们来打开自己的Qt Creator来创建一个名叫“developernews”的项目。我们使用"App with Simple UI"模版。


ubuntu-sdk-simple-project.png



如果你还没有安装好自己的SDK的话,请参照文章"Ubuntu SDK 安装"来完成自己的安装。我们可以直接运行已经创建好的应用。为了显示的更像一个是一个手机的界面,我们直接把“main.qml"中的尺寸设置如下:

    width: units.gu(50)
    height: units.gu(75)


分辨率无关

Ubuntu的用户界面工具包的重要功能是把用户定义多个设备尺寸进行匹配。采取的方法是定义一个新的单元类型,网格单元(GU在短)。网格单位转换为像素值取决于应用程序运行在屏幕上和设备的类型。下面是一些例子:

Device Conversion
Most laptops 1 gu = 8 px
Retina laptops 1 gu = 16 px
Smart phones 1 gu = 18 px
更多的关于分辨率无关的知识可以在链接找到。


运行效果(Ctrl + R) 如下图所示:




最原始的应用其实没有什么。你可以按一下按钮改变方框中的文字。下面我们来开始设计我们的应用。

2)删除我们不需要的文件及部分代码


由于最初的代码其实对我们来书没有多大的用处。我们现在来修改我们的代码:

1)删除在"main.qml"中不需要的代码,以使得代码如下图所示:



2)修改page中的title使之成为"Developer News"。
3)在项目中的"HelloComponent.qml"上点击右键,并删除该文件。

我们重新运行程序,我们将看到没有任何内容的应用



3)加入一个PageStack

PageStack可以使得我们让一个Page推到另外一个page之上。他可以跟踪这些页面的变化,并自动提供一个"back"的按钮可以让我回到以前的页面。

现在我们来使用PageStack来重新设计我们的应用。把整个在"main.qml"中的的Page代码换成如下的代码:

import QtQuick 2.0
import Ubuntu.Components 0.1
import "components"

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"
    
    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.liu-xiao-guo.developernews"
    
    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true
    
    width: units.gu(50)
    height: units.gu(75)
    
    PageStack {
        id: pageStack
        anchors.fill: parent
        Component.onCompleted: {
            console.log('pagestack created')
            pageStack.push(listPage)
        }
        
        Page {
            id: listPage
            title: i18n.tr("Articles")
        }
    }
}

这里,我们可以看到每个component在被装载完成之后,有一个event事件onCompleted被调用。我们可以用这个方法来初始化我们的一下需要处理的事情。这里,我们把listPage压入堆栈尽管没有任何东西。

这时如果我们重新运行程序,我们会发现界面没有任何新的变化。这是因为我们的page中没有任何的数据。我们在“Application Output”窗口会发现如下的输出:
pagestack created

这说明我们的代码是成功运行的。

4)加入我们自己的控件

我们将加入一个新的QML控件。这个控件的名称叫做“ArticleListView"。它将被定义在一个叫做"ArticleListView.qml"的文件中。控件的名字通常是以大写字母开始的。

     

我们点击项目的右键,加入一个名字叫做“ArticleListView.qml”文件。并把文件放入"components"的目录之中。在默认的情况下,"ArticeListView.qml"除了定义一个方框外,没有任何其他的东西。我们接下来向其中添加我们所需要的内容。

4)定义ArticleListView

我们将用UbuntuListView来显示来自http://developer.ubuntu.com的RSS 条目。UbuntuListView是继承于Qt中的ListView。但是它加入了一些新的一些feature。比如说pull-to-refresh。它也可以很方便地来搜索文字。下面我们来详细介绍如何做:

1)把"ArticleListView.qml"中的代码换成如下的代码:

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


UbuntuListView {
   id: listView
   property alias status: rssModel.status


   model: XmlListModel {
       id: rssModel
       query: "/rss/channel/item"
       XmlRole { name: "title"; query: "title/string()" }
       XmlRole { name: "published"; query: "pubDate/string()" }
       XmlRole { name: "content"; query: "*[name()='content:encoded']/string()" }
   }


   delegate: Subtitled {
       text: title
       subText: published
       progression: true
   }


   Scrollbar {
       flickableItem: listView
   }
}

这里你们可以看到我们定义了一个alias status的属性。对很多初学者来说,可能并不好理解。你可以理解为C语言中的指针虽然并不那么确切。它实际上就是把一个component中的其他item中的属性暴露出来以使得该属性在这个component(比如UbuntuListView)被引用时可以被修改或被引用。

你的第一个挑战。在XmlListModel中我们必须定义我们所需要的feed。对developer.ubuntu.com的RSS的地址是http://developer.ubuntu.com/feed/。为了能够在我们的应用中使用它,我们必须在我们的例程中定义它。具体的property,请参考API documentation of XmlListModel。tips:大家可以查看"source"属性。我们在浏览器中输入地址developer.ubuntu.com/feed,我们可以看到如下的内容



5)使用ArticleListView

我们在上节中已经设计了一个自己的component。在这节中,我们来使用它,就像原本我们已有的其他的控件一样。我们把我们设计好的ArticleListView放到我们已经设计好的“main.qml”中来。在你的main.qml中加入如下的Page:

           ArticleListView {
               id: articleList
               objectName: "articleList"
               anchors.fill: parent
               clip: true
           }

重新运行我们的应用。我们可以看到我们几乎快成功了。我们可以看到来自developer.ubuntu.com的文章列表了。如果你还没有看到这个运行的结果。请查看一下你的XmlListModel中的source是否已经设置正确。


6)创建一个新的Component

就像上面我们创建的ArticleListView一样,我们来创建一个新的ArticleContent的component。该component的文件名字叫做"ArticleContent.qml"。文件位于和ArticleListView一样的路经(components)。
下面我们来向这个新创建的component中加入我们所需要的内容。打开文件"component/ArticleContent.qml",并输入如下的代码:

import QtQuick 2.0
import Ubuntu.Components 1.1

Item {
   property alias text: content.text

   Flickable {
       id: flickableContent
       anchors.fill: parent

       Text {
           id: content
           textFormat: Text.RichText
           wrapMode: Text.WordWrap
           width: parent.width
       }

       contentWidth: parent.width
       contentHeight: content.height
       clip: true
   }


   Scrollbar {
       flickableItem: flickableContent
   }
}


这里我们可以看到,我们创建了一个最基本的继承于Item的component。我们可以在Text网址找到更多关于Text的一些属性以更好地使用它。

7)把ArticleContent和app的其它内容连起来


到目前为止,我们已经创建了一个ArticleContent的控件。我们可以在我们的应用中使用它。每当一个在ArticleListView中的一个item被点击后,我们可以用它来显示详细的内容。

首先,我们必须在ArticleListView中每个item被点击时生成一个signal,并把这个signal连接到我们需要产生的动作。我们可以定义一个名叫"clicked"的signal。

1)打开"ArticleListView.qml"文件,并定义如下的signal:

   signal clicked(var instance)

2) 在“Subtitled"项加入如下的代码:

onClicked: listView.clicked(model)



3)使用我们已经创建好的ArticleContent控件。我们在"main.qml"文件中创建一个新的Page,并使用PageStack。

    PageStack {
        id: pageStack
        anchors.fill: parent
        Component.onCompleted: {
            console.log('pagestack created')
            pageStack.push(listPage)
        }

        Page {
            id: listPage
            title: i18n.tr("Articles")

            ArticleListView {
                id: articleList
                objectName: "articleList"
                anchors.fill: parent
                clip:true

                onClicked: {
                    console.log('[flat] article clicked: '+instance.title)
                    articleContent.text = instance.content
                    pageStack.push(contentPage)
                }

            }
        }

        Page {
            id: contentPage
            title: i18n.tr("Content")

            ArticleContent {
                id: articleContent
                objectName: "articleContent"
                anchors.fill: parent
            }
        }
    }



我们这时运行程序,可以看到如下的图片。我们可以点击在主界面中的item,并查看具体的内容:

     

至此我们已经完成了第一个阶段的代码。整个程序的代码可以在如下的网址看到。

bzr branch lp:~liu-xiao-guo/debiantrial/developernews

8)添加一个reload按钮


在这里,我们想添加一个“reload"的按钮,这样我们可以随时查看最新的在developer.ubuntu.com的内容。这里我们来定义一个"reload"方法以使得它能在“main.qml"文件中被调用。它的作用是使得XmlListModel中的refresh方法被调用。

1)打开"components/ArticleListView.qml"文件,加入如下的方法到UbuntuListView中去:

   /*
      Functions can be added to any Component definition, and can be called on
      using any instance of that Component. Here we will define a 'reload'
      function that we can call from our main application file that will cause
      the interal XmlListModel to reload it's content
    */
    function reload() {
        console.log('reloading')
        rssModel.update()
    }

挑战自己:请查看XmlListModel。你将发现上面的"update()"方法其实不是真正的方法。请找出正确的方法替换它。

2)在MainView的定义中加入如下的Action

  Action {
       id: reloadAction
       text: "Reload"
       iconName: "reload"
       onTriggered: articleList.reload()
   }
Action是一个可以重复使用的控件,并可以在多处被引用
在这里定义并给于其一个id使得它可以在多处被引用。


3) 在"listPage"中,加入如下的ToolbarButton。

tools: ToolbarItems {
               ToolbarButton {
                   action: reloadAction
               }
           }
在article的contengPage,我们想显示一个toolbar按钮
在browser中打开该文章。因为我们已经定义好了一个
可以重复使用的的Action,我们只需要引用它的即可

重新运行应用,我们可以看到如下的画面。你们可以尝试点击"reload"看看有什么反应。


5.png


9)玩一玩应用的一些属性

虽然目前我们的应用已经完成了我们大部分的所需要的功能。在这里,我们来尝试修改应用的一些属性来看看有什么一些变化。

1)定义应用MainView的id以使得我们在下面的章节中被引用

  id: mainView

2)Playtime。在MainView中找到相应的一些属性来尝试修改看看应用有什么变化。尝试改变boolean值来看看应用有那些变化。

3)我们尝试改变如下的值:

   automaticOrientation: true
   useDeprecatedToolbar: false

再重新运行应用,我们发现应用在我们转动屏幕的时候会发生相应的转动。你也可以同时看到toolbar的位置也发生了相应的变化。


6.png   


至此我们整个应用在第一阶段基本已经完成了。

整个应用的源码可以在地址下载:

bzr branch lp:~liu-xiao-guo/debiantrial/developernews_step1

我们可以在下一个章节中继续学习conditional layout来完成整个的练习!


作者:UbuntuTouch 发表于2014-9-2 14:17:36 原文链接
阅读:25 评论:0 查看评论

Read more