Canonical Voices

What Ubuntu Touch Development in CSDN (Chinese) talks about

UbuntuTouch

[原]Ubuntu 手机开发培训准备

在这篇文章中,我们将介绍学生如何做培训准备前的准备工作。提前准备并安装好自己的环境是做好一个培训非常重要的步骤。


                 

1)安装好自己的SDK


如果想在自己的电脑上安装Ubntu系统

学生可以按照文章“Ubuntu SDK 安装”安装好自己的Ubuntu系统及SDK。让后根据文章“创建第一个Ubuntu for phone应用”来检验自己安装的环境是否正确。这种安装通常需要在电脑上安装多系统功能,或虚拟机(模拟器在虚拟机的效果可能并不好)。

如果想做一个专为Ubuntu手机开发而做的Live USB

请参照文章“如何制作Ubuntu SDK Live USB盘”来专门制作一个可以启动的Live USB盘。这个盘可以直接插入到电脑中的USB口中,并启动Ubuntu系统。这个USB盘中已经安装好整个可以供开发的SDK,不需要安装任何额外的软件即可开发。

a) 在BIOS中启动硬件虚拟化功能,这样会使得模拟器的运行速度加快
b) 在BIOS中设置优选顺序以使得USB可以优先启动,或在启动的时候按下F12功能键,并选择由USB来启动Ubuntu

在启动Ubuntu系统后,Ubuntu SDK已经完全安装好了。开发者可以直接进行开发了。建议参阅文章“创建第一个Ubuntu for phone应用”来检验自己安装的环境是否正确。


在开发过程中如果使用手机进行安装时,如果需要密码解锁手机的话,这个密码是“0000”。

2)Ubuntu手机介绍


对不熟悉Ubuntu手机的开发者来说,可以先观看视频“如何使用Ubuntu手机”来了解Ubuntu手机。如果你想对Ubuntu SDK有更深的认识,请观看视频“如何使用Ubuntu SDK (视频)”。开发者也可以观看Ubuntu手机的官方宣传视频来更进一步了解。

你可以在地址“Ubuntu手机介绍”下载有关Ubuntu手机介绍的幻灯片,并在地址观看相应的视频


3)QML应用开发


Flickr应用开发

阅读文章“使用Ubuntu SDK开发Flickr应用教程”,并观看视频“Ubuntu手机应用QML开发 (视频)”。幻灯片“Ubuntu应用开发”。

教程的源码在: bzr branch lp:~liu-xiao-guo/debiantrial/flickr7
我们可以在Shell中输入以上的指令来下载源码。

DeveloperNews RSS阅读器

首先我们可以阅读文章“从零开始创建一个Ubuntu应用--一个小的RSS阅读器”及文章“如何在Ubuntu中使用条件布局”。视频在“在Ubuntu平台上开发Qt Quick QML应用 (视频)

教程的源码在:bzr branch lp:~liu-xiao-guo/debiantrial/developernews4

我们可以在Shell中输入以上的指令来下载源码。


4)Scope 开发


大家可以先观看视频“Ubuntu Scope简介及开发流程”来了解Ubuntu OS上的Scope开发流程。


幻灯片“Scope技术开发”。幻灯片讲演的视频在地址观看。

教程的源码在: bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient8
我们可以在Shell中输入以上的指令来下载源码。

更多关于Scope开发的例程可以在链接找到。


5)HTML 5开发


我们可以参阅文章“在Ubuntu手机平台上创建一个HTML 5的应用”来学习如何在Ubuntu平台上开发HTML 5的应用。源码在地址下载:

git clone https://gitcafe.com/ubuntu/html-rssreader6.git

更多例程:
  • 百度翻译: bzr branch lp:~liu-xiao-guo/debiantrial/baidutranslator
  • 字典: bzr branch lp:~liu-xiao-guo/debiantrial/meanings

6)更多的培训材料


我们也有更多的英文的培训材料。开发者可以在地址下载。


如果有任何问题,请在该文章处评论。我会尽力回答你们的问题。


作者:UbuntuTouch 发表于2015/1/4 15:36:54 原文链接
阅读:1252 评论:6 查看评论

Read more
UbuntuTouch

在这篇文章中,我们来介绍如何判断一个QML应用被推到后台或前台。我们知道,在Ubuntu手机平台中,它是一个单应用的操作系统。当一个应用被推到后台后,应用就被挂起,不能运行。我们有时需要这个标志来判断我们的应用什么时候是在前台,什么时候是在后台。


我们用Ubuntu SDK创建一个简单的QML应用:


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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


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

        Connections {
             target: Qt.application
             onActiveChanged: {
                 console.log("Qt.application.active: " + Qt.application.active);
             }
         }
    }
}

在这里,我们使用Qt.application这个变量的"active"属性来判断一个应用是否被推到后台或前台。我们运行应用结果如下:




当我们在手机上把应用推到后台时,就会显示false;当我们把应用推到前台时,就会显示true。

整个应用的源码在:bzr branch lp:~liu-xiao-guo/debiantrial/foregrounddetect


作者:UbuntuTouch 发表于2015/1/5 10:56:25 原文链接
阅读:481 评论:0 查看评论

Read more
UbuntuTouch

在这个视频里,我们从“0”开始来开发一个mini的RSS阅读器。通过这个练习,开发者可以对QML的编程有一个基本的了解,并了解在Ubuntu平台上的一些开发的流程。应用的图片如下:


  



作者:UbuntuTouch 发表于2015/1/13 15:08:31 原文链接
阅读:394 评论:0 查看评论

Read more
UbuntuTouch

[原]如何制作Ubuntu SDK Live USB盘

对于一些想开发Ubuntu手机应用或Scope的开发者来说,不想重新买一个电脑安装Ubuntu操作系统或在自己的硬盘上重新安装一个Ubuntu系统,那么可以考虑制作一个Ubuntu系统的Live USB盘。这个USB包括如下的部分:


  • Ubuntu Kylin 14.10操作系统
  • Ubuntu SDK (包括已经安装好的SDK,模拟器及编译环境)

使用这个Live USB盘,开发者就不用安装任何的东西,直接插入电脑的USB口中。在电脑启动的过程中,选择我们制作好的USB启动盘进行启动(在电脑启动的过程中,按下“F12”键)。在启动的过程中选择“Try Ubuntu Kylin without installing




虽然这是一个Ubuntu OS的启动盘,但是它可以保存我们在开发过程中所创建的项目(存于Home目录中)及一些设置(比如wifi设置密码等)。


当我们选择USB时,我们最好是选择USB 3.0并把USB盘放入到电脑USB 3.0的口中。一般来说,电脑上的USB 3.0口是用蓝色标示的。建议使用质量较好,速度较快一点的USB这样可以使得系统的启动和运行更快更流畅。目前我们使用SanDisk CZ80来做测试,效果还是不错的。USB需要有16G的存储。


为了使得我们的模拟器能够更加流畅及模拟器不会出现黑色的屏幕,我们需要在电脑的BIOS里启动硬件虚拟化功能。开发者需要到自己的电脑的BIOS里的设置启动VT-X/AMD-V。开发者可以参考文章“Ubuntu SDK 安装”来检查自己的电脑是否支持virtualization。




如果开发者想要在自己的电脑上安装Ubuntu系统并在上面开发的话,可以参考文章“Ubuntu SDK 安装”来一步一步地安装Ubuntu SDK。



1)如何在Ubuntu系统下制作Live USB盘


启动Ubuntu操作系统,打开浏览器并在如下的地址下载最新的image:


https://mega.co.nz/#F!S8QSRZyI!2HBWgXk4kmc_2bcCcpBR3Q


下载的文件包含:

  • kylin-live-20150133.iso (md5sum 13cd61270bf98eb462dc0497df8eee33) 
  • casper-rw-20150113.tar.bz2  (md5sum 8c69f94a03481275bf66aa883b69ae1b)
  • post-usb-creator-window.sh(在Windows下制作需要这个)
  • README.md (简单的说明文件)

我们把下载的文件存于到我们想要的一个目录中,比如在自己的Home下的“usb”目录中。


在Dash中输入“usb”,并启动“Startup Disk Creator/启动盘创建器”






我们按照如下的方法来制作我们的USB启动盘。





在设置“储存在额外保留空间”时,它的值应该为非零的值。等USB盘已经制作好以后,你将会看到如下的画面:







重新挂载USB盘,因为在前一步会自动卸载USB盘,或者在Ubuntu中的文件浏览器中点击USB所在的device。这样就可以完成重新挂载USB:





然后按下面运行自带的脚本,参数为 USB 盘挂载的路径。


解压已经下载的casper-rw-2015xxxx.tar.bz2文件


等文件都被解压完后,进入解压文件所在的目录,并在shell中执行如下的指令:


liuxg@liuxg:~/usb$ ./post-usb-creator-linux.sh /media/liuxg/BD52-7153/


这里“/liuxg/BD52-7153”为USB盘挂载的路径。根据自己USB盘所在的路径替换。


2)如何在Windows 平台下制作启动盘


http://www.ubuntu.com/download/desktop/create-a-usb-stick-on-windows

下载制作工具,与 Linux 平台的工具相似。




单我们在选择“Persistent file”时,它的大小应该是非零的一个值。在我们填入“Step 2”时,我们不应该把拷贝好的字符串拷到该输入框中,否则在“Step 3”中的输入框就会是灰色的。我们应该点击“Browse”按钮,并按照如下的方式进行输入image的路径:




在这之后把 casper-rw 文件拷贝到USB的主目录下即可。


:如果只想使用英文版的Ubuntu系统就不需要进行下面的步骤。如果想要支持中文版,请把 post-usb-creator-window.sh 也拷贝到 USB盘的根目录下。从USB 盘启动Ubuntu系统后, 在dash中启动Terminal,






并在Terminal中执行如下的命令:


$ cd /cdrom/

$ sudo ./post-usb-creator-window.sh


再次重新启动后,会进入中文版的Ubuntu系统。


3)测试已经制作好的USB启动盘


我们可以把我们的Live USB盘插入电脑,我们可以通文章“创建第一个Ubuntu for phone应用”来检验我们是否有一个完好的Ubuntu SDK。


在我们启动模拟器时,如果需要输入密码,请使用默认的密码“0000”。如果开发者需要自己修改这个密码,请到Ubuntu SDK模拟器中的“系统设置”中去修改。


对于应用开发者来说,在Qt Creator中的热键组合“Ctrl + Space”键有它独特的用处。可是,在Ubuntu系统中,“Ctrl + Space”被用来转换中英文输入法。建议开发者参考文章“怎么在Ubuntu OS上面安装搜狗输入法及对Qt Creator的支持”来重新定义键的组合。


已知问题 (known issues)

如果你在使用的过程中,发现有如下的乱码的情况(极少情况下出现),请重新启动你的机器来纠正这个问题。




在个别电脑上不能启动的问题


我们发现在联想 E455 出现不能启动的问题,目前怀疑是和 AMD 显卡驱动有关,问题仍在调查中,如果遇到些问题,请在系统上安装14.04 LTS版本并安装相应的ubuntu-sdk包来尝试学习ubuntu phone的开发知识,其中的基本概念都是一样。


注:如果想长时间致力于ubuntu phone的开发建议在电脑上安装一个ubuntu系统,最好是utopic (14.10),而不是在Live环境下进行学习,一是以防数据的丢失,二是在使用性能上有更快速的体验。



作者:UbuntuTouch 发表于2015/1/22 15:35:55 原文链接
阅读:527 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将介绍如何使用Ubuntu系统提供的online account API来实现我们的微博。通过这篇文章,开发者可以使用同样的方法来实现对其它Web Service的访问,比如淘宝,优酷等网站的访问。我们实现的新浪微博的Scope的截图如下。本篇文章的重点是介绍online account API的使用,所以我不会像以前的那些教程一样从最基本的Scope开发介绍。更多关于online account的介绍在网址找到。


   


1)创建一个最基本的weibo Scope

我们通过使用Ubuntu SDK来创建一个最基本的Scope。在这里我们使用如下的Scope template:


     


    

按照如上的步骤,我们可以创建一个我们自己的一个最基本的weibo Scope,虽然里面显示的还是天气。


2)为online account添加必要的文件


我们可以参考文章,也可以参照视频,来对online account API有一个更深的了解。为了方便我们的设计,我们在项目的根目录下创建如下的"click"目录:



我们同时也把在根目录下的“manifest.json.in”及“weibo.apparmor”移到“click”目录下,当然,我们也需要对根目录下的“CMakeLists.txt”做必要的修改。修改后的CMakeLists.txt文件如下:

project(weibo CXX)
cmake_minimum_required(VERSION 2.8.10)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")

# We require g++ 4.9, to avoid ABI breakage with earlier version.
set(cxx_version_required 4.9)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    if (NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${cxx_version_required}")
        message(FATAL_ERROR "g++ version must be ${cxx_version_required}!")
    endif()
endif()

# Set strict and naggy C++ compiler flags, and enable C++11
add_definitions(
  -fno-permissive
  -std=c++11
  -pedantic
  -Wall
  -Wextra
  -fPIC
  -DQT_NO_KEYWORDS
)

include(GNUInstallDirs)
find_package(PkgConfig)
find_package(Intltool)

# We depend on Boost for string trimming
find_package(
  Boost
  REQUIRED
)

# Search for our dependencies
pkg_check_modules(
  SCOPE
  libunity-scopes>=0.6.0
  net-cpp>=1.1.0
  REQUIRED
)

find_package(Qt5Core REQUIRED)
include_directories(${Qt5Core_INCLUDE_DIRS})

# Add our dependencies to the include paths
include_directories(
  "${CMAKE_SOURCE_DIR}/include"
  ${Boost_INCLUDE_DIRS}
  ${SCOPE_INCLUDE_DIRS}
)

# Do not remove this line, its required for the correct functionality of the Ubuntu-SDK
set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file")
set(UBUNTU_PROJECT_TYPE "Scope" CACHE INTERNAL "Tells QtCreator this is a Scope project")

# Important project paths
set(CMAKE_INSTALL_PREFIX /)
set(SCOPE_INSTALL_DIR "/weibo")
set(GETTEXT_PACKAGE "weibo")
set(PACKAGE_NAME "weibo.ubuntu")
set(SCOPE_NAME "${PACKAGE_NAME}_weibo")

set(SCOPE_INSTALL_NAME "${PACKAGE_NAME}_weibo")
set(SCOPE_ACCOUNTS_NAME "${PACKAGE_NAME}_accounts")
set(SYMBOL_MAP "${CMAKE_SOURCE_DIR}/data/${PACKAGE_NAME}.map")

add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}")
add_definitions(-DSCOPE_NAME="${SCOPE_NAME}")
add_definitions(-DSCOPE_INSTALL_NAME="${SCOPE_INSTALL_NAME}")
add_definitions(-DSCOPE_ACCOUNTS_NAME="${SCOPE_ACCOUNTS_NAME}")
add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}")

# If we need to refer to the scope's name or package in code, these definitions will help

#This command figures out the target architecture and puts it into the manifest file
execute_process(
  COMMAND dpkg-architecture -qDEB_HOST_ARCH
  OUTPUT_VARIABLE CLICK_ARCH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

#configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)

# Install the click manifest
#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/")
#install(FILES "weibo.apparmor" DESTINATION "/")

# Make these files show up in QtCreator
file(GLOB_RECURSE
  _PO_FILES
  "po/*.po" 
)
add_custom_target(hidden_files
  ALL
  SOURCES
#    manifest.json.in
#    weibo.apparmor
    data/${SCOPE_NAME}.ini.in
    po/POTFILES.in
    po/${GETTEXT_PACKAGE}.pot
    ${_PO_FILES}
)

add_subdirectory(click)

# Add our main directories
add_subdirectory(src)
add_subdirectory(data)
add_subdirectory(po)

# Set up the tests
enable_testing()
add_subdirectory(tests)
add_custom_target(
  check
  ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure
)

这里主要的修改是影藏显示“manifest.json.in”及“weibo.apparmor”文件的显示,应为这两个文件已经移到“click”目录中了。同时也注释掉如下的语句,应为这项工作在“click”目录中的“CMakeLists.txt”中已经做了。

#configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)

# Install the click manifest
#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/")
#install(FILES "weibo.apparmor" DESTINATION "/")

另外,我们为了能够正确地显示项目,我们也对如下的句子做了修改:

set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file")

这里我们加上了“click”路径。同时我们也加入了如下的定义:

set(SCOPE_INSTALL_NAME "${PACKAGE_NAME}_weibo")
set(SCOPE_ACCOUNTS_NAME "${PACKAGE_NAME}_accounts")
set(SYMBOL_MAP "${CMAKE_SOURCE_DIR}/data/${PACKAGE_NAME}.map")

add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}")
add_definitions(-DSCOPE_NAME="${SCOPE_NAME}")
add_definitions(-DSCOPE_INSTALL_NAME="${SCOPE_INSTALL_NAME}")
add_definitions(-DSCOPE_ACCOUNTS_NAME="${SCOPE_ACCOUNTS_NAME}")
add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}")

这样这些变量及定义可以在项目的其它文件中被正确地引用了。

下面,我们来看看“click”目录下的文件:

apparmor.json


{
    "template": "ubuntu-scope-network",
    "policy_groups": [
        "accounts"
    ],
    "policy_version": 1.2
}


注意这里在“policy_groups”里我们多加入了一个称作为“accounts”的policy。这个是必须的,否则不可以正常工作。


manifest.json.in

这个文件的显示如下:

{
    "description": "This is a tutorial for weibo scope",
    "framework": "ubuntu-sdk-14.10",
    "architecture": "@CLICK_ARCH@",
    "hooks": {
        "weibo": {
            "scope": "weibo",
            "apparmor": "apparmor.json",
            "account-application": "weibo.application",
            "account-service": "weibo.service"
        },
        "accounts": {
            "account-provider": "weibo.provider",
            "account-qml-plugin": "qml-plugin"
        }
    },
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "name": "@PACKAGE_NAME@",
    "title": "weibo scope",
    "version": "0.1"
}

这个文件和我们template所产生的文件略有不同。这里的“hooks”中加入了account的一些东西。在每个用户登陆时都会生产相应的配置文件。

weibo.application.in


<?xml version="1.0" encoding="UTF-8"?>
<application>
  <description>Weibo scope</description>
  <desktop-entry>@SCOPE_INSTALL_NAME@.desktop</desktop-entry>
  <services>
    <service id="@SCOPE_INSTALL_NAME@">
      <description>Watch your favorite Weibo messages</description>
    </service>
  </services>
</application>


这个是微博Scope的描述文件。它定义了Scope的service id及描述

weibo.service.in


<?xml version="1.0" encoding="UTF-8"?>
<service>
  <type>sharing</type>
  <name>Weibo scope</name>
  <icon>weibo/icon.png</icon>
  <provider>@SCOPE_ACCOUNTS_NAME@</provider>
  <translations>unity-scope-weibo</translations>
</service>

这个service文件描述了微博Scope的service信息。

weibo.provider.in

这是一个微博的account provider信息。这是一个重要的文件。如果我们不能正确地设置这个文件,可能我们不能正确地访问微博account。

<?xml version="1.0" encoding="UTF-8"?>
<provider>
  <name>Weibo</name>
  <icon>weibo/icon.png</icon>
  <translations>unity-scope-weibo</translations>
  <plugin>generic-oauth</plugin>
  <domains>.*weibo\.com</domains>
  <single-account>true</single-account>

  <template>
    <group name="auth">
      <setting name="method">oauth2</setting>
      <setting name="mechanism">web_server</setting>
      <group name="oauth2">
        <group name="web_server">
          <setting name="Host">api.weibo.com</setting>
          <setting name="AuthPath">oauth2/authorize</setting>
          <setting name="TokenPath">oauth2/access_token</setting>
          <setting name="RedirectUri">https://api.weibo.com/oauth2/default.html</setting>
          <setting name="ResponseType">code</setting>
          <setting name="ClientId">your developer key</setting>
          <setting type="as" name="AllowedSchemes">['https','http']</setting>
          <setting name="ClientSecret">your developer secret</setting>
          <setting name="ForceClientAuthViaRequestBody" type="b">true</setting>  
      </group>
      </group>
    </group>
  </template>
</provider>

我们需要在“微博开放平台”去申请开发者key及secret。同时,我们一定要记得在微博的设置中做如下的设置,否则我们不能得到正确的数据。



“click”目录下的CMakeLists.txt文件内容如下:

function(configure_scope_files)
  foreach(_file ${ARGV})
    configure_file(
      "${_file}.in"
      "${CMAKE_CURRENT_BINARY_DIR}/${_file}"
      @ONLY
    )
  endforeach()
endfunction()

configure_scope_files(
  manifest.json
  weibo.provider
  weibo.service
  weibo.application
)

add_custom_target(hidden_files1
  ALL
  SOURCES
    apparmor.json
)

install(
  FILES
    "${CMAKE_CURRENT_BINARY_DIR}/manifest.json"
    "apparmor.json"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.provider"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.service"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.application"
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

install(
  DIRECTORY
    "qml-plugin"
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

这里它对该目录下的文件进行了安装。

为了能够对Scope的设置文件“weibo.ubuntu_weibo.ini.in”进行本地化的支持,我们对“data\CMakeListx.txt”加入如下的句子:

intltool_merge_translations(
  "${CMAKE_CURRENT_SOURCE_DIR}/${SCOPE_NAME}.ini.in"
  "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_INSTALL_NAME}.ini"
  ALL
  UTF8
)

这样它可以提取里面相应的英文,并在“po”目录中的.pot文件中进行翻译。

经过我们这样的修改后,我们的最基本的具有online account的设置文件已经基本已经做好了。我们可以在我们的手机或模拟器中运行我们的Scope了。在运行时,



我们选择“yes”继续运行。我们可以看到如下的画面:




者和我们所创建的最基本的Scope没有任何的差别,我们可以在手机的如下地址查看该目录的内容:



这个是我们所创建weibo provider的地方。


3)对代码进行修改

我们在上面已经对online account的配置文件做了修改。下面我们来对我们的cpp代码进行修改,为了能够使得我们使用online account,我们在query.cpp文件中加入如下的代码:

const static string SEARCH_CATEGORY_LOGIN_NAG = R"(
{
        "schema-version": 1,
        "template": {
        "category-layout": "grid",
        "card-size": "large",
        "card-background": "color:///#1ab7ea"
        },
        "components": {
        "title": "title",
        "background": "background",
        "art" : {
        "aspect-ratio": 100.0
        }
        }
        }
        )";


void Query::run(sc::SearchReplyProxy const& reply) {
    add_login_nag(reply);
 ...
}

void Query::add_login_nag(const sc::SearchReplyProxy &reply) {
    //    if (getenv("VIMEO_SCOPE_IGNORE_ACCOUNTS")) {
    //        return;
    //    }
    qDebug() << "SCOPE_INSTALL_NAME: " << SCOPE_INSTALL_NAME;
    qDebug() << "SCOPE_ACCOUNTS_NAME: " << SCOPE_ACCOUNTS_NAME;

    sc::OnlineAccountClient oa_client(SCOPE_INSTALL_NAME, "sharing", SCOPE_ACCOUNTS_NAME);

    // Check if our service is authenticated
    bool service_authenticated = false;

    int count = oa_client.get_service_statuses().size();
    qDebug() << "count: " << count;

    for ( sc::OnlineAccountClient::ServiceStatus const& status :
          oa_client.get_service_statuses())
    {
        if (status.service_authenticated)
        {
            service_authenticated = true;
            qDebug() << "Sevice is authenticated!";
            qDebug() << "account id: "  << status.account_id;
            qDebug() << "client id: " << QString::fromStdString(status.client_id);
            qDebug() << "service enabled: " << status.service_enabled;
            qDebug() << "secret: " << QString::fromStdString(status.client_secret);
            qDebug() << "access token: " << QString::fromStdString(status.access_token);
            accessToken_ = QString::fromStdString(status.access_token);

            // pass the access token to the client so that http request can be made
            client_.setAccessToken(accessToken_);
            break;
        }
    }

    if (!service_authenticated)
    {
        qDebug() << "Service is not authenicated!";

        sc::CategoryRenderer rdr(SEARCH_CATEGORY_LOGIN_NAG);
        auto cat = reply->register_category("weibo_login_nag", "", "", rdr);

        sc::CategorisedResult res(cat);
        res.set_title(_("Log-in to Weibo"));

        oa_client.register_account_login_item(res,
                                              query(),
                                              sc::OnlineAccountClient::InvalidateResults,
                                              sc::OnlineAccountClient::DoNothing);

        reply->push(res);
    }
}

重新运行我们的Scope,我们可以在手机上看到如下的画面:


我们点击“Log-in to Weibo”按钮:



我们使用我们自己的微博的账号进行登陆,在代码中,我们可以看到:



我们打开手机中的“系统设置”,再打开“账号”,当我们完成我们自己的账号登陆微博后,可以看到:



这说明我们的online account设置等已经是成功的。我们更进一步对我们的client代码进行修改,我们可以看到如下的画面:




整个项目的source code可以在如下的地址下载:

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

在使用代码时,一定要在“click/weibo.provider.in”中填入自己的开发者key及secret。
作者:UbuntuTouch 发表于2015/2/3 11:51:37 原文链接
阅读:590 评论:0 查看评论

Read more
UbuntuTouch

在这个视频里,介绍如何在开发Ubuntu平台上开发Webapps及HTML 5的应用。


http://v.youku.com/v_show/id_XODkwNDQxMDQ0.html

作者:UbuntuTouch 发表于2015/2/10 19:26:33 原文链接
阅读:398 评论:0 查看评论

Read more
UbuntuTouch

无论你是互联网世界的一个高手或是一个从来没有接触过互联网的新手,这篇文章将给你带来完整的在Ubuntu平台上开发HTML 5的应用。我们将慢慢地通过这个练习让你很自然地进入并熟悉整个的HTML 5应用的开发流程。在这个练习中,我们将设计一个最简单的RSS阅读器。当我们的应用设计完整后,应用的显示如下:


       

如果你是一个固执的HTM 5黑客,你可以选择任何你所喜欢的工具及工具包来开发你的HTML 5应用。它们将会很好地工作于Ubuntu手机上。我们将只专注于Ubuntu SDK提供的工具及工具包。更多关于HTML 5开发的信息可以在Ubuntu的官方网站中文网站得到。


如果你还没有安装好自己的Ubuntu SDK,请参照文章“Ubuntu SDK 安装”来安装好自己的SDK。


1)创建一个新的项目


在这一步,我们将使用Ubuntu SDK来生产一个项目:




     

      


请按照上面的步骤来创建一个新的项目“html5-rssreader”。我们首先在Desktop上运行该应用(使用热键Ctrl +R):




可以看出是以非常简单的HTML 5应用。我们可以点击应用的按钮在浏览器中打开上面显示的链接。


2)如何调试我们的应用


我们知道调试应用是非常重要的。我们怎么调试我们的HTML 5应用呢?我们开发的步骤是一般现在自己的电脑上开发好我们直接的应用,然后再打包并部署到自己的手机上或模拟器中。当我们在SDK中运行我们自己的HTML 5应用时,我们可以看到:




在Application Output输出窗口,我们可以看到一个地址:

http://192.168.1.106:9221


这个就是我们用来调试的地址。我们把这个地址拷到我们的Chrome或Chromium地址栏中,并进入:



在我们调试时,我们只能运行一个应用的实例,否则,我们将看不到任何的输出。


3)删除默认项目中不需要的代码



在这一节中,我们将删除项目中不需要的部分代码。

在项目的www/目录中,你可以看到如下的内容:
  • HTML 文件
  • images (在www/img目录下)
  • javascript文件 (在www/js目录下)
  • CSS文件 (在www/css目录下)
首先我们来对index.html进行修改:

1)首先,我们来查看如下的代码:

<div data-role="content">

2)就像你看到的那样,有一个<div>在那里。它定义了一个叫做hello-page的tab。<div>包含了最直接的HTML组成部分。我们删除所有包含在

 <div data-role="content">

里面的内容。这样整个body的内容为:

<body>

    <div data-role="mainview">

        <header data-role="header">
            <ul data-role="tabs">
                <li data-role="tabitem" data-page="hello-page">Hello World</li>
            </ul>
        </header>

        <div data-role="content">
        </div>
    </div>

    <script src="js/app.js"></script>
</body>

重新运行我们的应用,我们可以看到:



我们可以看到应用中没有显示任何的东西。这也并不奇怪,因为我们本来就没有放任何的东西在里面。我们接下来也需要修改应用的标题。


4)删除不必要的代码


在上面的一节中,我们主要对index.html进行了修改。在这个章节里,我们将对css及javascript文件进行修改。

1)我们把css/app.css中的所有内容删除掉。这里的内容是针对helloworld模版的。我们不需要它们

2)在js/app.js文件中,我们删除如下的代码:

 function addClass(elem, className) {
       elem.className += ' ' + className;
   };

   function removeClass(elem, className) {
       elem.className = elem.className.replace(className, '');
   };

同时,我们删除从如下的行:

   // Detect if Cordova script is uncommented or not and show the appropriate status.

到最后的一行的前面:

};

等我们完成所有的操作之后,最后js/app.js文件的内容如下:

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

UbuntuUI是Ubuntu HTML 5应用的一个关键的架构类。我们需要构造它并初始化它以生产我们需要的Ubuntu HTML 5应用。我们可以通过这个对象来方位Ubuntu HTML 5的对象(对应于Ubuntu DOM元)及方法。到目前位置,这是我们最基本的一个最小的一个HTML 5应用虽然没有任何的东西。运行我们的应用,我们可以看到:




所有的源码在地址找到 git clone https://gitcafe.com/ubuntu/html-rssreader1.git


5)设计我们自己的应用


在这个章节中,我们将专注于设计我们自己的应用,所以我们我们将删除所有和helloworld相关的东西。


1)把如下的行

   <title>An Ubuntu HTML5 application</title>
   <meta name="description" content="An Ubuntu HTML5 application">

改为:

   <title>RSS Mobile Reader</title>
   <meta name="description" content="RSS Mobile Reader">

2)删除如下的代码:

   <ul data-role="tabs">
               <li data-role="tabitem" data-page="hello-page">Hello World</li>
   </ul>

取而代之的是,我们将使用pagestack。关于应用的两种布局,我们可以参考网址以了解更多的信息。

我们在如下行

 <div data-role="content">

的下面,加入如下的代码:

    <div data-role="pagestack">
         <div data-role="page" id="main" data-title="RSS Mobile Reader">
         </div>
   </div>

3)在js/app.js文件中,我们加入如下的一行代码在UbuntuUI初始化之后:

 UI.pagestack.push("main");

这里,"pagestack"以一种stack的形式管理所有的pages。最初始,没有任何的page,push()方法通常是在应用启动时,把第一个page推进stack并显示出来。

如果我们现在再运行我们的应用,我们可以看到如下的内容。我们可以看到我们的应用的page标题已经发生变化。我们同时也可以注意到在页面的最下面,我们可以看到“back”字样。这是和上面的显示是不同的地方。


6)把我们的内容放到应用中去


在这个一节中,我们将把我们想要显示的内容放到我们的应用中。我们回顾一下我们的在该文章中一开始显示设计,在第一页中,实际上是一个列表的显示。它显示了我们需要的展示的RSS feed的列表。在接下来的第二页中,我们显示的是所选中的RSS feed所包含的所有每个entry的列表。最后一个页面显示的是每个entry的具体的内容。

现在我们开始在index.html中加入我们先前已经定义好的“main”的页面:

1)在index.html中加入如下的行到javascript的插入列表中:

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

这个对于我们使用列表来说是必须的。在“main”页面中,我们也加入称作为“yourfeeds”的list

 <section data-role="list" id="yourfeeds"></section>

2)在完成好我们上面的代码后,我们接下来主要的代码将在js/app.js文件中。我们可以是使用外部的库来帮我们完成我们的工作。在js/app.js文件中,我们在推入“main”页面过后,即如下的行:

 UI.pagestack.push("main");

加入如下的代码:

 if (typeof localStorage["feeds"] == "undefined") {
       restoreDefault();
   }
   //load local storage feeds
   var feeds = eval(localStorage["feeds"]);
   if (feeds !== null) {
      var feeds_list = UI.list('#yourfeeds');
      feeds_list.removeAllItems();
      feeds_list.setHeader('My feeds');

       for (var i = 0; i < feeds.length; i++) {
           feeds_list.append(feeds[i],
                            null,
                            null,
                            function (target, thisfeed) { loadFeed(thisfeed); },
                            feeds[i]);
       }
   }

我们使用localStorage来存储我们所需要的feeds。如果最早没有被定义,就是“underdefined”,我们将调用restoreDefault()(在下面的部分来实现)来初始化我们所需要的feeds。如果在localStorage中已经有我们所需要的feeds,我们先清除“yourfeeds”列表中的内容,并把list的header设为“My feeds”。每当列表中的某一项被点击,我们就调用loadFeed()(在下面的代码中来实现)来下载这个feed。


3)在js/app.js文件的最后部分加入如下的代码:

function restoreDefault() {
   localStorage.clear();
   var feeds = [];
   feeds.push("http://daker.me/feed.xml");
   feeds.push("http://www.omgubuntu.co.uk/feed");
   feeds.push("http://hespress.com/feed/index.rss");
   feeds.push("http://rss.slashdot.org/Slashdot/slashdot");
   feeds.push("http://www.reddit.com/.rss");
   feeds.push("http://www.guokr.com/rss/");

   try {
       localStorage.setItem("feeds", JSON.stringify(feeds));
       window.location.reload();
   } catch (e) {
       if (e == QUOTA_EXCEEDED_ERR) {
           console.log("Error: Local Storage limit exceeds.");
       } else {
           console.log("Error: Saving to local storage.");
       }
   }
}

在这里,我们定义了restoreDefault()函数。它首先清除localStorage的数据,并加入一个JSON格式的RSS feed到localStorage中。代码中也同时加入了一些exception处理。

我再次运行我们的应用,当我们点击我们的列表中的每一项时,没有任何的反应。这是因为我们根本就没有实现loadFeed函数。




到目前为止的素有代码在如下的地址可以下载:

git clone https://gitcafe.com/ubuntu/html-rssreader2.git


7)从Internet下载内容


到目前为止,我们的代码并不复杂。互联网上有很多东西是值得我们借鉴和利用的。事实上,你可以从互联网上借鉴使用任何你所想要的库来完成你所需要的工作。在今天的练习中,我们借鉴jquery来完成我们的一部分工作。

1)打开一个Teminal, 并进入到js/app.js所在的目录中,在它里面打入如下的命令:

cp /usr/share/javascript/jquery/jquery.min.js .

这里把Desktop中的jquery考入到我们的应用中,并成为应用的一部分。


我们需要一个RSS的parser。虽然google也有一个自己的API,但是由于一些原因这个API在中国并不能被使用。我们在Terminal中打入如下的命令:

git clone https://github.com/jfhovinne/jFeed.git

等我们下载完所有的源码后,我们进入目录:jFeed/build/dist,并把文件“jquery.jfeed.pack.js”拷入到我们的js/app.js目录中。


3)在index.html文件中,在<head>加入如下的语句:

    <!-- External javascript imports -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.jfeed.pack.js"></script>

在“main”页面下,加入另外一个叫做“results”的页面:

        <div data-role="content">
            <div data-role="pagestack">
                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                 </div>
                 <div data-role="page" id="results" data-title="Articles >">
                    <section data-role="list" id="resultscontent"></section>
                 </div>
            </div>
            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
        </div>

这里,我们也加入了一个等待的“loading”对话框。在下载数据时,显示“loading”字样。

4)现在,我们更进一步在js/app.js文件中做一些修改,我们首先把UbuntuUI变成一个全球的变量,把如下的代码:

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

改为:

var UI = new UbuntuUI();

$(document).ready(function () {

我们同时也需要在函数的最后把};修改为});。这样整个代码为:

$(document).ready(function () {
    UI.init();
    UI.pagestack.push("main");

    if (typeof localStorage["feeds"] == "undefined") {
        restoreDefault();
    }

    //load local storage feeds
    var feeds = eval(localStorage["feeds"]);
    if (feeds !== null) {
        var feeds_list = UI.list('#yourfeeds');
        feeds_list.removeAllItems();
        feeds_list.setHeader('My feeds');

        for (var i = 0; i < feeds.length; i++) {
            feeds_list.append(feeds[i],
                              null,
                              null,
                              function (target, thisfeed) { loadFeed(thisfeed); },
                              feeds[i]);
        }
    }
});


5)现在我们来完成loadFeed方法的实现。在js/app.js的末尾,我们加入如下的代码:

function loadFeed(url) {
   UI.pagestack.push("results");
   UI.dialog("loading").show()

    console.log("url is: " + url );

    $.getFeed( {
                  url: url,
                  success: function(feed) {
                      UI.dialog("loading").hide();

                      var results_list = UI.list('#resultscontent');
                      results_list.removeAllItems();
                      results_list.setHeader(feed.title);

                      console.log("title: " + feed.title);

                      // walk through the feeds
                      for( var i = 0; i < feed.items.length; i++ ) {
                          var item = feed.items[ i ];

//                          console.log("title: " + item.title);
//                          console.log("link: " +  item.link);
//                          console.log("content: " + item.description);

                            results_list.append(
                                item.title.replace(/"/g, "'"),
                                null,
                                null,
                                function (target, result_infos) {
                                  showArticle.apply(null, result_infos); },
                                [ escape(item.title),
                                  escape(item.link),
                                  escape(item.description) ] );
                      }
                    }

    });
}

重新运行我们的应用,我们可以点击我们的每个rss feed,并看到每个rss feed的标题列表:

  

所有的源码在如下的地址找到:

git clone https://gitcafe.com/ubuntu/html-rssreader3.git


8)显示实际的文章


如果细心的开发者仔细看了我们上一节的代码后,你可能会发现,我们已经提到了“showArticle”,但是我们还是没有实现它。当我们点击RSS feed的标题后,没有任何的东西显示。它的实现如下:

1)在index.html中,加入“article”页面:

        <div data-role="content">
            <div data-role="pagestack">
                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                 </div>
                 <div data-role="page" id="results" data-title="Articles >">
                    <section data-role="list" id="resultscontent"></section>
                 </div>
                 <div data-role="page" id="article" data-title="Article >">
                     <section id="articleinfo"></section>
                 </div>
            </div>
            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
        </div>

2)在js/app.js文件的最后面,加入如下的方法:

function showArticle(title, url, desc) {
   UI.pagestack.push("article");

   if (typeof desc == "undefined")
       desc = "(No description provided)";
   $("#articleinfo").html("<p>" + unescape(title) + "</p><p>" + unescape(desc) + "</p><p><a target=\"_blank\" href=\"" + unescape(url) + "\">" + unescape(url) + "</a></p>");

}


这样我们就可以得到文章的标题及其内容。为了展示文章的内容,我们显示文章的title,内容及一个url。为了展示文章的内容及title,我们使用“unescaped”。

重新运行我们的应用,我们可以看到如下的画面:



整个项目的源码在:

git clone https://gitcafe.com/ubuntu/html-rssreader4.git


9)添加RSS feed


现在我们的RSS feed都是固定,预先设定好的。如果一个用户需要添加一个新的RSS feed该怎么办呢?

1)在index.html文件中,在javascript的import区域,加入如下的代码:

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

2)在main页面中加入如下的footer:

                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                     <footer id="footer1" data-role="footer" class="revealed">
                         <nav>
                             <ul>
                                 <li>
                                     <a href="#" id="addfeed">
                                         <img src="/usr/share/ubuntu-html5-ui-toolkit/0.1//ambiance/img/actions/add.svg" alt="Add feed" />
                                         <span>Add feed</span>
                                     </a>
                                 </li>
                             </ul>
                         </nav>
                     </footer>                     
                 </div>

我们在footer中加入了一个toolbar以添加一个RSS feed。

3)我们添加一个对话框来输入所需要的RSS feed的详细信息:

在“loading”对话框的前面或后面,添加对话框:

            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
            <div data-role="dialog" id="addfeeddialog">
             <section>
                 <h1>Add a new feed</h1>
                 <p>Type the url feed you want to add</p>
                 <input type="url" id="rssFeed" placeholder="http://">
                 <menu>
                     <button data-role="button" id="no">Cancel</button>
                     <button data-role="button" class="success" id="yes">Add</button>
                 </menu>
             </section>

这里我们定义了一个叫做“addfeeddialog”的对话框。它让用户来输入一个RSS feed的url。


4)在如下函数:

$(document).ready(function () {

结束的前一行,添加如下的代码:

   UI.button('yes').click(function (e) {
       var url = $("#rssFeed").val();
       if (url !== "") {
           var feeds = eval(localStorage["feeds"]);
           feeds.push(url);
           localStorage.setItem("feeds", JSON.stringify(feeds));
           window.location.reload();
       }
   });

   UI.button('addfeed').click(function () {
       $('#addfeeddialog').show();
   });

重新运行我们的应用,并点击“Add feed”按钮

  

添加一个RSS feed是不是在弹指之间呢?所有的源码在:

git clone https://gitcafe.com/ubuntu/html-rssreader5.git


10)你的挑战


我们现在对话框的“Cancel”还不能工作,你可以帮我完成它的代码吗?


11)美化我们的应用


在这个章节里,我们将通过使用css来达到美化我们的应用的目的。


在css/app.css文件中,我们加入如下的代码:

#articleinfo {
   padding: 10px;
   -webkit-box-sizing: border-box;
   box-sizing: border-box;
}

#articleinfo iframe {
   max-width: 100%;
}

#articleinfo p {
   margin: 7px 0;
}

#articleinfo a{
   text-decoration: none;
   color: #787878;
   font-weight: bold;
}

重新运行我们的应用,我们可以看到Article显示得更加漂亮。到目前为止,所有的源码在如下的地址可以找到:

git clone https://gitcafe.com/ubuntu/html-rssreader6.git

如果大家有任何的问题,欢迎在文章的评论中提出来。







作者:UbuntuTouch 发表于2015/3/3 14:39:17 原文链接
阅读:263 评论:4 查看评论

Read more
UbuntuTouch

目前在Ubuntu提供了丰富的开发途径供开发者进行开发。它覆盖了从本地开发到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的本地开发语言。使用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上面也可以使用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 原文链接
阅读:154 评论:0 查看评论

Read more
UbuntuTouch

[原]怎么在Virtualbox下安装Ubuntu OS

在这篇文章中,我们来介绍如何安装VirtualBox及在VirtualBox下面安装最新的Ubuntu操作系统。


1)下载VirtualBox文件

首先我们到网站 https://www.virtualbox.org/wiki/Downloads下载最新的virtualbox。



根据自己的操作系统,在该网站上下载两样东西:

  • VirtualBox platform packages
  • VirtualBox 4.3.16 Oracle VM VirtualBox Extension Pack 
你可以永远下载最新的版本。

2)安装VirtualBox

我们在Ubuntu OS或Windows下,双击刚刚下载的VirtualBox文件,系统会自动帮我们调用软件帮我们安装。在Ubuntu OS下的界面如下:



至此,我们已经完成了对VirtualBox的安装。我们可以在Ubuntu中打开dash,找到VirtualBox的图标。在Windows上,我们也可以通过类似的方法找到相应的图标:



点击VirtualBox图标,我们可以看到如下的画面:



3) 下载Ubuntu Desktop Image

由于在Ubuntu for phone的开发中,我们将使用Utopic (14.10)来进行开发。我们在网址http://cdimage.ubuntu.com/daily-live/current/下载适合自己平台的image。这里我们选择utopic-desktop-amd64.iso。我们把image存放于我们电脑中的一个目录中。

4) 在VirtualBox中安装Ubuntu OS

打开"Oracle VM VirtualBox Manager",并点击“New”。可以看到如下的画面:



紧接着,我们根据我们电脑的实际情况给予一定的内存大小。



对于我们首次创建Ubuntu OS来说,我们选择“Create a virtual hard drive now”。如果你曾经创建过,并存有一个virtual hard drive的文件,你可以直接选择“Using an existing virtual hard drive file”。这样创建起来更块。



接下来:



再接下来,我们选择“Dynamically allocated”。



紧接着,我们选择至少有20G的硬盘容量以装载整个OS及SDK的安装:



这样就完成了我们初步的设置。下面我们来真正地来安装我们的Ubuntu OS系统。

点击VirtualBox中的“Start




我们选择我们刚才下载好的image:



再选择“Install Ubuntu



如果网络的情况好,可以在安装的过程中同时下载更新及第三方的软件。











紧接下来,就开始安装Ubuntu OS了:



最终,我们把Ubuntu系统安装好了。




5) 安装VirtualBox Extensions

我们发现当我们把Ubuntu OS安装好后,界面的分辨率是不对的。我们没法把它的分辨率调到我们的主屏幕的分辨率。这时我们需要安装Extensions。



首先,我们关掉Ubuntu OS。打开VirtualBox,并点击菜单中的“Preferences”:



点击"Extensions", 



重新启动我们创建的VM。



然后选择:



如果弹出一个对话框,就点击“运行”(Run)即可。如果没有对话框出现的话,寻找“Terminal



打开“Terminal”应用,并输入如下的命令:



等安装完后,我们重新启动VM。我们就可以看到全屏的Ubuntu系统了。它的分辨率和Host的分辨率应该是一样的。另外,如果在任何时候我们在VM中的系统升级而造成的分辨率下降或共享文件夹不可以工作,我们都可以通过上面的方法重新编译,并使得显示正常。

6) 设置VirtualBox


点击VirtualBox的“Settings”,选择“Display”进行设置。




点击“Shared Folders”,来设置可以在host及guest OS之间进行拷贝文件。



同时,我们在VM中的Ubuntu OS中的“Terminal”键入如下的命令:

$sudo usermod -a -G vboxsf username

这里“username"是用户名。具体操作如下:

 

在VM中打开文件浏览器,即可看见Host中被分享的文件夹。文件名通常会有一个“sf”开始,表示shared folder。




选择“General”,我们可以设置可以在host及guest系统之间进行拷贝。这个对我们有时非常有用。



这样我们基本上对VirtualBox的设置已经完成。当然,你们如果有兴趣的话,可以对更多的选项进行设置。


7) 安装中文输入法

我们知道中文输入法对开发中文的应用是非常有用的。我们可以参照文章“怎么在Ubuntu OS上面安装搜狗输入法及对Qt Creator的支持”进行输入法的设置。

8) 安装Ubuntu SDK

我们可以按照“Ubuntu SDK 安装”来安装我们的SDK来进行我们的Ubuntu应用开发了。


作者:UbuntuTouch 发表于2014-9-24 13:34:18 原文链接
阅读:322 评论:0 查看评论

Read more
UbuntuTouch

当我们刷最新的Ubuntu手机软件时,我们必须打开开发者模式。否则,当我们使用"adb"命令时,会出现如下的情况:




当手机和电脑连接后,不能查看到任何的device。当我们打开开发者模式后,我们可以看到如下的画面:



这里我们可以看到,我们通过"adb"命令可以看到连接到的device。


为了打开开发者模式,我们可以通过如下的步骤来操作:

1)打开“系统设置”应用




2) 选中“关于此手机”




3)点击“开发者模式”




4) 打开开关




一旦完成开发者模式的设置,我们就可以对手机进行部署了。

我们也可以通过“adb shell"命令对手机的文件系统进行操作




作者:UbuntuTouch 发表于2014-9-23 10:06:47 原文链接
阅读:388 评论:0 查看评论

Read more
UbuntuTouch

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

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

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

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

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


事实上,在Ubuntu OS上,我们可以可以使用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 原文链接
阅读:70 评论:0 查看评论

Read more
UbuntuTouch

在Ubuntu平台上面,我们可以使用History API来读取电话本及短信的内容,并使用listview来显示出来。下面我们来展示该怎么做。


1)创建一个基本的QML应用

我们使用Qt Creator来创建一个最基本的QML应用。我们选择使用“App with Simple UI"模版来创建我们的应用。





为了使用History API, 我们必须引入

import Ubuntu.History 0.1

为了能够使我们读取不同的history,我们先来做一个ComboButton。它的设计如下:

            ComboButton {
                id: type
                expanded: false

                text: "Voice"
                ListView {
                    model: typemodel
                    delegate: ListItem.Standard {
                        text: modelData

                        onClicked: {
                            console.log("item is clicked!" + index + " " + name);
                            type.expanded = false;
                            type.text = text;

                            console.log("type is: " + type.text);

                            if ( name === "Voice") {
                                historyEventModel.type = HistoryThreadModel.EventTypeVoice;
                                listView.model = historyEventModel
                            } else if ( name === "Text" ) {
                                historyEventModel.type = HistoryThreadModel.EventTypeText;
                                listView.model = historyEventModel
                            } else if ( name === "Thread") {
                                listView.model = historyThreadModel
                            }

                        }
                    }
                }
            }
这里我们可以参考ComboButton来更加多了解如何使用这个控件。这里,我们提供了三个选项"Voice", "Text" 及“Thread"。同时我们也创建了俩个不同的history model。

    HistoryEventModel {
        id: historyEventModel
        filter: HistoryFilter {}
        type: HistoryThreadModel.EventTypeVoice
        sort: HistorySort {
            sortField: "timestamp"
            sortOrder: HistorySort.DescendingOrder
        }
    }

    SortProxyModel {
        id: sortProxy
        sortRole: HistoryEventModel.TimestampRole
        sourceModel: historyEventModel
        ascending: false
    }

    HistoryThreadModel {
        id: historyThreadModel        
	filter: HistoryFilter {}
 sort: HistorySort { sortField: "lastEventTimestamp" sortOrder: HistorySort.DescendingOrder 
        } 


由于一些性能方面的原因的考虑,目前我们必须把filter设置为空,即:
filter: HistoryFilter {}

我们同时也把上面定义的model和我们的ListView一起连接起来:

            ListView {
                id: listView

                width: parent.width
                height: parent.height - type.height

                Component {
                    id: sectionDelegate

                    Text {
                        text: section
                    }
                }

                model: historyEventModel

               delegate:
               ...
            }

目前似乎一切都已经好了。接下来我们来运行我们的应用到手机中。我们会发现在手机中没有任何的history的东西显示。问题出现在哪里呢?



我们开启一个terminal,然后打入如下的命令:

$adb shell

然后再在手机中的命令行输入:

root@ubuntu-phablet:~# grep "DENIED" /var/log/syslog 

我们可以看到如下的图片:



显然,我们遇到了一个安全的问题。为了接的这个问题,我们必须在应用中加入它所需要的policy。



我们再重新运行程序。在手机上,我们可以看到如下的画面:




我们再也没有security的问题了。整个工程的源码在如下的地址可以下载:

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





作者:UbuntuTouch 发表于2014-9-4 12:05:55 原文链接
阅读:148 评论:0 查看评论

Read more
UbuntuTouch

QML中的Loader是用来动态地载入一个QML的Component。它可以用来载入一个QML文件(使用它的source属性)。它也可以载入一个Component(使用它的sourceComponent属性)。它适合在需要载入一个Component时才载入它,这样避免资源的浪费。它可以动态地载入按需求在需要的时候创建我们需要的Component。更多阅读,可以参照:http://qt-project.org/doc/qt-4.8/qml-loader.html


1)动态载入一个Component


我们首先来创建我们一个基本的应用。这里我们使用一个"App with Simple UI"的模版。我们首先创建一个称作为"filearray.js"的javascript文件,如下:

var filearray = [
                    ["images/fall1.jpg", "First image"],
                    ["images/fall2.jpg", "Second image"],
                    ["images/fall3.jpg", "Three image"],
                    ["images/fall4.jpg", "Fourth image"]
                ];

这里创建了一个二维的数组。为了我们能够在应用中使用。同时,我们在"main.qml"中也创建一个Component。这样它可以在我们的应用中动态地产生。代码如下:

        Component {
            id: imageText

            Rectangle {
                id:top
                anchors.fill: parent
                Image {
                    id:innerImage
                    anchors.top: parent.top
                    anchors.topMargin:30
                    anchors.horizontalCenter: parent.horizontalCenter
                    width:parent.width*0.8;
                    height: parent.height*0.8
                    source:root.currentImage

                }
                Text{
                    id:answer
                    anchors.top:innerImage.bottom
                    anchors.topMargin:30
                    horizontalAlignment: Text.AlignHCenter
                    width:parent.width;
                    text:root.currentText
                }
            }

       
这个Component非常简单。上面使用了一个Image,下面是一个Text。这两个item的内容是从root控件中的两个property中获得。我们希望这root中的这两个property改变时他们的内容也可以改变。

   Page {
        id: root;
        title: i18n.tr("QML Loader")
        property int index: 0
        property string currentImage
        property string currentText:" "

同时,为了说明问题,我们也设计了一个timer。当这个timer每次timeout时,我们希望我们的loader:

        Loader {
            id: loader
            anchors.fill: parent
            anchors.centerIn:parent
        }

的"sourceComponent"每次都能发生改变,以使得UI得到变化。这里有趣的是,我们也可以为Loader定义它的大小:

  • 如果没有定义Loader的大小的话,Loader将会自动地适配到Component的大小尺寸(当component完成装载以后)
  • 如果Loader的大小已经被定义的话,当component完成装载后,component的尺寸将自动被适配到Loader的尺寸
    function timeout() {
        console.log("root.index" + root.index);
        console.log(FileExt.filearray[root.index][0]);
        root.currentImage = FileExt.filearray[root.index][0];
        root.currentText = FileExt.filearray[root.index][1];
        loader.sourceComponent = imageText;

        root.index++;
        if ( root.index === FileExt.filearray.length) {
            root.index = 0;
        }
    }

        Timer {
            id: timer
            interval: 3000
            onTriggered: {
                timeout();
            }
            repeat: true
            triggeredOnStart: true
        }

运行的效果图如下:



我们可以看到画面中的每三次改变一次。每一次都是一个新的Component,而不是同一个Component不变,只是其中的属性发生改变。Loader很适用于在不同的场景中装载不同的component,比如在不同的菜单中,装载不同的component,以显示不同的UI。Qt SDK很多的例程就是这样写的!

整个例程的代码在如下地址找到:

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

2)使用Loader载入qml文件

在下面的例程中,我们来完成一个使用Loader来载入qml文件。我们来做一个类似wizard的东西。当第一个页面显示完后,紧接着按下一个按钮,进入下一个页面。我们首先来创建一个简单的"App with Simple UI"的模版应用,然后,修改main.qml文件:

import QtQuick 2.0
import Ubuntu.Components 1.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.wizard"


    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

    Page {
        id: root
        title: i18n.tr("Wizard")

        Loader {
            z: 1
            id: main
            anchors.fill: parent
        }

        Image {
            source: "images/fall1.jpg"
            anchors.fill: parent
        }

        Button {
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 20
            anchors.rightMargin: 20


            text: "Go to Page 1"
            onClicked: {
                main.source = "Page1.qml"
                console.log("Clicked in main")
            }
        }

        Connections {
            target:main.item
            onHandlerLoader:{
                console.log("Something happened!")
            }
        }
    }
}


这里我们定义了一个叫做main的Loader。当我们点击按钮”Go to Page 1"时,我们使用它来装载另外一个页面“Page1.qml"。注意我们在这里设置它的"z" order值为"1”。同时,我们也可以通过如下的Connections来接受来自main Loader的一些signal来做我们所需要的一些处理。
        Connections {
            target:main.item
            onHandlerLoader:{
                console.log("Something happened!")
            }
        }

在我们的Page1.qml文件中,我们设计如下:

import QtQuick 2.0
import Ubuntu.Components 1.1

Rectangle {
    id:page1
    anchors.fill: parent

    signal handlerLoader;

    Loader{
        z: 2
        id:loader
        anchors.fill: parent
    }
    
    Image {
        source: "images/fall2.jpg"
        anchors.fill: parent
    }

    Button {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 20
        anchors.rightMargin: 20

        text: "Go to Page 2"
        onClicked: {
            loader.source = "Page2.qml"
            handlerLoader();
        }
    }
}

在这里,我们定义了另外一个Loader, 并且设置它的“z” order值为2,使一个画面得它可以覆盖以前的页面。我们也尝试定义了一个signal "handlerLoader"。这样我们可以使得前一个面可以的得到一个响应。我们可以把我们想要的信号通过这样的方式发送出去,让需要对它感兴趣的代码利用它。

运行我们的程序,结果如下:

       

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

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

作者:UbuntuTouch 发表于2014-9-9 14:07:55 原文链接
阅读:171 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章里,我来介绍如何在Ubuntu  OS上上面读取电话本的信息。


1)首先我们来创建一个最基本的应用

打开我们的Qt Creator, 我们来创建一个称作为“contact1"的项目。在本项目中,我们使用"App with Simple UI"的模版。我们修改我们的“main.qml"代码如下:


import QtQuick 2.0
import Ubuntu.Components 0.1
import "components"
import QtContacts 5.0
import Ubuntu.Components.ListItems 0.1 as ListItem

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

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

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

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

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

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

        ContactModel {
            id: contactModel

            manager: "galera"
        }

        ListView {
            id: contactView

            anchors {
                left: parent.left
                right: parent.right
                top: parent.top
                bottom: buttons.bottom
            }

            model: contactModel

            delegate: ListItem.Subtitled {
                text: contact.name.firstName
                subText: contact.phoneNumber.number
            }
        }
    }
}

这里我们首先输入我们的QtContact 5.0库。在代码中我们定义了如下的ContactModel:

        ContactModel {
            id: contactModel
            manager: "galera"
        }

一定要记住使用"galera" manager。最后,我们使用一个ListView来展示我们的内容。代码显示如上所示。但是我们现在马上在手机上运行时,可能看到如下的信息:

Fail to connect with service: QDBusError("org.freedesktop.DBus.Error.AccessDenied", "An AppArmor policy prevents this sender from sending this message to this recipient, 0 matched rules; type="method_call", sender=":1.119" (uid=32011 pid=20604 comm="/usr/lib/arm-linux-gnueabihf/qt5/bin/qmlscene $@ m") interface="org.freedesktop.DBus.Introspectable" member="Introspect" error name="(unset)" destination="com.canonical.pim" (uid=32011 pid=3057 comm="/usr/lib/arm-linux-gnueabihf/address-book-service/")")

这说明,我们的程序遇到了安全的问题。我们必须在我们应用的apparmor文件中加入相应的policy以来使得我们的读取是可行的。

==ou

加入"Contacts"policy后,我们再在手机中运行,可以看到如下的画面:



我们可以在如下的地址下载我们的代码:

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

2)读取favorite contact信息


我们在我们的应用中定义如下的以个favorite model

        ContactModel {
            id: favouritesContactsModel

            manager: "galera"
            sortOrders: [
                SortOrder {
                    id: sortOrder

                    detail: ContactDetail.Name
                    field: Name.FirstName
                    direction: Qt.AscendingOrder
                }
            ]

            fetchHint: FetchHint {
                detailTypesHint: [ContactDetail.Avatar,
                    ContactDetail.Name,
                    ContactDetail.PhoneNumber]
            }

            filter: DetailFilter {
                id: favouritesFilter

                detail: ContactDetail.Favorite
                field: Favorite.Favorite
                value: true
                matchFlags: DetailFilter.MatchExactly
            }

            onErrorChanged: {
                if (error) {
                    busyIndicator.busy = false
                    contactListView.error(error)
                }
            }
        }

我们在手机上使一个contact成为favorite。这时在我们的ListView中使用我们的favorite Model。代码如下:

        ListView {
            id: contactView
            anchors.fill:parent

            model: root.showFavourites ? favouritesContactsModel : contactModel

            delegate: ListItem.Subtitled {
                text: contact.name.firstName
                subText: contact.phoneNumber.number
            }
        }
我们可以看到我们的一个被设为favorite的contact被列举出来了。



代码在如下的地址可以下载:

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





作者:UbuntuTouch 发表于2014-9-9 9:01:03 原文链接
阅读:136 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章里,我们将学习如何使用QML的Qt.createComponent来动态生成我们所需要的Component。这是一个有趣的练习。我希望大家能跟随我一步一步地完成这个练习。最终使得大家对QML应用有更多的认识。这篇文章中我们也将使用我们的sensor来完成我们其中的一部分功能。在练习之前请大家先去按照安装Ubuntu SDK来安装好我们的环境。


1)使用Qt Creator创建一个最基本的应用


我们首先选择一个“App with Simple UI"的模版来创建我们的最基本的应用。在创建应用的时候,由于应用的package名字中不能出现大写的字母,所以我们选择使用小写的“balloon"来作为我们的工程的名字:



紧接着,我们填入所需要的信息来完成我们的应用。



把应用的大小设为如下的值:

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


运行我们的应用:



我们看到,这个应用没有什么太多的内容。我们可以尝试点击按钮,然后看见方框中的文字发生改变。

2)添加Balloon Component

我们按照如下的图,用右键点击项目"balloon",并选择“Add New"。



我们选择创建一个叫做“Balloon.qml”的文件。记住第一个字母为大写的字母。



至此我们已经创建了一个名字叫做"Balloon.qml"的component,虽然现在它做不了什么。为了测试我们刚刚已经创建好的Balloon Component,我们把我们的Balloon在main.qml中创建出来。现在我们来修改main.qml文件:

import QtQuick 2.0
import Ubuntu.Components 1.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.balloon"

    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

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

        Balloon {
            width: parent.width/3
            height: parent.height/3
            x: (parent.width - width) / 2
            y: (parent.height - height) /2
        }
    }
}

这里我们把Balloon放到了我们主界面的正中央的位置。重新运行我们的程序,我们可以看到:



显然,我们可以看到,我们的Balloon component里没有任何的东西。只是一个白色方框在那里。但是,至少,我们可以看到确实Balloon已经能被正确地调用。在下面的章节中,我们来一步一步地完成我们的Balloon的功能。

3)完成Balloon模块

请到地址https://github.com/liu-xiao-guo/balloon下载应用,并拷贝应用的"images"目录到我们已经创建好的项目中(别的文件不要拷贝)。这样使得我们的应用有一个叫做"images"的目录,里面有我们想要的图片。



打开我们的Balloon.qml文件。我们知道Balloon component其实是一个Image的item。我们首先把它设计为如下:

import QtQuick 2.0

Image {
    id: balloon
    width: 100
    height: 250

    source: "images/red.svg"
}

我们运行程序,我们发现它的结果如下:



显然,我们已经看到了我们所需要看到的气球了。只是它的颜色是固定的红色。我们想显示不同的气球。这时我们需要加入一个颜色的属性。我们的Balloon.qml的代码如下:

Image {
    id: balloon
    width: 100
    height: 250
    property string color: "red"

    source: "images/" + color + ".svg"
}

默认的颜色我红色(在没有定义的情况下)。我们可以尝试改变我们的main.qml文件。加入color属性:

        Balloon {
            color: "green"
            width: parent.width/3
            height: parent.height/3
            x: (parent.width - width) / 2
            y: (parent.height - height) /2
        }

再重新运行应用:



我们看见了一个绿色的气球。显然它的颜色属性是起作用的。为了能够使我们拖动鼠标移动气球,我们可以在Balloon.qml的Image中加入如下的代码:

    MouseArea {
        x: 0; y: parent.height/2
        width: parent.width
        height: parent.height/2

        drag.target: parent
        drag.axis: "XandYAxis"
    }

再重新运行应用。我们尝试用鼠标点击气球的下半部,并移动气球。我们可以看到气球随着鼠标的移动而移动。下面,我们想在点击气球的上半部分时,气球发生爆炸。为了这样做,我们必须定义另外一个MouseArea来扑捉这个事件。当我们点击气球的上半部时会发出一个爆破声。

    MouseArea {
        x: 0; y: 0
        width: parent.width
        height: parent.height/2

        onClicked: {
            balloon.state = "exploded";
            player.play();
        }
    }

为了我们能够听到一个声音,我们也同时定义了一个MediaPlayer。同时记得把刚下载好的程序中的"sounds"目录考到我们的项目中,并处于和“images”相同的目录中。

import QtMultimedia 5.0

Image {
    ...

    MediaPlayer {
        id: player
        source: "sounds/blast.wav"
    }

   ...
}

为了能够使得我们的应用能够在手机上播放出声音,我们必须为它加入我们所需要的security policy。为此,我们必须修改项目的“balloon.apparmor”文件:



有了“Audio”的policy,我们就可以在我们的手机上听到一声“砰”的声音(在气球爆炸的时候)。

这里我们把Image的id定义为"balloon"。当点击气球的上半部时,我们同时设置balloon的状态为“exploded”状态。我们知道,QML设计中可以设置component为不同的状态。在不同的状态中,可以定义component中各个item的不同属性的值。默认的状态为"",即空串。我们定义的状态如下:

    states: [
        State {
            name: "exploded"

            StateChangeScript {
                script: {
 //                   particle.running = true;
                }
            }

            PropertyChanges {
                target: balloon
                visible: false
                source: "images/" + color + "_exploded.png"
            }

            PropertyChanges {
                target: balloon
                opacity: 0
            }

            PropertyChanges {
                target: balloon
                scale: 0
            }

 //           StateChangeScript { script: balloon.destroy(1000); }
        }
    ]

这时我们重新运行我们的应用,我们会发现我们可以点击球的上半部,并听到一声爆破声。随后球就消失了。我们的目的达到了,但是,还不是我们最终想要的。这是因为从气球点击到消失,速度太快了。我们机会没有看到任何的中间过程。为了我们能够看到气球是怎么爆炸的,我们必须使用一个叫做 transition的。

    transitions: [
        Transition {
            to: "exploded"
            SequentialAnimation {

                // Disappear
                NumberAnimation { target: balloon; property: "opacity"
                    to: 0; duration: 800 }
                NumberAnimation { target: balloon; property: "scale"
                    to: 0; duration: 800 }

                PropertyAction { target: balloon; property: "source"
                    value:  {
                        if ( !surprise )
                            "images/" + color + "_exploded.png"
                        else
                            "images/flower.png";
                    }
                }

                NumberAnimation { target: balloon; property: "opacity"
                    to: 1; duration: 300 }
                NumberAnimation { target: balloon; property: "scale"
                    to: 1; duration: 300 }

                PauseAnimation {
                    duration: {
                        if (surprise)
                            2000
                        else
                            800
                    }
                }

                PropertyAction { target: balloon; property: "visible"
                    value: "false"}
            }
        }
    ]

有了这个我们可以看到气球是逐渐消失的。我们在Image中也加入了如下的属性。当这个气球是一个surprise时,我们会显示一朵花:

Image {
    id: balloon
    width: 100
    height: 250
    property string color: "red"
    property bool surprise: true
    ....
}

为了达到更加逼真的效果,我也为我们的Balloon加入了一个烟花的效果:

import QtQuick.Particles 2.0
...
Image {
    ...
ParticleSystem {
        id: particle
        anchors.fill: parent
        running: false

        Emitter {
            group: "stars"
            emitRate: 800
            lifeSpan: 2400
            size: 24
            sizeVariation: 8
            anchors.fill: parent
        }

        ImageParticle {
            anchors.fill: parent
            source: "qrc:///particleresources/star.png"
            alpha: 0
            alphaVariation: 0.2
            colorVariation: 1.0
        }

        Emitter {
            anchors.centerIn: parent
            emitRate: 400
            lifeSpan: 2400
            size: 20 // 48
            sizeVariation: 8
            velocity: AngleDirection {angleVariation: 180; magnitude: 60}
        }

        Turbulence {
            anchors.fill: parent
            strength: 2
        }
    }

...
}

并在state变化时让它运行:

            StateChangeScript {
                script: {
                    particle.running = true;
                }
            }

运行应用,效果图如下:

    

至此所有的源码可以在如下的网址下载:


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

由于篇幅的原因。我们将在下一篇文章中详细介绍怎么动态创建很多个Balloon的。大家敬请期待!











            
作者:UbuntuTouch 发表于2014-9-10 11:41:52 原文链接
阅读:237 评论:0 查看评论

Read more
UbuntuTouch

上一篇文章中,我们已经生成了我们Balloon component了。现在我们来让大家怎么来动态生成很多的气球。


4)更进一步完成我们的Balloon component


为了使得我们的Balloon更加像现实生活中的气球,我们来给Balloon给予更多的属性:

    property int x1
    property int y1
    property int speed
    property bool created: false

这里x1, y1是我们让气球飞到一个目的地时的终点位置。我们可以使用如下的语句使得它具有动画的效果:

   NumberAnimation on y {
        easing.type: Easing.InOutQuad; to: y1; duration: speed
        running: created
    }

    NumberAnimation on x {
        easing.type: Easing.InOutQuad; to: x1; duration: speed
        running: created
    }

无论x, 或是y变化时,我们都做一个动画,用speed定义的时间来完成。这个动画只有在“created"为真时才起作用。这是为了能够保证我们的动画只有在Balloon被动态生成完成后才可以产生动作。“created"在我们的程序设计中,只有被动态生成时才设计为true。为了能够destroy我们动态生成的Ballloon,我们也在Transition中的部分做了如下的修改:

    transitions: [
        Transition {
            to: "exploded"
            SequentialAnimation {
                NumberAnimation { target: balloon; property: "opacity"
                    to: 0; duration: 800 }

                NumberAnimation { target: balloon; property: "scale"
                    to: 0; duration: 800 }

                PropertyAction { target: balloon; property: "source"
                    value:  {
                        if ( surprise )
                            "images/flower.png";
                        else
                            ""
                    }
                }

                NumberAnimation { target: balloon; property: "opacity"
                    to: 1; duration: 300 }
                NumberAnimation { target: balloon; property: "scale"
                    to: 1; duration: 300
                }

                PauseAnimation {
                    duration: {
                        if (surprise)
                            400
                        else
                            200
                    }
                }

                PropertyAction { target: balloon; property: "visible"
                   value: "false"}
            }
        }
    ]

5)动态生成QML Component

为了动态生成Balloon, 我们在主界面里加入一个Button。这个按钮可以帮我们生成所需要的Balloon。我们同时也为我们的主界面加上一个天空的背景:

    Page {
        id:main
        title: i18n.tr("Balloon")
        property int time: 2000
        property int rotateVal: 0

        Image {
            anchors.fill:parent
            source: "images/sky.jpg"
        }

        Balloon {
            id: red
            x: main.width/2
            y: main.height - 60
            rotation: main.rotateVal
            color: "red"
            y1: main.height/6
            x1: 0
            speed: main.time/2
            created: true
            surprise: true
        }

        Balloon {
            id: blue
            x: main.width / 3 - 60
            y: main.height - 60
            color: "blue"
            rotation: main.rotateVal
            y1: main.height/4
            x1: main.width/2 + 20
            speed: main.time/2
            created: true
        }

        Balloon {
            id: green
            x: main.width*2/3
            y: main.height - 10
            rotation: main.rotateVal
            color: "green"
            y1: main.height/5
            x1: main.width/3 + 20
            speed: main.time/3
            created: true
            surprise: true
        }

        Button {
            z: 2
            id: restartButton

            anchors.bottom: parent.bottom
            anchors.right: parent.right
            anchors.bottomMargin: 10
            anchors.rightMargin: 10

            width: 100
            height: 40
            text: "Add"
            onClicked: {
                var x = Math.random() * main.width
                var y = main.height - 60
                var colors = new Array("red","blue","green");

                var date = new Date()
                // Use miliseconds avoids the same random secuece
                // generation among calls
                var mils = date.getMilliseconds()
                var index = Math.floor((Math.random()*mils)%3)

                var obj = Logic.createBalloon( Math.floor(x), Math.floor(y),
                                              colors[index] )
            }
        }
    }

在这里,我们使用了一个天空的Image背景。同时也创建了三个不同颜色的气球。同时,我们也加入了一个按钮“Add”用来添加动态生成的气球。为了能够使用上面的“Logic”,我们也创建了一个叫做“logic.js”的文件,处于和"main.qml"同一个目录中。它的内容如下:

var component;

function addBinding (from, toObj, toProp)
{
    var bindObj = Qt.createQmlObject("import QtQuick 2.0; Binding {value:"+from+"}", main)
    if (bindObj) {
        bindObj.target = toObj
        bindObj.property = toProp
    }
    else {
        console.log("error creating binding object")
        console.log(bindObj.errorString())
        return false
    }
    return true
}


function createBalloon(x, y, color) {
    // console.log( "Color:" + color)
    if (component == null)
        component = Qt.createComponent("Balloon.qml");

    // Note that if Block.qml was not a local file, component.status would be
    // Loading and we should wait for the component's statusChanged() signal to
    // know when the file is downloaded and ready before calling createObject().
    if (component.status === Component.Ready) {
        var dynamicObject = component.createObject(main);
        if (dynamicObject === null) {
            console.log("error creating block");
            console.log(component.errorString());
            return false;
        }

        var xx = main.width*Math.random();
        var xx1 = Math.floor(Math.min(xx, main.width-dynamicObject.width));

        dynamicObject.x = x;
        dynamicObject.y = y;
        dynamicObject.x1 = xx1;
        dynamicObject.y1 = 100 * Math.random();
        dynamicObject.speed = 2000
        dynamicObject.color = color;

        with(Math) {
            var tmp = floor(random() * 10 + 1)
            dynamicObject.surprise = (tmp===10);
        }

        var bindObj = Qt.createQmlObject("import QtQuick 2.0; Binding {value: main.rotateVal}", main);

        if (bindObj) {
            bindObj.target = dynamicObject
            bindObj.property = "rotation"
        }
        else {
            console.log("error creating binding component") ;
            console.log(bindObj.errorString());
            return false;
        }

        // This should be set last
        dynamicObject.created = true;

        // addBinding( "scaleVal", dynamicObject, scale );

    } else {
        console.log("error loading block component");
        console.log(component.errorString());
        dynamicObject = null;
        return null;
    }

    return dynamicObject;
}

function playsound(surprise) {
    if ( surprise ) {
        clapPlayer.play();
    }
    else {
        player.play();
    }
}

这里,我们使用了createBalloon(x, y, color)方法在(x, y)处创建我们所需要颜色的气球。最后为了引用"logic.js",我们必须在main.qml文件的顶头部分使用如下的语句:

import "logic.js" as Logic

这样我们就可以使用这个js文件中的方法了。


    

至此,我们已经基本完成了如何自动动态地生成QML component。全部的代码在如下的地址可以下载:

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

6)加入sensor 到应用中去

接下来,我们想把sensor加入到我们的应用中去。我们想当我们倾斜我们的手机的时候,气球也要随着转动,同时当我们摇动手机的时候,我们希望气球能跟着我们的摇动不断地变大,这样能使得我们的气球直至破裂。为了我们能够使用sensor,我们必须在main.qml中加入如下的库:

import QtSensors 5.0

同时,我们也加入如下的代码:

        Accelerometer {
            id: accel
            active: true
            dataRate: 20

            onReadingChanged: {
                var x = Math.abs(accel.reading.x);
                var y = Math.abs(accel.reading.y);
                var z = Math.abs(accel.reading.z);

                if ( x > main.maxX ) {
                    main.maxX = x;
                }

                if ( y > main.maxY ) {
                    main.maxY = y;
                }

                if ( z > main.maxZ ) {
                    main.maxZ = z;
                }

                if ( x > main.threshold || y > main.threshold || z > main.threshold ) {
                    console.log("x: " + main.maxX + " y: " + main.maxY + " z: " + main.maxZ);
                    var count = main.children.length

                    for(var i=0; i < count; i++) {
                        if ( main.children[i].type !== "balloon" )
                            continue;
                        if ( main.children[i].type === "balloon") {
                            if ( main.children[i].color === "red")
                                main.children[i].scale += 0.1
                            else if ( main.children[i].color === "green" )
                                main.children[i].scale += 0.02
                            else
                                main.children[i].scale += 0.05
                        }

                        if( main.children[i].scale > 2.0 &&
                                main.children[i].state !== "exploded" &&
                                main.children[i].color === "red" &&
                                main.children[i].type === "balloon" ) {
                            main.children[i].state = "exploded"
                        }

                        if( main.children[i].scale > 3.0 &&
                                main.children[i].state !== "exploded" &&
                                main.children[i].color === "green" &&
                                main.children[i].type === "balloon" ) {
                            main.children[i].state = "exploded"
                        }

                        if( main.children[i].scale > 4.0 &&
                                main.children[i].state !== "exploded" &&
                                main.children[i].color === "blue" &&
                                main.children[i].type === "balloon" ) {
                            main.children[i].state = "exploded"
                        }
                    }
                }
            }
        }

        RotationSensor {
            id: rotation
            dataRate: 50
            onReadingChanged: {
                var count = main.children.length

                for ( var i = 0; i < count; i ++ ) {
                    if (main.children[i].type !== "balloon")
                        continue;

                    main.children[i].rotation = -rotation.reading.x;
                    main.children[i].rotation = -rotation.reading.y;
                }

            }
        }

我们使用了加速传感器和旋转传感器。这样在我们摇动的时候,我们就可以使得气球不断地变大。当我们倾斜的时候,气球也可以随着我们的倾斜而倾斜。

最终的代码可以在如下的地址下载:

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





作者:UbuntuTouch 发表于2014-9-10 20:39:32 原文链接
阅读:202 评论:0 查看评论

Read more
UbuntuTouch

Ubuntu SDK对我们大多数开发者来说非常有效。它甚至可以帮我们来进行在线调试。在这篇文章中,我们来介绍如何使用command line来编译并运行我们的scope。


1)创建一个最基本的Scope

我们可以使用Ubuntu SDK来创建一个最基本的Scope。在创建时,我们可以选择任何一个template。我们可以在SDK中试着在Desktop上进行编译运行以确保没有任何问题。



我们创建一个叫做“scopetest”的scope。

2)运用Command Line进行编译及运行


我们首先打开一个Terminal。我们也可以打开已经创建好的template Scope中的readme.txt文件:

This project contains a simple Unity Scope and all files needed to build
and deploy it. Building it is simple. First you need to install the scopes
development package and CMake. Then you go into the source root and type
the following commands.

mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=debug ..
make

我们首先进入Scope所在的目录,并创建一个“build”子目录。

  


  


我们打入如上所述的命令。等编译好没有错误后,我们再进入当前目录下的“src”子目录,并打入如下的命令:

unity-scope-tool com.ubuntu.developer.liu-xiao-guo.scopetest_scopetest.ini 

这样,我们就可以在desktop上运行我们的scope了。


3)安装到手机中


我们可以创建两个如下的scripts文件:

build.sh


build.sh:
#!/bin/bash
click-buddy --arch armhf --framework ubuntu-sdk-14.10 --dir ..

provision.sh

#!/bin/bash
rm -f ../*click
click-buddy --arch armhf --framework ubuntu-sdk-14.10 --dir .. --provision

我们要记得使用如下的命令使得这两个文件变成为可以执行的文件:

$chmod +x build.sh
$chmod +x provision.sh


并同时把这两个文件存于scope项目的上一个目录。对我来说,我存放于“release”目录中。这样当我们处于“build”目录时,




我们也可以同时使用provision.sh来部署scope到手机中



在我们的手机上就可以看到我们已经部署好的scope。我们如果看不到的话,我们可以通过搜索的方式找到我们的scope。


作者:UbuntuTouch 发表于2014-10-31 10:54:57 原文链接
阅读:257 评论:0 查看评论

Read more
UbuntuTouch

[原]使用Ubuntu SDK开发Flickr应用教程

在这篇文章中我们将一步一步地教大家怎么在Ubuntu手机平台来开发一个QML的应用。我们知道QML开发对很多初学者来说并不难。我们需要有一些简单的javascript的基础就可以开始我们的开发了。QML应用的调试也是很方便的。我们通过这个教程的学习,掌握基本的开发流程及界面设计。最终的应用显示的图片如下:

     


如果大家想对这个开发有更多的了解,可以参阅文章“Ubuntu手机应用QML开发 (视频)

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

我们首先打开Ubuntu SDK,并选择“App with Simple UI”模版。

  

这样我们就产生了一个最基本的QML应用。我们可以在desktop上运行它(通过按下Ctrl+R)键或点击SDK左下方的绿色的运行键。我们同时修改main.qml中的尺寸为:

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

这样我们的应用更像手机的长宽比。修改应用的title为:

        title: i18n.tr("Flickr")


应用中有一个按钮,大家可以点击并看看有什么变化。

2)实现一个最基本的列表

回头看看我们的设计,我们需要有一个列表来列出我们的图片。我们这里来使用一个列表的控件来实现它。在Ubuntu SDK的toolkit中,有一个conrtol叫做UbuntuListView。我们可以使用它来设计我们的列表。当然我们也可以使用QML中的ListView。UbuntuListView有一些更多的功能比如PullToRefresh。我们可以在下面用到。我们首先通过如下的方式添加一个文件“PictureListView.qml”到项目中。



    



这样我们就基本上创建了一个叫做“PictureListView.qml”的文件。在这里,它实际上创建一个叫做“PictureListView”的Component。一个Component的名称通常是以大写字母为开始的。通常它是以“Item”为基础的元素。Item在QML中是所有可视元素的最基础的元素。相当于在我们C++或JAVA编程中最基础的一个类。PictureListView.qml的设计如下:

import QtQuick 2.0

Item {
    anchors.fill: parent

}

为了能够在main.qml中调用它,我们修改我们的main.qml如下:

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.flickr"

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

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

    Page {
        id:mainPage
        title: i18n.tr("Flickr")
        clip:true

        PictureListView {
            anchors.fill: parent
        }
    }
}

注意这里的“applicationName”。对于一些应用来说它必须是和应用的包的名称是一样的。因为应用安全的考虑,在使用ContentHub或使用API时创建自己的设置文件,都存在于和这个名称相关的目录里。重新运行运行程序,显示如下。目前我们的应用应该没有做上面东西。代码可以在如下的地址找到:

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

2)完成UbuntuListView

我们再次回顾一下我们最上面的设计。里面是一个ListView。这里我们来使用UbuntuListView来完成该View。我们重新设计我们的PictureListView.qml文件。为了能够显示我们的ListView,我们对PictureListView做了一个简单的展示:

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

Item {
    id: mainScreen

    UbuntuListView {
        id: listView
        width: mainScreen.width
        height:mainScreen.height

        model: 10

        delegate:
            Text {
                text: "Hi"
            }

        Scrollbar {
            flickableItem: listView
        }
    }
}

重新运行程序,我们可以看到显示为10个“Hi”,证明ListView已经是在工作,虽然不是我们所需要的设计。在我们的设计中,我们的ListView的左边是一个图片,右边是一些文字。为了快速设计的方便,我们们将先只显示一行字。同时我们可以在网上的任何一个位置下载一个我们自己喜欢的图片并存放到项目根目录下的一个叫做“images”的目录中,并重新命令为sample.jpg。在这里,我选择网上的一个图片。我们也需要下载另外一个图片list.png来完成我们的这个步骤(存放于images目录中)。我们可以同时参阅Qt网页里的ListView介绍。我们修改我们的程序如下:

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

Item {
    id: mainScreen

    ListModel {
        id: sampleListModel

        ListElement {
            imagePath: "images/sample.jpg"
            title: "First picture"
        }
        ListElement {
            imagePath: "images/sample.jpg"
            title: "Second picture"
        }
        ListElement {
            imagePath: "images/sample.jpg"
            title: "Third picture"
        }
    }

    Component {
        id: listDelegate
        BorderImage {
            source: "images/list.png"
            border { top:4; left:4; right:100; bottom: 4 }
            anchors.right: parent.right
            // width: mainScreen.width
            width: ListView.view.width

            property int fontSize: 25

            Rectangle {
                id: imageId
                x: 6; y: 4; width: parent.height - 10; height:width; color: "white"; smooth: true
                anchors.verticalCenter: parent.verticalCenter

                BorderImage {
                    source: imagePath; x: 0; y: 0
                    height:parent.height
                    width:parent.width
                    anchors.verticalCenter: parent.verticalCenter
                }

            }

            Text {
                x: imageId.width + 20
                y: 15
                width: ListView.view.width*2/3
                text: title; color: "white"
                font { pixelSize: fontSize; bold: true }
                elide: Text.ElideRight; style: Text.Raised; styleColor: "black"
            }
        }
    }

    UbuntuListView {
        id: listView
        width: mainScreen.width
        height:mainScreen.height

        model: sampleListModel

        delegate: listDelegate
    }

    Scrollbar {
        flickableItem: listView
    }
}

特别值得注意的是,我们这里使用了一个叫做listDelegate的Component来显示每一个list中的项。这个listDelegate相当于是每个ListView中的每一项显示的模版。对于Android开发比较熟悉的开发者来说,这个并不陌生。Android里是采用adapter来实现相同功能的。同时我们也使用了一个人工假想的sampleListModel来填充我们的数据。重新运行我们的应用。我们可以看到如下的显示:



到这里,我们基本上已经完成了整个ListView的显示。整个的源代码在如下的网址可以下载:

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


3)创建toolbar

在这个章节里,我们来完成我们的toolbar的设计。再回到我们最上面图显示的最初的设计,我们需要有一个输入的框来输入我们的关键词以来搜索。

    BorderImage {
        id:toolbar

        anchors.bottom:parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter

        width: parent.width
        height: units.gu(6)

        source: "images/toolbar.png"
        border.left: 4; border.top: 4
        border.right: 4; border.bottom: 4

        Row {
            id:inputcontainer
            anchors.centerIn:toolbar
            anchors.verticalCenterOffset:6
            spacing:12
            Text {
                id:label
                font.pixelSize:30
                anchors.verticalCenter:parent.verticalCenter;
                text: i18n.tr("Search:")
                font.bold:true
                style:Text.Raised
                styleColor:"#fff"
                color:"#444"
            }

            TextField {
                id:input
                placeholderText: "Please input a text to search:"
                width:220
                text:"Beijing"
            }
        }

        Image {
            id: backbutton
            opacity:0
            source: "images/back.png"
            anchors.verticalCenter:parent.verticalCenter
            anchors.verticalCenterOffset:0
            anchors.left:parent.left
            anchors.leftMargin:8

            MouseArea{
                anchors.fill:parent
                onClicked: mainScreen.state=""
                scale:4
            }
        }
    }

整个的设计也是非常直接的。我们使用了一个toolbar.png作为背景。使用一个Row来管理我们的“Search”及TextField输入框。同时我们还有一个在默认情况下opacity为零的back.png的Image。当opacity为零时,就是不可见。许多的control还有visible。虽然在某种程度上,visible和opacity有相同的功能,但是由于opacity可以从“0”到“1”进行变化,从而达到动画的效果。它用来显示在detail屏中来返回到ListView.默认情况下是不可见的。我们需要下载toolbar.pngback.png来完成该步骤。为了能够得到整屏的显示,我们也对ListView的高度进行调整如下:

    UbuntuListView {
        id: listView
        width: mainScreen.width
        height:mainScreen.height - toolbar.height
        model: sampleListModel
        delegate: listDelegate
    }

这样整个屏幕的上方是一个ListView,下面的部分是toolbar。重新运行我们的应用,我们可以看到如下的画面:



虽然现在还不能做什么,但是我们应用的基本框架已经搭好了。在接下来的章节中,我们来更进一步把真实的数据填进来。这个步骤的代码在如下的地址可以找到:

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

3)对ListView添加真实的数据


虽然上面我们已经有一个显示,但是毕竟是虚假的数据。在这个章节中,我们将使用XmlListModel来对Flickr网站进行请求并得到真实的数据。首先,我们在PictureListView.qml的开始部分加入

import QtQuick.XmlListModel 2.0

这里我们包含上面的XmlListModel的库,就可以使用它里面的API了。在sampleListModel的上面加入如下的代码:

XmlListModel {
    id: feedModel

//     source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2"
    source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2&tags=" + escape(input.text)
    query: "/rss/channel/item"  // flickr
    namespaceDeclarations:  "declare namespace media=\"http://search.yahoo.com/mrss/\";"

    // Flickr
    XmlRole { name: "title"; query: "title/string()" }
    XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" }
    XmlRole { name: "photoAuthor"; query: "author/string()" }
    XmlRole { name: "photoDate"; query: "pubDate/string()" }

    XmlRole { name: "url"; query: "media:content/@url/string()" }
    XmlRole { name: "description"; query: "description/string()" }
    XmlRole { name: "tags"; query: "media:category/string()" }
    XmlRole { name: "photoWidth"; query: "media:content/@width/string()" }
    XmlRole { name: "photoHeight"; query: "media:content/@height/string()" }
    XmlRole { name: "photoType"; query: "media:content/@type/string()" }
}

同时修改listView的model为feedModel:

    UbuntuListView {
        id: listView
        width: mainScreen.width
        height:mainScreen.height - toolbar.height
        model: feedModel
        delegate: listDelegate
    }

这里我们使用了XmlListModel来查询我们的数据。我们可以在浏览器中输入地址http://api.flickr.com/services/feeds/photos_public.gne?format=rss2&tags=beijing。我们可以看到如下的显示



我们通过XmlListModel进行查询,并同时转换为我们所需要的数据进行显示。比如我们用到的“title”及“imagePath”。在查询时我们也同时使用了input输入框中的text。我们运行我们刚生成的应用,并同时修改在输入框中的内容,比如“shanghai”或“tianjing”等。我们会看到内容的改变。


  


为了使得图片的大小更加适合于在手机上显示,我们也同时修改listDelegate中BorderImage的高度为180.

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


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

4)显示照片的详细信息


我们虽然已经显示了照片的详细信息,我们希望在点击每个ListView中的每个项目的时候,能够更加清楚地看得到更加详细的照片信息。为了能够达到这个目的,我们可以在ListView的右边放置一个同样大小的屏幕。当我们点击ListView中的每一项时,我们就把屏幕向右移动。这样就可以看到照片的详细信息。当我们看完照片时,我们也可以把屏幕向左移动这样又回到ListView的页面。为了达到这样的设计,我们在我们的项目中加入一个新的叫做“SpinnerImage.qml”的Component。它的创建方法和上面介绍的“PictureListView.qml”是一样的。它的设计如下:

import QtQuick 2.0

Image {
    id:image

    property bool loading:status != Image.Ready

    Image {
        id: container
        property bool on: false
        source: "images/spinner.png";
        visible: loading
        anchors.centerIn:parent
        NumberAnimation on rotation {
            running: loading ; from: 0; to: 360;
            loops: Animation.Infinite; duration: 2000
        }
    }
}

我们可以看到,这是一个Image中含有一个Image的Component。当id为image中的Image还在下载时,里面的container中的spinner.png将不断地旋转。我们需要下载spinner.png文件。

我们需要修改ListView的设计如下:

    Row {
        id:viewcontainer

        UbuntuListView {
            id: listView
            width: mainScreen.width
            height:mainScreen.height - toolbar.height

            model: feedModel
            delegate: listDelegate

            Scrollbar {
                flickableItem: listView
            }
        }

        SpinnerImage {
            id:viewscreen
            clip:true
            width:mainScreen.width
            height:mainScreen.height - toolbar.height
            smooth:true
            fillMode:Image.PreserveAspectFit
        }
    }

在这里我们使用了一个Row布局,从而产生了一个和mainScreen宽度两倍的显示。在起始显示的时候,只有最左边的显示可以在屏幕中展示。为了能够响应对屏幕的触控,我们在我们的listDeleage中最外层的BorderImage中加入MouseArea中如下的代码:

    Component {
        id: listDelegate
        BorderImage {
            source: "images/list.png"
            border { top:4; left:4; right:100; bottom: 4 }
            anchors.right: parent.right
            // width: mainScreen.width
            width: ListView.view.width
            height: 180

            property int fontSize: 25

            Rectangle {
                id: imageId
                x: 6; y: 4; width: parent.height - 10; height:width; color: "white"; smooth: true
                anchors.verticalCenter: parent.verticalCenter

                SpinnerImage {
                    source: imagePath; x: 0; y: 0
                    height:parent.height
                    width:parent.width
                    anchors.verticalCenter: parent.verticalCenter
                }
            }

            Text {
                x: imageId.width + 20
                y: 15
                width: mainScreen.width - x - 40
                text: title; color: "white"
                font { pixelSize: fontSize; bold: true }
                elide: Text.ElideRight; style: Text.Raised; styleColor: "black"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    viewcontainer.x -= mainScreen.width
                }
            }
        }
    }

重新运行应用。我们发现,当我们点击listview中的每一项时,就会很快地切换到另外一个页面,并展示该图片。在实际体验中,这个可能并不是最好的,有时我们希望能看到一个平滑过度的中间状态,就像我们在其它平台中看到的那样。我们在这里可以使用QML中的状态来实现这个功能。在PictureListView.qml中,加入如下的代码(放在mainScreen):
    states: [
        State {
            name: "view"
            PropertyChanges {
                target: viewcontainer
                x:-mainScreen.width
            }
            PropertyChanges {
                target: backbutton
                opacity:1
            }
            PropertyChanges {
                target: inputcontainer
                opacity:0
            }
        }
    ]

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

在这里我们定义了一个新的状态“view”。默认的状态为"",即一个空字符串。在状态放生切换时我们可以使用transitions来实现动画的展示。在上面,我们使得在x发生改变时,需要500毫秒来完成。同样我们也使得backbutton及inputcontainer的opacity也发生改变。有兴趣的开发者可以调整一下时间的参数来看看这个变化的过程。这样我们就可以看到状态的变化。加入了状态后,我们重新改写listDelegate中MouseArea中的代码:

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    viewscreen.source = imagePath.replace("_s", "_m")
                    mainScreen.state = "view"
                }
            }

在这里,我们同时也修改了imagePath中的内容,从而使得我们能够在展示详细图片时,使用的是“中”(m)等大小的图片而不是“小”(s)的图片。重新运行我们的应用:


   

当我们点击在ListView中的每个项时,就会展示它的详细的图片的情况。我们可以点击详细图片下方的箭头回到ListView中去。这个代码在toolbar的如下代码中可以到:

        Image {
            id: backbutton
            opacity:0
            source: "images/back.png"
            anchors.verticalCenter:parent.verticalCenter
            anchors.verticalCenterOffset:0
            anchors.left:parent.left
            anchors.leftMargin:8

            MouseArea{
                anchors.fill:parent
                onClicked: mainScreen.state=""
                scale:4
            }
        }

在这里我们可以看到,当我们点击的时候,mainScreen的状态state有设为"",也就是应用最起始的状态。在x坐标发生变化时,我们可以通过transitions来实现动画的效果。

这个应用在手机的图片为:

  


整个应用的源码在如下地址可以找到:

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


5)更进一步完善应用


细心的开发者如果在手机上运行并输入字符串时,可能会发现跳出的键盘挡住了我们的输入框。我们可以通过打开在main.qml中的MainView中的如下的项:

anchorToKeyboard: true

更多的阅读可以参照文章来详细了解。

另外,在ListView中,当照片还在下载时,我们也希望能够看到不断选择的spinner。我们可以把listDelegate中的imageId修改为:

            Rectangle {
                id: imageId
                x: 6; y: 4; width: parent.height - 10; height:width; color: "white"; smooth: true
                anchors.verticalCenter: parent.verticalCenter

                SpinnerImage {
                    source: imagePath; x: 0; y: 0
                    height:parent.height
                    width:parent.width
                    anchors.verticalCenter: parent.verticalCenter
                }
            }

我们可以在UbuntuListView中加入“PullToRefresh”如下的代码:

        UbuntuListView {
            id: listView
            width: mainScreen.width
            height:mainScreen.height - toolbar.height

            model: feedModel
            delegate: listDelegate

            // let refresh control know when the refresh gets completed
            PullToRefresh {
                refreshing: listView.model.status === XmlListModel.Loading
                onRefresh: listView.model.reload()
            }

            Scrollbar {
                flickableItem: listView
            }
        }

这样,当我们往下拉ListView时,会自动更新我们的ListView中的内容。

我们也可以通过修改Flickr.png来实现我们自己的定制的icon。整个应用的最终版本在如下的地址可以找到:

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

6)加入Swipe功能


目前我们的应用应该还是比较地完善。我们还希望加入一个功能。当我们显示大的图片时,我们希望能够通过从左向右的滑动来回到以前的列表的画面。这样我们就不必使用点击屏幕左下方的箭头了。为了实现这个功能,我们复制我们的“SpinnerImage.qml”文件到当前的目录,并同时重新命名为“DetailedImage.qml”文件。我们回到SDK的Flickr的项目中来。我们发现项目已经自己找到这个文件了。这个功能是和CMake项目不太相同的地方。为了捕获Swipe的动作,我们在这个文件中加入对MouseArea的处理。整个源码如下:

import QtQuick 2.0

Image {
    id:image

    property bool loading:status != Image.Ready

    Image {
        id: container
        property bool on: false
        source: "images/spinner.png";
        visible: loading
        anchors.centerIn:parent
        NumberAnimation on rotation {
            running: loading ; from: 0; to: 360;
            loops: Animation.Infinite; duration: 2000
        }
    }

    // Create the detection of swipe
    signal swipeRight;
    signal swipeLeft;
    signal swipeUp;
    signal swipeDown;

    MouseArea {
        anchors.fill: parent
        property int startX;
        property int startY;

        onPressed: {
            startX = mouse.x;
            startY = mouse.y;
        }

        onReleased: {
            var deltax = mouse.x - startX;
            var deltay = mouse.y - startY;

            if (Math.abs(deltax) > 50 || Math.abs(deltay) > 50) {
                if (deltax > 30 && Math.abs(deltay) < 30) {
                    // swipe right
                    swipeRight();
                } else if (deltax < -30 && Math.abs(deltay) < 30) {
                    // swipe left
                    swipeLeft();
                } else if (Math.abs(deltax) < 30 && deltay > 30) {
                    // swipe down
                    swipeDown();
                } else if (Math.abs(deltax) < 30 && deltay < 30) {
                    // swipe up
                    swipeUp();
                }
            }
        }
    }
}

我们通过对MouseArea中的“onPressed”及“onReleased”处理,从而产生相应的信号swipeRight, swipeLeft, swipeUp, swipeDown。为了对这个信号的响应,我们对PictureListView.qml 中的如下部分做一下的修改:

    Row {
        id:viewcontainer

        UbuntuListView {
            id: listView
            width: mainScreen.width
            height:mainScreen.height - toolbar.height

            model: feedModel
            delegate: listDelegate

            // let refresh control know when the refresh gets completed
            PullToRefresh {
                refreshing: listView.model.status === XmlListModel.Loading
                onRefresh: listView.model.reload()
            }

            Scrollbar {
                flickableItem: listView
            }
        }

        DetailedImage {
            id:viewscreen
            clip:true
            width:mainScreen.width
            height:mainScreen.height - toolbar.height
            smooth:true
            fillMode:Image.PreserveAspectFit

            onSwipeRight: {
                onClicked: mainScreen.state=""
            }
        }
    }

我们把原来的“SpinnerImage”改为我们刚生产的“DetailedImage”,并同时对“swipeRight”信号进行响应。注意这里的“onSwipeRight”里的“S”是大写的。当我们得到这个信号时,我们把应用的状态设为最初始的状态""。重新运行我们的应用,并在手机上运行:





如果开发者不知道怎么部署应用到手机上,可以参考文章“ 怎么安装Ubuntu应用到Device中”。在手机上面显示的图片如下:

  

整个应用的源码在如下的地址可以找到:

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

或在地址:git clone https://gitcafe.com/ubuntu/Flickr.git

另外一个更加复杂的版本在如下地址可以下载:

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



作者:UbuntuTouch 发表于2014-10-29 15:44:42 原文链接
阅读:238 评论:2 查看评论

Read more
UbuntuTouch

[原]使用URL dispatcher的范例

在上面的一篇文章中,我们介绍了如何使用URL disptacher。在这篇文章中,我们来通过一个范例更进一步来了解如何实现它。


1)创建一个具有URL dispatcher的应用

我们首先打开我们的SDK,然后创建一个最基本的QML template应用。我们把该应用叫做“MyApp”。我们首先在“MyApp”的根目录添加一个文件叫做“MyApp.url-dispatcher”文件,这里面的内容如下:

[
	{
		"protocol": "launchmyapp"
	}
]

这样的定义使得任何在Qt.openUrlExternally()中具有以“launchmyapp:///”开头的调用,就可以打开该应用。比如:

Qt.openUrlExternally("launchmyapp:///123");

同时,我们修改我们的manifest.json文件如下:

{
    "architecture": "all",
    "description": "description of MyApp",
    "framework": "ubuntu-sdk-14.10-dev2",
    "hooks": {
        "MyApp": {
            "apparmor": "MyApp.apparmor",
            "desktop": "MyApp.desktop",
	    "urls": "MyApp.url-dispatcher"
        }
    },
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "name": "com.ubuntu.developer.unknown.myapp",
    "title": "MyApp",
    "version": "0.1"
}

注意这里的“urls”项。到这里,我们基本上就可以让我们的应用能够被其它的应用调用了。为了能够得到调用应用传来的参数,我们也同时修改我们的desktop文件如下:

[Desktop Entry]
Name=MyApp
Exec=qmlscene $@ main.qml -- %u
Icon=MyApp.png
Terminal=false
Type=Application
X-Ubuntu-Touch=true

注意这里的"-- %u",这是加入的部分。为了能够在程序中显示得到的URL信息,我们对程序做了如下的修改:


import QtQuick 2.0
import Ubuntu.Components 1.1

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

MainView {
    id:root

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.unknown.myapp"

    Component.onCompleted: {
        mylabel.text = "aaaa";
        console.log( "arg length: " + myarg.arguments.length );

        if ( myarg.defaultArgument === undefined) {
            mylabel.text = "undefined";
        } else {
            mylabel.text = "args: " + myarg.defaultArgument.at(0);
        }

        console.log("argument: " + myarg.defaultArgument.at(0));
        console.log("")
    }

    Arguments {
        id: myarg
        defaultArgument.help: "Expects URL of the media to play."
        defaultArgument.valueNames: ["URL"]
    }

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

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

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

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

            Row {
                spacing: units.gu(2)

                Label {
                    id: mylabel
                    objectName: "label"

                    text: i18n.tr("Received parameters: ")
                }

                Label {
                    id: label
                    objectName: "label"

                    text: i18n.tr("")
                }

                Connections {
                    target: UriHandler
                    onOpened: {
                        // root.applicationName = "good"
                        mylabel.text = "dddddd";

                        var para = "";
                        for (var i = 0; i < uris.length; ++i) {
                            // application.parseArgument(uris[i])
                            console.log( uris[i] );
                            para +=  uris[i];
                        }

                        label.text = para;
                    }
                }
            }
        }
    }

}

注意这里的“UriHandler”部分,当应用在运行时,url dispatcher被调用时,该部分代码会被执行。当应用没有运行时,我们通过传人的参数从而得到输入的参数值:

    Arguments {
        id: myarg
        defaultArgument.help: "Expects URL of the media to play."
        defaultArgument.valueNames: ["URL"]
    }

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

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


2)创建调用应用


这个应用其实很简单。我们直接创建一个基本的QML template应用,同时修改我们的main.qml如下:

import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.unknown.launchmyapp"

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

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

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

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

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

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

                text: i18n.tr("Launch MyApp")

                onClicked: {
                    Qt.openUrlExternally("launchmyapp:///123");
                }
            }

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

                text: i18n.tr("Open MyApp")

                onClicked: {
                    Qt.openUrlExternally("appid://com.ubuntu.developer.unknown.myapp/MyApp/current-user-version");
                }
            }
        }
    }
}

这里我们使用了两种方法来调用我们的“MyApp”。第一种是通过:

 Qt.openUrlExternally("launchmyapp:///123");

这种方法的好处是可以传人我们需要的参数,并解析,从而对于不同的参数可以得到不同的响应。

另外一种方式是通过:

Qt.openUrlExternally("appid://com.ubuntu.developer.unknown.myapp/MyApp/current-user-version");

这种方法不能解析任何的参数,它可以把应用启动起来。我们可以通过如下的方法得到应用的一些信息:


运行我们的应用:



我们按下第一个按钮,如果“MyApp”没有运行时,会显示如下的在左边的画面。如果“MyApp”在已经运行的情况下,可以看到如下的右边的画面:

   

如果,我们点击应用下面的按钮的话,可以看到如下的画面:



整个项目的源码在如下的地址可以找到:


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




作者:UbuntuTouch 发表于2014-10-28 12:49:37 原文链接
阅读:335 评论:0 查看评论

Read more
UbuntuTouch

URL dispatcher 是在Ubuntu OS上的一个服务。它可以让我们的应用(confined,i.e, click package应用)来启动其它的应用。这些应用通常是一个特别的URL来 识别的。可能最常见的例子就是向网页浏览器发送http:// URL来启动网页浏览器,但是像music或其它的应用也可以支持。对于大多数的应用来说,这是一个最有用的方式来退出现有的应用来启动另外一个应用。


对于Qt应用来说,它访问URL dispatcher的方法是通过Qt的desktop plugin。C++应用可以通过使用QDesktopServices.openURL()方法。QML应用可以使
用 Qt.openUrlExternally(string)方法。对于其它的应用来说,它们可以利用根据自己的需求使用Platform API或直接使用liburl-dispatcher。

URL dispatcher项目可以在Launchpad的链接找到。

支持的URL格式

基本URL格式


在URL dispatcher中有几种被支持的格式:



application:///$(app_id).desktop
The application URL can be used to start an application with a known Application ID. For applications that install their desktop file in /usr/share/applications the application ID should just be the name of the desktop file.
appid://$(pkg)/$(app)/$(version) Allows for launching an application using an application ID. Also provides for wild cards. The $(app) can be first-listedlast-listed-app or only-listed-app to select the appropriate applicaiton using the click manifest. Also the $(version) can be current-user-version to select the version in the manifest

这里的第二种方法是推荐的方法。比如对于Clock应用来说,我们可以看到如下的应用URL:


appid://com.ubuntu.clock/clock/current-user-version

我们可以通过如下的方法得到在手机中应用click package的信息:


     


应用URL

对于一些应用来说,我们可以在启动应用的时候同时传人一些参数来启动该应用。我们可以通过在应用的Click manifest文件中加入URL的定义来注册该应用可以被一个或多个URL来启动。为了达到这个目的,我们可以在该文件中的“hooks”部分加入一个和“desktop”并列的小的json文件的申明。一个简单的manifest就像下面定义的。


{
        "name": "My App",
        "version": "1.2.3",
        "hooks": {
                "foo": {
                        "desktop": "foo.desktop",
                        "urls": "foo.url-dispatcher"
                }
        }
}

这里它指向另外一个在click包中的JSON文件。它定义了什么URL将被接受。一个简单的文件就像如下的格式:

[
        {
                "protocol": "foo",
                "domain-suffix": "bar.com"
        }
]

通过上面的定义,每当有任何一个像“foo://*.bar.com”格式URL请求,由foo.desktop代表的应用将被自动被调用。在这里,如果“domain-suffix”项被省去的话,该应用将接受所有以“foo”代表的protocol格式的请求。URL dispatcher的定义是一个数组,这样有很多的这样的格式可以在应用中被定义。

一个例程显示如何调用Music及Clock可以在如下的地址找到:

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

作者:UbuntuTouch 发表于2014-10-23 23:06:03 原文链接
阅读:229 评论:0 查看评论

Read more