Canonical Voices

What Ubuntu Touch Development in CSDN (Chinese) talks about

UbuntuTouch

[原]Windows7下安装Ubuntu双系统

Windows7和Ubuntu双系统的安装是非常容易的。在Ubuntu的安装的时候,已经提供了图形化的界面来帮助用户安装。你可以选择单单安装Ubuntu操作系统抹去原来的Windows操作系统,也可以选择与Windows操作系统共存,用Windows引导Ubuntu。但是这就造成了一个问题——一旦你想要删除Ubuntu操作系统,想要抹掉原先分给Ubuntu的磁盘空间,就会破坏Windows的MBR分区。因为在你按照原先系统安装盘的指令安装Ubuntu操作系统的时候,MBR分区的数据会被重写。

小编之前也有过这样的情况。后果就是在我删除整个Ubuntu分区后,windows也已经打不开了。在这个时候,其实你可以做一个工作来找回你“丢失”的Windows操作系统。修复MBR分区。比较常见的,比较“古朴”的方法就是进DOS使用fdisk /mbr命令。但是当时小编用的时候貌似还是不行。于是找到了以下的方法:进入windows系统,调出cmd,用bootrec /fixmbr命令,就能修复MBR分区了。

那么每次这样是不是很麻烦?能不能从源头上来解决呢?可以的。在安装Ubuntu操作系统的时候就可以解决这个问题。下面就跟着小编的这个步骤来安装操作系统,不破坏windows的正常使用。


1)下载Ubuntu desktop操作系统


可以在以下网址:http://www.ubuntu.com/download/desktop

下载到Ubuntu desktop的发行版。根据你自己的使用情况及CPU的情况来下载适合自己电脑的Ubuntu Desktop操作系统。




建议大家下载Ubuntu 14.10,因为在做Ubuntu手机、平板开发的时候是需要14.10的系统的。小编在这里选择64位的14.10操作系统



2)磁盘分区


在这一步中,你将要对你的磁盘进行分区。如果普通使用的话20G的磁盘空间就可以了,但是做开发的话是不够的。小编在这里给Ubuntu分了100G的空间。

在我的“计算机”上右键,选择“管理”,打开之后依次选择“存储——磁盘管理”,之后你就将看到你的硬盘的虚拟化的图像。其实还有其他的外接存储设备都会有显示。




在一个硬盘分区上右键选择“压缩卷”,跟着步骤走,输入你想要磁盘大小。


3)制作USB启动盘


找一个U盘,4G的吧,应该够了,插入电脑的USB接口。小编在这里使用的是UltraISO(http://cn.ultraiso.net/xiazai.html)这个软件。




“打开文件”,定位到你刚刚下载的Ubuntu的安装文件,“打开”




选择“启动”——“写入硬盘映像”




在这里一定要注意哦,这里“硬盘驱动器”这里是不是你的那个U盘。如果你插了其他的存储设备一定要看清,不然会造成不必要的损失。

然后点“写入”,静候直至出现“刻录成功”的字样。




至此,你的U盘安装盘就已经做好了。



4)安装Ubuntu操作系统


令人激动人心的时刻到了安装Ubuntu操作系统




开机的时候进入Boot Manager选择USB启动。各个电脑的进入Boot选项的方法都不同,但是无外乎F12,F10,F2等等的function键。




在这里你可以选择

  • 使用Ubuntu操作系统但并不安装

  • 安装Ubuntu

  • OEM安装

  • 检查磁盘

我们在这里选择“Install Ubuntu”(“安装Ubuntu”)


注:如果你看到以下画面,请:

  • 检查电脑USB口

  • 更换U盘重新制作USB启动盘

  • 插拔U盘查看是否是接触问题

最有可能的还是U盘的问题,所以请使用U盘芯片比较好的U盘,推荐用Sandisk





接着就是选择语言,wifi,等等的安装前的设置。

但是我们要注意一下,在安装类型的这里仔细看一下。








在这里需要注意下


安装类型选择其他选项




选择你刚刚在Windows下分出来的那个盘,选择“Ext4 日志文件系统” 挂载点“/”




这步做完,你的那个盘的格式会变为Ext4




然后在“安装启动引导器的设备”一栏里选择你的那个盘,小编这里是“sda6”(每个人的电脑可能显示的是不同的)




之后选择“格式化”——“现在安装”

在选择时区、设置用户名密码之后就会进入到安装过程




这一步之后就已经把Ubuntu安装到那个分区了。

电脑就会自动重启


5)设置引导


在如上步骤安装完之后,重启进入windows引导没有Ubuntu的。




在这一步就是要设置Ubuntu的引导。

进入windows之后,我们所使用的软件是EasyBCD(http://easybcd.softonic.cn/)





选择“Add New Entry”选择“Linux/BSD”——选择“GRUB2”并命名为Ubuntu,点击“Add Entry”





6)重新启动计算机,安装完成


重新启动计算机后,你就会看到Windows的引导里有你刚刚建立的Ubuntu的选项。选择Ubuntu就会进入Grub。然后就会进入Ubuntu操作系统。






这样的安装方式,就不怕以后删除Ubuntu之后不能启动windows了,但是小编还是希望大家不要删除Ubuntu的啦~


Ubuntu小编原创,欢迎转载!



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

Read more
UbuntuTouch

[原]Ubuntu OS上的QML应用框架

在我们编写QML应用的时候,我们有时事先需要来考虑我们怎么使用一个好的框架来完成我们的应用。我们的应用有多少个页面,页面之间的导航到底是怎么样子的。这个对于我们一开始来设计我们的应用来说非常中要。在这篇文章中,我们来介绍如何在上层来设计我们的应用框架。


1)使用tab来创建一个平面的导航应用

我们可以使用我们的Ubuntu SDK来创建一个最基本的叫做TabApp的应用:

   

 

这样我们就生成了我们的一个最基本的应用。我们把应用的宽度和高度设为如下的值:

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

同时,我们也修改我们的Main.qml如下:

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     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)


    Tabs {
        id: tabs

        Tab1 {
            objectName: "Tab1"
        }

        Tab2 {
            objectName: "Tab2"
        }
    }
}

在这里我们定义了两个Tab页面,分别为Tab1及Tab2。它们的内容分别为:

import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page one")
        }

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

import QtQuick 2.0
import Ubuntu.Components 1.1

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

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page two")
        }
    }
}

这是一个最简单的Tab导航应用。我们在手机上运行:

    


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

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


我们也可以把我们的Main.qml修改如下:

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     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)

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

    Tabs {
        id: tabs
        Tab {
            title: i18n.tr("Simple page")
            page: Page {
                Label {
                    id: label
                    anchors.centerIn: parent
                    text: "A centered label"
                }
                tools: ToolbarItems {
                    ToolbarButton {
                        action: reloadAction
                    }
                }
            }
        }

        Tab {
            id: externalTab
            title: i18n.tr("External")

            page: Loader {
                id: loader
                anchors.fill: parent
                source: (tabs.selectedTab === externalTab) ? Qt.resolvedUrl("ExternalPage.qml") : ""

                onLoaded: {
                    console.log( loader.source + " is loaded")
                }
            }
        }

        Tab {
            title: i18n.tr("List view")
            page: Page {
                ListView {
                    clip: true
                    anchors.fill: parent
                    model: 20
                    delegate: ListItem.Standard {
                        iconName: "compose"
                        text: "Item "+modelData
                    }
                }
            }
        }
    }
}

运行我们的应用:


   

所有的源码在:

https://gitcafe.com/ubuntu/TabApp4.git

我们如果想在Tab架构中使用pagestack的话,我们对我们的应用必须做一些修改。我们只能把Tabs作为第一个页面推到PageStack的栈中。Main.qml具体实现如下:

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     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)

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

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

        Tabs {
            id: tabs
            Tab {
                title: "Tab 1"
                page: Page {
                    Button {
                        anchors.centerIn: parent
                        onClicked: pageStack.push(page3)
                        text: "Press"
                    }
                }
            }
            Tab {
                title: "Tab 2"
                page: Page {
                    Label {
                        anchors.centerIn: parent
                        text: "Use header to navigate between tabs"
                    }
                }
            }
        }
        Page {
            id: page3
            visible: false
            title: "Page on stack"
            Label {
                anchors.centerIn: parent
                text: "Press back to return to the tabs"
            }
        }
    }
}


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

   

我们可以看见在上面显示的那样,有一个叫做“Page on stack”。可以通过按下换回箭头回到上一个页面。

具体的代码:

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

2)使用PageStack来导航


在这一节中,我们将介绍如何使用PageStack来管理我们的页面。当用户进入下一个页面完成自己的工作后,可以通过按下标题栏中的返回箭头回到上一个页面。按照上面同样的步骤,我们可以创建一个叫做PageStack的项目。Main.qml的设计如下:

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

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pagestack.ubuntu"

    /*
     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)

    PageStack {
        id: pageStack
        Component.onCompleted: {
            push(page0)
        }

        Page {
            id: page0
            title: i18n.tr("Root page")
            visible: false

            Column {
                anchors.fill: parent

                ListItem.Standard {
                    text: i18n.tr("Page one")
                    onClicked: pageStack.push(page1, {color: UbuntuColors.orange})
                    progression: true
                }
                ListItem.Standard {
                    text: i18n.tr("Page two")
                    onClicked: pageStack.push(Qt.resolvedUrl("Page2.qml"))
                    progression: true
                }
            }
        }

        Page {
            title: "Rectangle"
            id: page1
            visible: false
            property alias color: rectangle.color
            Rectangle {
                id: rectangle
                anchors {
                    fill: parent
                    margins: units.gu(5)
                }
            }
        }
    }
}


这里我们在应用启动时创建一个PageStack,并同时把“page0”压入栈中。使它成为第一个页面。在“page0”中,我们有有两个列表项,分别可以进入到下一个页面中。

运行我们的应用:

   

我们可以在每个页面看见有个返回的箭头。

整个项目的源码在:

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




作者:UbuntuTouch 发表于2015/3/25 10:20:09 原文链接
阅读:557 评论:2 查看评论

Read more
UbuntuTouch

尽管我们的developer网站有丰富的API介绍,但是,有些API的介绍可能并不全,有些API也在不断地演进中。为了得到更详细的API,我们可以通过如下的命令来得到更加详细的信息。比如我们对“SingleDownload”API来得到更加多的信息。


$qmlplugindump Ubuntu.DownloadManager 0.1



显示的结果如下:


import QtQuick.tooling 1.1


// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump Ubuntu.DownloadManager 0.1'


Module {
    Component {
        name: "Ubuntu::DownloadManager::DownloadError"
        prototype: "QObject"
        exports: ["Error 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "type"; type: "string"; isReadonly: true }
        Property { name: "message"; type: "string"; isReadonly: true }
    }
    Component {
        name: "Ubuntu::DownloadManager::SingleDownload"
        prototype: "QObject"
        exports: ["SingleDownload 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "autoStart"; type: "bool" }
        Property { name: "errorMessage"; type: "string"; isReadonly: true }
        Property { name: "isCompleted"; type: "bool"; isReadonly: true }
        Property { name: "downloadInProgress"; type: "bool"; isReadonly: true }
        Property { name: "allowMobileDownload"; type: "bool" }
        Property { name: "throttle"; type: "qulonglong" }
        Property { name: "progress"; type: "int"; isReadonly: true }
        Property { name: "downloading"; type: "bool"; isReadonly: true }
        Property { name: "downloadId"; type: "string"; isReadonly: true }
        Property { name: "headers"; type: "QVariantMap" }
        Signal {
            name: "canceled"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "finished"
            Parameter { name: "path"; type: "string" }
        }
        Signal {
            name: "paused"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "processing"
            Parameter { name: "path"; type: "string" }
        }
        Signal {
            name: "progressReceived"
            Parameter { name: "received"; type: "qulonglong" }
            Parameter { name: "total"; type: "qulonglong" }
        }
        Signal {
            name: "resumed"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "started"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "errorFound"
            Parameter { name: "error"; type: "DownloadError&" }
        }
        Signal { name: "errorChanged" }
        Method {
            name: "registerError"
            Parameter { name: "error"; type: "Error"; isPointer: true }
        }
        Method {
            name: "bindDownload"
            Parameter { name: "download"; type: "Download"; isPointer: true }
        }
        Method {
            name: "unbindDownload"
            Parameter { name: "download"; type: "Download"; isPointer: true }
        }
        Method {
            name: "onFinished"
            Parameter { name: "path"; type: "string" }
        }
        Method {
            name: "onProgress"
            Parameter { name: "received"; type: "qulonglong" }
            Parameter { name: "total"; type: "qulonglong" }
        }
        Method {
            name: "onPaused"
            Parameter { name: "wasPaused"; type: "bool" }
        }
        Method {
            name: "onResumed"
            Parameter { name: "wasResumed"; type: "bool" }
        }
        Method {
            name: "onStarted"
            Parameter { name: "wasStarted"; type: "bool" }
        }
        Method {
            name: "onCanceled"
            Parameter { name: "wasCanceled"; type: "bool" }
        }
        Method { name: "start" }
        Method { name: "pause" }
        Method { name: "resume" }
        Method { name: "cancel" }
        Method {
            name: "download"
            Parameter { name: "url"; type: "string" }
        }
    }
    Component {
        name: "Ubuntu::DownloadManager::UbuntuDownloadManager"
        prototype: "QObject"
        exports: ["DownloadManager 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "autoStart"; type: "bool" }
        Property { name: "cleanDownloads"; type: "bool" }
        Property { name: "errorMessage"; type: "string"; isReadonly: true }
        Property { name: "downloads"; type: "QVariantList"; isReadonly: true }
        Signal { name: "errorChanged" }
        Method {
            name: "download"
            Parameter { name: "url"; type: "string" }
        }
    }
}


我们通过使用“finished”信号可以得到下载完成的事件而进行分别的处理!


另外一个例子(Push notification)


liuxg@liuxg:~$ qmlplugindump Ubuntu.PushNotifications 0.1
import QtQuick.tooling 1.1

// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump Ubuntu.PushNotifications 0.1'

Module {
    Component {
        name: "PushClient"
        prototype: "QObject"
        exports: ["PushClient 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "appId"; type: "string" }
        Property { name: "token"; type: "string"; isReadonly: true }
        Property { name: "notifications"; type: "QStringList"; isReadonly: true }
        Property { name: "status"; type: "string"; isReadonly: true }
        Property { name: "persistent"; type: "QStringList"; isReadonly: true }
        Property { name: "count"; type: "int" }
        Signal {
            name: "countChanged"
            Parameter { type: "int" }
        }
        Signal {
            name: "notificationsChanged"
            Parameter { type: "QStringList" }
        }
        Signal {
            name: "persistentChanged"
            Parameter { type: "QStringList" }
        }
        Signal {
            name: "appIdChanged"
            Parameter { type: "string" }
        }
        Signal {
            name: "error"
            Parameter { type: "string" }
        }
        Signal {
            name: "tokenChanged"
            Parameter { type: "string" }
        }
        Signal {
            name: "statusChanged"
            Parameter { type: "string" }
        }
        Method { name: "getNotifications" }
        Method {
            name: "notified"
            Parameter { name: "appId"; type: "string" }
        }
        Method { name: "emitError" }
        Method {
            name: "clearPersistent"
            Parameter { name: "tags"; type: "QStringList" }
        }
    }
}


作者:UbuntuTouch 发表于2015/3/12 14:55:15 原文链接
阅读:415 评论:5 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将使用Ubuntu SDK所提供的online account API来访问微博的API并显示所需要的内容。这篇文章的重点是展示如何在HTML 5中使用online account API。我们将创建一个简单的HTML 5应用。我们最终显示的画面如下:


  


更多关于HTML5应用开发的资料可以在地址:https://developer.ubuntu.com/en/apps/html-5/


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


我们还是像以前一样使用我们的Ubuntu SDK来创建一个最基本的weibo HTML 5应用。


  

  

这样我们就创建了一个最基本的weibo HTML 5应用。你可以使用热键Ctrl + R来运行它虽然它并不能做什么事。






2)加入online account所需要的文件



我们可以参考文章来对online account API有更深的了解。为了能够访问,需要创建如下的文件:


1)weibo.application

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

2)weibo.service


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

3)创建一个plugin文件目录并在它的下面创建

 a)Main.qml, 它的内容为:

import Ubuntu.OnlineAccounts.Plugin 1.0

OAuthMain {}

    b) qml-weibo.ubuntu_plugin.provider,它的内容为:

<?xml version="1.0" encoding="UTF-8"?>
<provider>
  <name>Weibo</name>
  <icon>weibo.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>


在上面的文件中一定要输入自己的“your developer key”及“your developer secret”。这个你需要在微博API的网站上去申请。整个文件的架构为:




4)修改manifest.json文件如下

{
    "name": "html5-weibo.ubuntu",
    "description": "description of html5-weibo",
    "architecture": "all",
    "title": "html5-weibo",
    "hooks": {
        "html5weibo": {
            "apparmor": "html5weibo.apparmor",
            "desktop": "html5weibo.desktop",
            "account-application": "weibo.application",
            "account-service": "weibo.service"
        },
        "account-plugin": {
            "account-provider": "plugin/html5-weibo.ubuntu_account-plugin.provider",
            "account-qml-plugin": "plugin/qml"
        }
    },
    "version": "0.1",
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "framework": "ubuntu-sdk-14.10"
}

这里添加了account及plugin的一些设置。


5)为了能够让.service,.application及.provider文件能正常得到显示,我们对“qml-weibo.qmlproject”添加如下的东西

    Files {
        filter: "*.service"
    }
    Files {
        filter: "*.application"
    }
    Files {
        filter: "*.provider"
    }


到这里,我们已经基本上已经修改或添加好我们所需要的文件。


3)设计应用的UI


修改我们的“index.html”文件:

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

    <!--
         Application stylesheets
         Include your own stylesheets below to style your application.
      -->
    <link rel="stylesheet" href="css/app.css" type="text/css" />

    <!-- 
        Ubuntu UI javascript imports - Ambiance theme
        Ubuntu provides building blocks that you can use in your application. For more information, you can check out the documentation at http://design.ubuntu.com/apps.
     -->
    <!-- Ubuntu UI Style imports - Ambiance theme -->
    <link href="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/css/appTemplate.css" rel="stylesheet" type="text/css" />    
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/fast-buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/core.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/page.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/pagestacks.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/popovers.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/list.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/toolbars.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/tabs.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/shape.js"></script>

</head>
<body>

    <div data-role="mainview">
      <header data-role="header">
      </header>

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

          <div data-role="page" id="main" data-title="Weibo Status">

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

            <div id='results'></div>
          </div> <!-- main -->

        </div> <!-- pagestack -->
      </div> <!-- content -->
    </div> <!-- mainview -->

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

  </body>
</html>

这里的UI非常简单,我只使用了一个list控件。我们大部分的代码将在“app.js”中实现:

var token = '';

window.onload = function () {
    console.log("this is so cool....!")

    var UI = new UbuntuUI();

    UI.init();
    UI.pagestack.push('main');
    var api = external.getUnityObject('1.0');
    var oa = api.OnlineAccounts;

    // UI.button('getstatus').click(auth);

    auth();

    function auth() {
        console.log("getstatus button is clicked!");

        var filters = {'provider': 'html5-weibo.ubuntu_account-plugin', 'service': ''};

        oa.api.getAccounts(filters, function(accounts) {
            console.log("total length: " + accounts.length);

            if (accounts.length < 1) {
                // setResults('No accounts available');
                oa.api.requestAccount(
                    "html5-weibo.ubuntu_html5weibo",
                    unescape("html5-weibo.ubuntu_account-plugin"),

                    function() {
                        console.log("requestAccount callback...");
                        auth();
                });
                return;
            } else {
                // setResults("Available accounts: " + accounts.length);
            }

            function authcallback(res) {
                token = res['data']['AccessToken'];
                console.log("AccessToken: " + token);
//                setResults("AccessToken: " + token);

                getWeiboStatus(token, function(statuses) {
                    console.log("the length: " + statuses.length);

                    if (statuses) {
                        // create the 'ul' element
                        var ul = document.createElement('ul');
//                        var results = document.getElementById('results');
                        var results = document.querySelector('#results');
                        var statuslist = document.querySelector('#statuslist');
                        var ul = document.createElement('ul');

                        for (var i = 0; i < statuses.length; i ++) {

                            var li = document.createElement('li');
                            ul.appendChild(li);

                            var aside = document.createElement('aside');
                            li.appendChild(aside);

                            var img = document.createElement('img');
                            img.setAttribute('src', statuses[i]['profile_image_url']);
                            img.setAttribute('width', "50");
                            img.setAttribute('height', "50");
                            aside.appendChild(img);

                            var a = document.createElement('a');
                            a.setAttribute('href', '#');
                            a.innerHTML = statuses[i]['text'];
                            li.appendChild(a);

                            var right = document.createElement('label');
                            right.innerHTML = ""
                            li.appendChild(right);

                        }

                        console.log("it is done6");
                        statuslist.appendChild(ul);
                    }
                    else {
                        setResults('<br><br>ERROR');
                    }
                });
            }

            accounts[0].authenticate(authcallback);
        }); //getAccounts
    } //auth

    function getWeiboStatus(accessToken, callback) {
        var http = new XMLHttpRequest()
        var url = "https://api.weibo.com/2/statuses/home_timeline.json?access_token=" + accessToken + "&page=" + 1

        console.log('url: \n' + url)

        http.open("GET", url, true);
        var statuses = [];
        http.onreadystatechange = function() {
            if (http.readyState === 4){
                if (http.status == 200) {
                    var response = JSON.parse(http.responseText);
                    console.log("it succeeds! lenght: " );

                    for (i = 0; i < response['statuses'].length; i++ ) {
                        var time = JSON.stringify(response['statuses'][i]['created_at']);
//                        console.log("time: " + time );

                        var text = JSON.stringify(response['statuses'][i]['text']);
//                        console.log("text: " + text);

                        var name = JSON.stringify(response['statuses'][i]['user']['name']);
//                        console.log("name: " + name);

                        var profile_image_url = JSON.stringify(response['statuses'][i]['user']['profile_image_url']);
                        profile_image_url = profile_image_url.slice(1, profile_image_url.length-1);
//                      console.log("profile_image_url: " + profile_image_url);

                        var id = JSON.stringify(response['statuses'][i]['idstr']);

                        statuses.push({'time': time,
                                       'text': text,
                                       'name': name,
                                       'id': id,
                                       'profile_image_url': profile_image_url});
                    }

                    callback(statuses);

                } else {
                    console.log("error: " + http.status)
                    callback(null);
                }
            }
        };
        http.send(null);
    }

    function setResults(data) {
        var results = document.getElementById('results');
        results.innerHTML = data;
    };

}; //onload

我们可以使用如下的句子来得到online account API的接口:

   var api = external.getUnityObject('1.0');
   var oa = api.OnlineAccounts;


在这里,我们使用HTML 5的online account API来检查是否有如下的provider:

var filters = {'provider': 'html5-weibo.ubuntu_account-plugin', 'service': ''};

如果没有,我们使用如下的代码来创建weibo账号:

           if (accounts.length < 1) {
                // setResults('No accounts available');
                oa.api.requestAccount(
                    "html5-weibo.ubuntu_html5weibo",
                    unescape("html5-weibo.ubuntu_account-plugin"),

                    function() {
                        console.log("requestAccount callback...");
                        auth();
                });
                return;
            }

等创建成功了,我们使用“auth()”来得到访问weibo的access token。进而我们可以得到我们所需要的任何的信息。

被创建的账号,可以在“系统设置”里的“账号”中可以看到:



另外我们可以在手机或模拟器中的如下的目录中看到我们所创建的文件:



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

作者:UbuntuTouch 发表于2015/3/16 10:07:57 原文链接
阅读:481 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们来介绍如何使用在线(online)的Webapp生成器来生产在Ubuntu手机或模拟器中可以安装的click安装包。Webapp生成器的地址:https://developer.ubuntu.com/webapp-generator/


1)打开生成器网页,并填入相应的信息


我们在浏览器中打开网页:https://developer.ubuntu.com/webapp-generator/, 并填入应用所需要的信息。请注意:如果你还没有Ubuntu One账号,请注册一个,否则你将不能进入。Ubuntu One账号在上传应用到商店时也是必须有的账号。






等填完我们所需要的信息后,我们点击按钮“Submit”,就可以在屏幕的做下方出现下载的一个叫做类似“wqqcom-uii.liu-xiao-guo_0.1_all.click”的Click安装包。这个文件通常应该在:

/home/HOST_NAME/Downloads

文件夹中(这里“HOSE_NAME”)根据自己的电脑名字的不同而不同。

2)安装应用到我们的手机或模拟器中


我们通过如下的命令来把我们生产的click安装包安装到手机或模拟器中。注意我们上面所生产的click包的名字中含有“all",说明它是对手机和模拟器都适用的安装包。我们打开一个Terminal,并在其中打入如下的命令:

liuxg@liuxg:~/Downloads$ adb push wqqcom-uii.liu-xiao-guo_0.1_all.click /home/phablet



紧接着,我们打入如下的命令:

liuxg@liuxg:~/Downloads$ adb shell
phablet@ubuntu-phablet:~$ pkcon --allow-untrusted install-local wqqcom-uii.liu-xiao-guo_0.1_all.click 





这样我们就把应用安装到手机或模拟器中。如果在手机桌面上没有找到该安装的应用,可以尝试搜索“qq”(对我们的这个例子而言,它的名字中含有qq)。

  


在模拟器中的安装和运行,显示如下:

     

欢迎大家生产自己喜欢的Webapps,并把它上传到我们的商店中。另外值得指出的是:我们也可以适用SDK中的“Web app”模版来生产我们喜欢的在线Webapps。




更多关于在线Webapp的开发的信息可以在地址https://developer.ubuntu.com/zh-cn/web/找到。

作者:UbuntuTouch 发表于2015/3/19 9:31:27 原文链接
阅读:458 评论:0 查看评论

Read more
UbuntuTouch

几天以前,有一个开发者问道如何得到TextArea中的slider的位置信息。目前在QML中的TextArea中并没有这个信息,那么我们如何得到这个信息呢?


1)通过遍历TextArea中的child来得到这个信息


我们可以通过研究TextArea的代码,我们可以发现其中是有一个叫做Flickable的child在里面的。它的里面的contentY含有这个信息,但是可惜的是它不暴露这个信息给我们,那么我们怎么才能得到这个信息呢?

我们设计了如下的测试程序:

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: "textareatest.ubuntu"

    /*
     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: 450
    height: 600

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

        Button {
            id: button
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: textArea.top

            text: "Get contentY"

            onClicked: {
                console.log("it is clicked!");

                // Get hold all of the children of the textArea
                var count = textArea.children.length
                console.log("length: " + count);

                for(var i=0; i < count; i++) {
                    var contentY = textArea.children[i].contentY;

                    if ( contentY === undefined) {
                        console.log("there is no contentY");
                    } else {
                        console.log("contentY: " + contentY);
                    }
                }
            }
        }

        TextArea {
            id: textArea
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            height: 450

            readOnly: true
            wrapMode: TextEdit.Wrap
            text: "this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n"

            onContentHeightChanged: {
                console.log("ContentHeight: " + contentHeight);
                console.log("height: " + height);
            }

//            onContentYChanged: {
//                console.log("contentY: " + contentY);
//            }
        }
    }
}

界面如下:



我们按下“Get contentY”按钮后,可以看到在“Application output”窗口中显示的contentY的值。我们其实使用了如下的句子来实现的:

       Button {
            id: button
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: textArea.top

            text: "Get contentY"

            onClicked: {
                console.log("it is clicked!");

                // Get hold all of the children of the textArea
                var count = textArea.children.length
                console.log("length: " + count);

                for(var i=0; i < count; i++) {
                    var contentY = textArea.children[i].contentY;

                    if ( contentY === undefined) {
                        console.log("there is no contentY");
                    } else {
                        console.log("contentY: " + contentY);
                    }
                }
            }
        }

通过遍历textArea的所有的child,并尝试得到contentY的属性。我们尝试滚动TextArea里的内容,就可以得到不同的contentY的值了。

所有的代码在地址:git clone https://gitcafe.com/ubuntu/TextArea1.git


2)通过重写Ubuntu的TextArea来实现


这个方法可能会导致在不同的framework上不兼容。我们下载相应平台的TextArea,并创建自己的MyTextArea如下:

/*
 * Copyright 2012 Canonical Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components 1.1 as Ubuntu
import "mathUtils.js" as MathUtils

/*!
    \qmltype TextArea
    \inqmlmodule Ubuntu.Components 1.1
    \ingroup ubuntu
    \brief The TextArea item displays a block of editable, scrollable, formatted
    text.

    The TextArea supports fix-size and auto-expanding modes. In fix-size mode the
    content is scrolled when exceeds the boundaries and can be scrolled both horizontally
    and vertically, depending on the contentWidth and contentHeight set. The following
    example will scroll the editing area both horizontally and vertically:

    \qml
    TextArea {
        width: units.gu(20)
        height: units.gu(12)
        contentWidth: units.gu(30)
        contentHeight: units.gu(60)
    }
    \endqml

    The auto-expand mode is realized using two properties: autoSize and maximumLineCount.
    Setting autoSize will set implicitHeight to one line, and the height will follow
    the line count, meaning when lines are added the area will expand and when
    removed the area will shrink. The maximumLineCount specifies how much the
    editor should be expanded. If this value is set to 0, the area will always
    expand vertically to fit the content. When autoSize is set, the contentHeight
    property value is ignored, and the expansion only happens vertically.

    \qml
    TextArea {
        width: units.gu(20)
        height: units.gu(12)
        contentWidth: units.gu(30)
        autoSize: true
        maximumLineCount: 0
    }
    \endqml

    TextArea comes with 30 grid-units implicit width and one line height on auto-sizing
    mode and 4 lines on fixed-mode. The line size is calculated from the font size and the
    ovarlay and frame spacing specified in the style.

    \section2 Scrolling and text selection
    The input is activated when the tap or mouse is released after being pressed
    over the component.

    Scrolling the editing area can happen when the size is fixed or in auto-sizing mode when
    the content size is bigger than the visible area. The scrolling is realized by swipe
    gestures, or by navigating the cursor.

    The content can be selected in the following ways:
    \list
    \li - double tapping/left mouse clicking over the content, when the word that
          had been tapped over will be selected
    \li - by pressing and dragging the selection cursor over the text input. Note
          that there has to be a delay of approx. 200 ms between the press and drag
          gesture, time when the input switches from scroll mode to selection mode
    \endlist

    The input is focused (activated) upon tap/left mouse button release. The cursor
    will be placed at the position the mouse/tap point at release time. If the click
    is happening on a selected area, the selection will be cleared. Long press above
    a selected area brings up the clipboard context menu. When the long press happens
    over a non-selected area, the cursor will be moved to the position and the component
    enters in selection mode. The selection mode can also be activated by tapping and
    keeping the tap/mouse over for approx 300 ms. If there is a move during this time,
    the component enters into scrolling mode. The mode is exited once the scrolling
    finishes. During the scrolling mode the selected text is preserved.

    \note During text selection all interactive parent Flickables are turned off.
  */

StyledItem {
    id: control
    implicitWidth: units.gu(30)
    implicitHeight: (autoSize) ? internal.minimumSize : internal.linesHeight(4)

    property alias contentY: flicker.contentY

    // new properties
    /*!
      The property presents whether the TextArea is highlighted or not. By
      default the TextArea gets highlighted when gets the focus, so can accept
      text input. This property allows to control the highlight separately from
      the focused behavior.
      */
    property bool highlighted: focus
    /*!
      Text that appears when there is no focus and no content in the component
      (hint text).

      \qmlproperty string placeholderText
      */
    property alias placeholderText: hint.text

    /*!
      This property contains the text that is displayed on the screen. May differ
      from the text property value when TextEdit.RichText format is selected.

      \qmlproperty string displayText
      */
    readonly property alias displayText: internal.displayText

    /*!
      The property drives whether text selection should happen with the mouse or
      not. The default value is true.
      \qmlproperty bool selectByMouse
      */
    property alias selectByMouse: editor.selectByMouse

    /*!
      \deprecated
      This property specifies whether the text area expands following the entered
      text or not. The default value is false.
      The property is deprecated, use autoSize instead
      */
    property bool autoExpand
    /*! \internal */
    onAutoExpandChanged: console.debug("WARNING: autoExpand deprecated, use autoSize instead.")

    /*!
      This property specifies whether the text area sizes following the line count
      or not. The default value is false.
      */
    property bool autoSize: false

    /*!
      The property holds the maximum amount of lines to expand when autoSize is
      enabled. The value of 0 does not put any upper limit and the control will
      expand forever.

      The default value is 5 lines.
      */
    property int maximumLineCount: 5

    // altered TextEdit properties
    /*!
      The property folds the width of the text editing content. This can be equal or
      bigger than the frame width minus the spacing between the frame and the input
      area defined in the current theme. The default value is the same as the visible
      input area's width.
      */
    property real contentWidth: control.width - 2 * internal.frameSpacing

    /*!
      The property folds the height of the text editing content. This can be equal or
      bigger than the frame height minus the spacing between the frame and the input
      area defined in the current theme. The default value is the same as the visible
      input area's height.
      */
    property real contentHeight: control.height - 2 * internal.frameSpacing

    /*!
      The property overrides the default popover of the TextArea. When set, the
      TextArea will open the given popover instead of the default one defined.
      The popover can either be a component or a URL to be loaded.
      */
    property var popover

    // forwarded properties
    /*!
      Whether the TextArea should gain active focus on a mouse press. By default this
      is set to true.
      \qmlproperty bool activeFocusOnPress
      */
    property alias activeFocusOnPress: editor.activeFocusOnPress

    /*!
      This property specifies a base URL which is used to resolve relative URLs within
      the text. The default value is the url of the QML file instantiating the TextArea
      item.

      \qmlproperty url baseUrl
      */
    property alias baseUrl: editor.baseUrl

    /*!
      Returns true if the TextArea is writable and the content of the clipboard is
      suitable for pasting into the TextArea.

      \qmlproperty bool canPaste
      */
    property alias canPaste: editor.canPaste

    /*!
      Returns true if the TextArea is writable and there are undone operations that
      can be redone.

      \qmlproperty bool canRedo
      */
    property alias canRedo: editor.canRedo

    /*!
      Returns true if the TextArea is writable and there are previous operations
      that can be undone.

      \qmlproperty bool canUndo
      */
    property alias canUndo: editor.canUndo

    /*!
      The text color.

      \qmlproperty color color
      */
    property alias color: editor.color

    /*!
      The delegate for the cursor in the TextArea.

      If you set a cursorDelegate for a TextArea, this delegate will be used for
      drawing the cursor instead of the standard cursor. An instance of the delegate
      will be created and managed by the text edit when a cursor is needed, and
      the x and y properties of delegate instance will be set so as to be one pixel
      before the top left of the current character.

      Note that the root item of the delegate component must be a QQuickItem or
      QQuickItem derived item.
      */
    property Component cursorDelegate: null

    /*!
      The position of the cursor in the TextArea.

      \qmlproperty int cursorPosition
      */
    property alias cursorPosition: editor.cursorPosition

    /*!
      The rectangle where the standard text cursor is rendered within the text
      edit. Read-only.

      The position and height of a custom cursorDelegate are updated to follow
      the cursorRectangle automatically when it changes. The width of the delegate
      is unaffected by changes in the cursor rectangle.

      \qmlproperty rectangle cursorRectangle
      */
    property alias cursorRectangle: editor.cursorRectangle

    /*!
      If true the text edit shows a cursor.

      This property is set and unset when the text edit gets active focus, but it
      can also be set directly (useful, for example, if a KeyProxy might forward
      keys to it).

      \qmlproperty bool cursorVisible
      */
    property alias cursorVisible: editor.cursorVisible

    /*!
      Presents the effective horizontal alignment that can be different from the one
      specified at horizontalAlignment due to layout mirroring.

      \qmlproperty enumeration effectiveHorizontalAlignment
      */
    property alias effectiveHorizontalAlignment: editor.effectiveHorizontalAlignment

    /*!
      The property holds the font used by the editing.

      \qmlproperty font font
      */
    property alias font: editor.font

    /*!
      Sets the horizontal alignment of the text within the TextAre item's width
      and height. By default, the text alignment follows the natural alignment
      of the text, for example text that is read from left to right will be
      aligned to the left.

      Valid values for effectiveHorizontalAlignment are:
        \list
        \li TextEdit.AlignLeft (default)
        \li TextEdit.AlignRight
        \li TextEdit.AlignHCenter
        \li TextEdit.AlignJustify
        \endlist

      \qmlproperty enumeration horizontalAlignment
      */
    property alias horizontalAlignment: editor.horizontalAlignment

    /*!
      This property holds whether the TextArea has partial text input from an
      input method.

      While it is composing an input method may rely on mouse or key events
      from the TextArea to edit or commit the partial text. This property can
      be used to determine when to disable events handlers that may interfere
      with the correct operation of an input method.

      \qmlproperty bool inputMethodComposing
      */
    property alias inputMethodComposing: editor.inputMethodComposing

    /*!
    Provides hints to the input method about the expected content of the text
    edit and how it should operate.

    The value is a bit-wise combination of flags or Qt.ImhNone if no hints are set.

    Flags that alter behaviour are:
    \list
    \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
    \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method in any persistent storage like predictive user dictionary.
    \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case when a sentence ends.
    \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
    \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
    \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
    \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
    \li Qt.ImhDate - The text editor functions as a date field.
    \li Qt.ImhTime - The text editor functions as a time field.
    \endlist
    Flags that restrict input (exclusive flags) are:

    \list
    \li Qt.ImhDigitsOnly - Only digits are allowed.
    \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
    \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
    \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
    \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
    \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
    \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
    \endlist
    Masks:

    \list
    \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
    \endlist

      \qmlproperty enumeration inputMethodHints
      */
    property alias inputMethodHints: editor.inputMethodHints

    /*!
      Returns the total number of plain text characters in the TextArea item.

      As this number doesn't include any formatting markup it may not be the
      same as the length of the string returned by the text property.

      This property can be faster than querying the length the text property
      as it doesn't require any copying or conversion of the TextArea's internal
      string data.

      \qmlproperty int length
      */
    property alias length: editor.length

    /*!
      Returns the total number of lines in the TextArea item.

      \qmlproperty int lineCount
      */
    property alias lineCount: editor.lineCount

    /*!
      Specifies how text should be selected using a mouse.
        \list
        \li TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
        \li TextEdit.SelectWords - The selection is updated with whole words.
        \endlist
      This property only applies when selectByMouse is true.

      \qmlproperty enumeration mouseSelectionMode
      */
    property alias mouseSelectionMode: editor.mouseSelectionMode

    /*!
      Whether the TextArea should keep the selection visible when it loses active
      focus to another item in the scene. By default this is set to true;

      \qmlproperty enumeration persistentSelection
      */
    property alias persistentSelection: editor.persistentSelection

    /*!
      Whether the user can interact with the TextArea item. If this property is set
      to true the text cannot be edited by user interaction.

      By default this property is false.

      \qmlproperty bool readOnly
      */
    property alias readOnly: editor.readOnly

    /*!
      Override the default rendering type for this component.

      Supported render types are:
        \list
        \li Text.QtRendering - the default
        \li Text.NativeRendering
        \endlist
      Select Text.NativeRendering if you prefer text to look native on the target
      platform and do not require advanced features such as transformation of the
      text. Using such features in combination with the NativeRendering render type
      will lend poor and sometimes pixelated results.

      \qmlproperty enumeration renderType
      */
    property alias renderType: editor.renderType

    /*!
      This read-only property provides the text currently selected in the text edit.

      \qmlproperty string selectedText
      */
    property alias selectedText: editor.selectedText

    /*!
      The selected text color, used in selections.

      \qmlproperty color selectedTextColor
      */
    property alias selectedTextColor: editor.selectedTextColor

    /*!
      The text highlight color, used behind selections.

      \qmlproperty color selectionColor
      */
    property alias selectionColor: editor.selectionColor

    /*!
      The cursor position after the last character in the current selection.

      This property is read-only. To change the selection, use select(start, end),
      selectAll(), or selectWord().

      See also selectionStart, cursorPosition, and selectedText.

      \qmlproperty int selectionEnd
      */
    property alias selectionEnd: editor.selectionEnd

    /*!
      The cursor position before the first character in the current selection.

      This property is read-only. To change the selection, use select(start, end),
      selectAll(), or selectWord().

      See also selectionEnd, cursorPosition, and selectedText.

      \qmlproperty int selectionStart
      */
    property alias selectionStart: editor.selectionStart

    /*!
      The text to display. If the text format is AutoText the text edit will
      automatically determine whether the text should be treated as rich text.
      This determination is made using Qt::mightBeRichText().

      \qmlproperty string text
      */
    property alias text: editor.text

    /*!
      The way the text property should be displayed.
        \list
        \li TextEdit.AutoText
        \li TextEdit.PlainText
        \li TextEdit.RichText
        \endlist
      The default is TextEdit.PlainText. If the text format is TextEdit.AutoText
      the text edit will automatically determine whether the text should be treated
      as rich text. This determination is made using Qt::mightBeRichText().

      \qmlproperty enumeration textFormat
      */
    property alias textFormat: editor.textFormat

    /*!
      Sets the vertical alignment of the text within the TextAres item's width
      and height. By default, the text alignment follows the natural alignment
      of the text.

      Valid values for verticalAlignment are:

        \list
        \li TextEdit.AlignTop (default)
        \li TextEdit.AlignBottom
        \li TextEdit.AlignVCenter
        \endlist

        \qmlproperty enumeration verticalAlignment
      */
    property alias verticalAlignment: editor.verticalAlignment

    /*!
      Set this property to wrap the text to the TextEdit item's width. The text
      will only wrap if an explicit width has been set.
        \list
        \li TextEdit.NoWrap - no wrapping will be performed. If the text contains
            insufficient newlines, then implicitWidth will exceed a set width.
        \li TextEdit.WordWrap - wrapping is done on word boundaries only. If a word
            is too long, implicitWidth will exceed a set width.
        \li TextEdit.WrapAnywhere - wrapping is done at any point on a line, even
            if it occurs in the middle of a word.
        \li TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise
            it will occur at the appropriate point on the line, even in the middle of a word.
        \endlist
       The default is TextEdit.Wrap

       \qmlproperty enumeration wrapMode
      */
    property alias wrapMode:editor.wrapMode

    // signals
    /*!
      This handler is called when the user clicks on a link embedded in the text.
      The link must be in rich text or HTML format and the link string provides
      access to the particular link.
      */
    signal linkActivated(string link)

    // functions
    /*!
      Copies the currently selected text to the system clipboard.
      */
    function copy()
    {
        editor.copy();
    }

    /*!
      Moves the currently selected text to the system clipboard.
      */
    function cut()
    {
        editor.cut();
    }

    /*!
      Removes active text selection.
      */
    function deselect()
    {
        editor.deselect();
    }

    /*!
      Inserts text into the TextArea at position.
      */
    function insert(position, text)
    {
        editor.insert(position, text);
    }

    /*!
      Returns the text position closest to pixel position (x, y).

      Position 0 is before the first character, position 1 is after the first
      character but before the second, and so on until position text.length,
      which is after all characters.
      */
    function positionAt(x, y)
    {
        return editor.positionAt(x, y);
    }

    /*!
      Returns true if the natural reading direction of the editor text found
      between positions start and end is right to left.
      */
    function isRightToLeft(start, end)
    {
        return editor.isRightToLeft(start, end)
    }

    /*!
      Moves the cursor to position and updates the selection according to the
      optional mode parameter. (To only move the cursor, set the cursorPosition
      property.)

      When this method is called it additionally sets either the selectionStart
      or the selectionEnd (whichever was at the previous cursor position) to the
      specified position. This allows you to easily extend and contract the selected
      text range.

      The selection mode specifies whether the selection is updated on a per character
      or a per word basis. If not specified the selection mode will default to whatever
      is given in the mouseSelectionMode property.
      */
    function moveCursorSelection(position, mode)
    {
        if (mode === undefined)
            editor.moveCursorSelection(position, mouseSelectionMode);
        else
            editor.moveCursorSelection(position, mode);
    }

    /*!
      \preliminary
      Places the clipboard or the data given as parameter into the text input.
      The selected text will be replaces with the data.
    */
    function paste(data) {
        if ((data !== undefined) && (typeof data === "string") && !editor.readOnly) {
            var selTxt = editor.selectedText;
            var txt = editor.text;
            var pos = (selTxt !== "") ? txt.indexOf(selTxt) : editor.cursorPosition
            if (selTxt !== "") {
                editor.text = txt.substring(0, pos) + data + txt.substr(pos + selTxt.length);
            } else {
                editor.text = txt.substring(0, pos) + data + txt.substr(pos);
            }
            editor.cursorPosition = pos + data.length;
        } else
            editor.paste();
    }

    /*!
      Returns the rectangle at the given position in the text. The x, y, and height
      properties correspond to the cursor that would describe that position.
      */
    function positionToRectangle(position)
    {
        return editor.positionToRectangle(position);
    }

    /*!
      Redoes the last operation if redo is \l{canRedo}{available}.
      */
    function redo()
    {
        editor.redo();
    }

    /*!
      Causes the text from start to end to be selected.

      If either start or end is out of range, the selection is not changed.

      After calling this, selectionStart will become the lesser and selectionEnd
      will become the greater (regardless of the order passed to this method).

      See also selectionStart and selectionEnd.
      */
    function select(start, end)
    {
        editor.select(start, end);
    }

    /*!
      Causes all text to be selected.
      */
    function selectAll()
    {
        editor.selectAll();
    }

    /*!
      Causes the word closest to the current cursor position to be selected.
      */
    function selectWord()
    {
        editor.selectWord();
    }

    /*!
      Returns the section of text that is between the start and end positions.

      The returned text will be formatted according the textFormat property.
      */
    function getFormattedText(start, end)
    {
        return editor.getFormattedText(start, end);
    }

    /*!
      Returns the section of text that is between the start and end positions.

      The returned text does not include any rich text formatting. A getText(0, length)
      will result in the same value as displayText.
      */
    function getText(start, end)
    {
        return editor.getText(start, end);
    }

    /*!
      Removes the section of text that is between the start and end positions
      from the TextArea.
      */
    function remove(start, end)
    {
        return editor.remove(start, end);
    }

    /*!
      Undoes the last operation if undo is \l{canUndo}{available}. Deselects
      any current selection, and updates the selection start to the current
      cursor position.
      */
    function undo()
    {
        editor.undo();
    }

    // logic
    /*!\internal - to remove warnings */
    Component.onCompleted: {
        editor.linkActivated.connect(control.linkActivated);
    }

    // activation area on mouse click
    // the editor activates automatically when pressed in the editor control,
    // however that one can be slightly spaced to the main control area
    MouseArea {
        anchors.fill: parent
        enabled: internal.frameSpacing > 0
        acceptedButtons: Qt.LeftButton | Qt.RightButton
        // activate input when pressed on the frame
        preventStealing: false
        Ubuntu.Mouse.forwardTo: [inputHandler]
    }

    //internals

    opacity: enabled ? 1.0 : 0.3

    /*!\internal */
    onVisibleChanged: {
        if (!visible)
            control.focus = false;
    }

    LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
    LayoutMirroring.childrenInherit: true

    QtObject {
        id: internal
        // public property locals enabling aliasing
        property string displayText: editor.getText(0, editor.length)
        property real frameSpacing: control.__styleInstance.frameSpacing
        property real minimumSize: units.gu(4)

        function linesHeight(lines)
        {
            var lineHeight = editor.font.pixelSize * lines + inputHandler.lineSpacing * lines
            return lineHeight + 2 * frameSpacing;
        }

        function frameSize()
        {
            if (control.autoSize) {
                var max = (control.maximumLineCount <= 0) ?
                            control.lineCount :
                            Math.min(control.maximumLineCount, control.lineCount);
                control.height = linesHeight(MathUtils.clamp(control.lineCount, 1, max));
            }
        }
    }

    // grab Enter/Return keys which may be stolen from parent components of TextArea
    // due to forwarded keys from TextEdit
    Keys.onPressed: {
        if ((event.key === Qt.Key_Enter) || (event.key === Qt.Key_Return)) {
            if (editor.textFormat === TextEdit.RichText) {
                // FIXME: use control.paste("<br />") instead when paste() gets sich text support
                editor.insert(editor.cursorPosition, "<br />");
            } else {
                control.paste("\n");
            }
            event.accepted = true;
        } else {
            event.accepted = false;
        }
    }
    Keys.onReleased: event.accepted = (event.key === Qt.Key_Enter) || (event.key === Qt.Key_Return)

    // holding default values
    Label { id: fontHolder }

    //hint
    Label {
        id: hint
        anchors {
            fill: parent
            margins: internal.frameSpacing
        }
        // hint is shown till user types something in the field
        visible: (editor.getText(0, editor.length) == "") && !editor.inputMethodComposing
        color: Theme.palette.normal.backgroundText
        fontSize: "medium"
        elide: Text.ElideRight
        wrapMode: Text.WordWrap
    }

    //scrollbars and flickable
    Scrollbar {
        id: rightScrollbar
        flickableItem: flicker
    }
    Scrollbar {
        id: bottomScrollbar
        flickableItem: flicker
        align: Qt.AlignBottom
    }
    Flickable {
        id: flicker
        objectName: "input_scroller"
        anchors {
            fill: parent
            margins: internal.frameSpacing
        }
        clip: true
        contentWidth: editor.paintedWidth
        contentHeight: editor.paintedHeight
        // do not allow rebounding
        boundsBehavior: Flickable.StopAtBounds

        // editor
        // Images are not shown when text contains <img> tags
        // bug to watch: https://bugreports.qt-project.org/browse/QTBUG-27071
        TextEdit {
            objectName: "text_input"
            readOnly: false
            id: editor
            focus: true
            width: control.contentWidth
            height: Math.max(control.contentHeight, editor.contentHeight)
            wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
            mouseSelectionMode: TextEdit.SelectWords
            selectByMouse: true
            activeFocusOnPress: true
            cursorDelegate: TextCursor {
                handler: inputHandler
            }
            color: control.__styleInstance.color
            selectedTextColor: Theme.palette.selected.foregroundText
            selectionColor: Theme.palette.selected.selection
            font.pixelSize: FontUtils.sizeToPixels("medium")
            // forward keys to the root element so it can be captured outside of it
            // as well as to InputHandler to handle PageUp/PageDown keys
            Keys.forwardTo: [control, inputHandler]

            // autosize handling
            onLineCountChanged: internal.frameSize()

            // input selection and navigation handling
            Ubuntu.Mouse.forwardTo: [inputHandler]
            InputHandler {
                id: inputHandler
                anchors.fill: parent
                main: control
                input: editor
                flickable: flicker
                frameDistance: Qt.point(flicker.x, flicker.y)
            }
        }
    }

    style: Theme.createStyleComponent("TextAreaStyle.qml", control)
}

这里我们加入了:

    property alias contentY: flicker.contentY

通过这样的方法,我们可以使得flicker的contentY的属性得以暴露,并被外面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: "textareatest.ubuntu"

    /*
     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: 450
    height: 600

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

        Button {
            id: button
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: textArea.top

            text: "Get contentY"

            onClicked: {
                console.log("it is clicked!");

                // Get hold all of the children of the textArea
                var count = textArea.children.length
                console.log("length: " + count);

                for(var i=0; i < count; i++) {
                    var contentY = textArea.children[i].contentY;

                    if ( contentY === undefined) {
                        console.log("there is no contentY");
                    } else {
                        console.log("contentY: " + contentY);
                    }
                }
            }
        }

        MyTextArea {
            id: textArea
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            height: 450

            readOnly: true
            wrapMode: TextEdit.Wrap
            text: "this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n
                   this is cool!\n"

            onContentHeightChanged: {
                console.log("ContentHeight: " + contentHeight);
                console.log("height: " + height);
            }

            onContentYChanged: {
                console.log("contentY: " + contentY);
            }
        }
    }
}





当我们滚动时,就可以看见contentY的属性的变化。

这个工程的代码在: git clone https://gitcafe.com/ubuntu/TextArea2.git
作者:UbuntuTouch 发表于2015/4/13 9:26:54 原文链接
阅读:319 评论:0 查看评论

Read more
UbuntuTouch

我们知道在触屏的手机中,可以利用手势可以产生一下动作。特别是Ubuntu手机,手势的操作利用的非常多。那么怎么可以在QML应用中侦测到手势呢?我以前在我的Flickr应用使用到一个手势的侦测。今天我们利用网上的一个例程来,做一个例子。这个例程更加具有可以重复利用性。我们的参阅代码地址:https://gist.github.com/kovrov/1742405


SwipeArea.qml


/* This code was written by Sergejs Kovrovs and has been placed in the public domain. */

import QtQuick 2.0


MouseArea {
    property point origin
    property bool ready: false
    property int threshold: units.gu(20)
    signal move(int x, int y)
    signal swipe(string direction)

    onPressed: {
        drag.axis = Drag.XAndYAxis
        origin = Qt.point(mouse.x, mouse.y)
    }

    onPositionChanged: {
        switch (drag.axis) {
        case Drag.XAndYAxis:
            if (Math.abs(mouse.x - origin.x) > threshold ) {
                drag.axis = Drag.XAxis
            }
            else if (Math.abs(mouse.y - origin.y) > threshold) {
                drag.axis = Drag.YAxis
            }
            break
        case Drag.XAxis:
            move(mouse.x - origin.x, 0)
            break
        case Drag.YAxis:
            move(0, mouse.y - origin.y)
            break
        }
    }

    onReleased: {
        switch (drag.axis) {
        case Drag.XAndYAxis:
            canceled(mouse)
            break
        case Drag.XAxis:
            swipe(mouse.x - origin.x < 0 ? "left" : "right")
            break
        case Drag.YAxis:
            swipe(mouse.y - origin.y < 0 ? "up" : "down")
            break
        }
    }
}

这里的代码定义了一个新的MouseArea的Component。这个Component可以在任何其它需要用到MouseArea的地方用到。你也可以理解为MouseArea的重载(从C++的角度,虽然并不精确)。我们对原作者的代码没有更多的改动,但是,我重新定义了一个“threshold”。这个是用来调整我们的Swipe的灵敏度的。比如,一个较大的值,使得我们必须使用较大的滑动才可以产生swipe的信号。较小的值,使得swipe的侦测更加容易。这个可以根据我们在实际的应用中进行调整。

那么我们如何使用这个SwipeArea的Component呢?

Swipe.qml


/* This code was written by Sergejs Kovrovs and has been placed in the public domain. */

import QtQuick 2.0

Item {
    id: root
    width: 480
    height: 320

    property var itemData: ["#22eeeeee", "#22bbbbbb", "#22888888", "#22555555", "#22222222"]
    property int currentIndex: 0

    onCurrentIndexChanged: {
        slide_anim.to = - root.width * currentIndex
        slide_anim.start()
    }

    PropertyAnimation {
        id: slide_anim
        target: content
        easing.type: Easing.OutExpo
        properties: "x"
    }

    Image {
        id: img
        anchors.verticalCenter: root.verticalCenter
        source: "images/wallpaper.jpg"
        fillMode: Image.PreserveAspectCrop
    }

    Item {
        id: content
        width: root.width * itemData.length
        property double k: (content.width - root.width) / (img.width - root.width)
        onXChanged: {
            img.x = x / k
//            console.log("img.x: " + img.x );
        }
        Repeater {
            model: itemData.length
            Rectangle {
                x: root.width * index
                width: root.width; height: root.height
                color: itemData[index]
                Text { text: index+1; anchors.centerIn: parent; font.pointSize: 100; color: "#88000000" }
            }
        }
    }

    SwipeArea {
        id: mouse
        anchors.fill: parent
        onMove: {
            content.x = (-root.width * currentIndex) + x
//            console.log("content.x " + content.x);
        }
        onSwipe: {
            switch (direction) {
            case "left":
                if (currentIndex === itemData.length - 1) {
                    currentIndexChanged()
                }
                else {
                    currentIndex++
                }
                break
            case "right":
                if (currentIndex === 0) {
                    currentIndexChanged()
                }
                else {
                    currentIndex--
                }
                break
            }
        }
        onCanceled: {
            currentIndexChanged()
        }
    }

    Row {
        anchors { bottom: parent.bottom; bottomMargin: 16; horizontalCenter: parent.horizontalCenter }
        spacing: 16
        Repeater {
            model: itemData.length
            Rectangle {
                width: 12; height: 12; radius: 6
                color: currentIndex === index ? "#88ffffff" : "#88000000"
                border { width: 2; color: currentIndex === index ? "#33000000" : "#11000000" }
            }
        }
    }
}

就像我们正常情况下使用MouseArea的情况一样,我们可以在我们需要侦测滑动的地方使用SwipeArea。这样,我们只需要捕获SwipeArea中发出的信号:

    SwipeArea {
        id: mouse
        anchors.fill: parent
        onMove: {
            content.x = (-root.width * currentIndex) + x
//            console.log("content.x " + content.x);
        }
        onSwipe: {
            switch (direction) {
            case "left":
                if (currentIndex === itemData.length - 1) {
                    currentIndexChanged()
                }
                else {
                    currentIndex++
                }
                break
            case "right":
                if (currentIndex === 0) {
                    currentIndexChanged()
                }
                else {
                    currentIndex--
                }
                break
            }
        }
        onCanceled: {
            currentIndexChanged()
        }
    }

这里,我们可以捕获onSwipe就可以得到Swipe的方向。在本例子中,我们只对左右感兴趣。我们可以通过手势向左或向右滑动来看一个图片的其它的部分。

  

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


作者:UbuntuTouch 发表于2015/4/27 9:41:58 原文链接
阅读:370 评论:0 查看评论

Read more
UbuntuTouch

我们可以在Ubuntu QML的API文档中看到Camera的用法,但是里面没有写到任何的前置Camera的调用方法。对于一些应用来说,前置Camera的使用是重要的。我们必须使用Qt C++代码来实现这个功能。在这篇文章中,我们来介绍如何使用Ubuntu手机中的前置照相机。


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


   

   

这样我们就生成了一个含有plugin的项目。

2)在项目中加入CameraSelector来选择照相机


为了能够调用Camera的一些API,我们在plugin中加入如下的CameraSelector类:

#ifndef CAMERA_SELECTOR_H
#define CAMERA_SELECTOR_H

#include <QObject>
#include <QCamera>
#include <QVideoDeviceSelectorControl>

class CameraSelector : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QObject* cameraObject READ cameraObject WRITE setCameraObject)
    Q_PROPERTY(int selectedCameraDevice READ selectedCameraDevice WRITE setSelectedCameraDevice)

public:
    QObject* cameraObject() const;
    void setCameraObject(QObject *cam);

    int selectedCameraDevice() const;
    void setSelectedCameraDevice(const int cameraId);

private:
    QCamera *m_camera;
    QVideoDeviceSelectorControl *m_deviceSelector;
};

#endif // CAMERA_SELECTOR_H


#include "cameraselector.h"

#include <QMediaService>

void CameraSelector::setCameraObject(QObject *cam)
{
    // get the QCamera from the declarative camera's mediaObject property.
    m_camera = qvariant_cast<QCamera*>(cam->property("mediaObject"));

    // get the video device selector control
    QMediaService *service = m_camera->service();
    m_deviceSelector = qobject_cast<QVideoDeviceSelectorControl*>(service->requestControl(QVideoDeviceSelectorControl_iid));
}


QObject *CameraSelector::cameraObject() const
{
    return m_camera;
}

int CameraSelector::selectedCameraDevice() const
{
     return 0;
}

void CameraSelector::setSelectedCameraDevice(const int cameraId)
{
    // A camera might already be started, make sure it's unloaded
    m_camera->unload();

    m_deviceSelector->setSelectedDevice(cameraId);
}


我们在“backend”中的CMakeLists.txt中加入“MultiMedia"库以调用QCamera。

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
)

set(
    FrontCamerabackend_SRCS
    modules/FrontCamera/backend.cpp
    modules/FrontCamera/mytype.cpp
    modules/FrontCamera/cameraselector.cpp
)

add_library(FrontCamerabackend MODULE
    ${FrontCamerabackend_SRCS}
)

set_target_properties(FrontCamerabackend PROPERTIES
         LIBRARY_OUTPUT_DIRECTORY FrontCamera)

qt5_use_modules(FrontCamerabackend Gui Qml Quick Multimedia)

# Copy qmldir file to build dir for running in QtCreator
add_custom_target(FrontCamerabackend-qmldir ALL
    COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/modules/FrontCamera/qmldir ${CMAKE_CURRENT_BINARY_DIR}/FrontCamera
    DEPENDS ${QMLFILES}
)

# Install plugin file
install(TARGETS FrontCamerabackend DESTINATION ${QT_IMPORTS_DIR}/FrontCamera/)
install(FILES   modules/FrontCamera/qmldir DESTINATION ${QT_IMPORTS_DIR}/FrontCamera/)


同时在backend.cpp中加入如下的句子:

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


为了使用Camera,我们对我们的FrontCamera.qml做如下的修改:

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

/*!
    \brief MainView with Tabs element.
           First Tab has a single Label and
           second Tab has a single ToolbarAction.
*/

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

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

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

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

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

    Page {
        title: i18n.tr("App with backend")

        Button {
            id: activateRearCamera
            text: "Rear Camera"

            onClicked: {
                selector. selectedCameraDevice = 0;
                camera.start();
            }
        }

        Button {
            id: activateFrontCamera
            text: "Front camera"
            anchors.left : activateRearCamera.right
            anchors.leftMargin: units.gu(2)
            onClicked: {
                selector. selectedCameraDevice = 1;
                camera.start();
            }
        }

        Camera {
            id: camera

            imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

            flash.mode: Camera.FlashRedEyeReduction

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

        CameraSelector {
            id: selector
            cameraObject: camera
        }

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

        Image {
            id: photoPreview
        }
    }
}


运行我们的应用:

  


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

作者:UbuntuTouch 发表于2015/4/8 12:55:25 原文链接
阅读:259 评论:0 查看评论

Read more
UbuntuTouch

前一段时间,有个开发者问我能否在Ubuntu手机中使用QtQuick.Dialogs来实现FileDialog。目前在手机上没有Qt这个库的实现。最主要的原因是它不使用unit grid的方式来布局,所以在真的手机上显得非常小。那么我们怎么才能实现同样的功能呢?


我们首先来查看一下我们的Ubuntu Qt所提供的API Dialog。这里我们有提供一个Dialog的control.我们可以仿照上面的例程来写出我们所需要的例程。另外,我们也需要使用另外一个API FolderListModel。通过这个API我们可以得到我们所需要的文件的目录的所有的文件:


通过对这两个API的了解和和使用,我们可以很快的做出如下的测试应用:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.Popups 0.1
import Qt.labs.folderlistmodel 2.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: "filedialog.ubuntu"

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

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

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

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

        Button {
            id: launcher
            text: i18n.tr("Open")
            width: units.gu(16)
            onClicked: PopupUtils.open(dialog, null)
        }

        Component {
            id: dialog
            Dialog {
                id: dialogue

                title: "FileList Dialog"
                text: "Show the files"

                ListView {
                    id: listview
                    width: parent.width
                    height: 200

                    FolderListModel {
                        id: folderModel
                        nameFilters: ["*.qml"]                      
                    }

                    Component {
                        id: fileDelegate
                        Text {
                            id: text
                            text: fileName
                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    console.log("it is clicked");
                                    listview.currentIndex = index;
                                }
                            }
                        }
                    }

                    // Define a highlight with customized movement between items.
                    Component {
                        id: highlightBar
                        Rectangle {
                            width: fileDelegate.width; height: 50
                            color: "red"
                            y: listview.currentItem.y;
                            Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
                        }
                    }

                    focus: true
                    model: folderModel
                    delegate: fileDelegate
                    highlight: highlightBar

                }

                Row {
                    id: row
                    width: parent.width
                    spacing: units.gu(1)
                    Button {
                        width: parent.width/2
                        text: "Cancel"
                        onClicked: PopupUtils.close(dialogue)
                    }

                    Button {
                        width: parent.width/2
                        text: "Confirm"
                        color: UbuntuColors.green
                        onClicked: {
                            console.log("caller: " + dialogue.caller);
                            console.log("currentIndex: " + listview.currentIndex);
                            console.log(folderModel.get(listview.currentIndex, "fileName"));
                            launcher.update();
                            PopupUtils.close(dialogue)
                        }
                    }
                }
            }
        }
    }
}

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

  


这里必须指出的是FolderListModel并不是可以访问系统的任何一个目录的。我们可以参考文章“Ubuntu OS应用Runtime Enviroment”来得到哪些目录是可以访问的。对访问另外一个应用所拥有的文件来说,我们可以通过使用contenthub来访问。


所有应用的源码在:git clone https://gitcafe.com/ubuntu/filedialog.git




作者:UbuntuTouch 发表于2015/4/15 11:04:28 原文链接
阅读:322 评论:0 查看评论

Read more
UbuntuTouch

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


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


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtFeedback 5.0

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

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

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

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

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

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

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

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

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

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

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

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

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

import QtFeedback 5.0

然后,实例化我们的HapticsEffect:

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

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


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

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

Read more
UbuntuTouch

我们知道目前Ubuntu手机平台有些类似iPhone平台,是一个单任务的操作系统,虽然系统本身具有多任务的功能。如果当前的应用被推到后台的话,应用将会被自动挂起,而不会被系统所运行。在这个时候如果我们的应用需要等待一个消息,比如就想微信之类的信息,我们就要使用Ubuntu平台所提供的Push Notification机制来实现我们的类似多任务的东西。当通知被收到后,我们就可以直接点击接受到的通知,应用又会被重新运行到前台。


关于Push notification,在我们的开发者网站上,有一篇文章(client)和一篇文章(server)详细介绍了它的机制。这里我不想讲太多的东西。有兴趣的同学们可以详读那篇文章。今天在这里,我来和大家分析一个具体的实例,以更好地了解如何在Ubuntu手机上实现这个功能。




在上述的图中可以看出来,整个系统的组成分两部分:客户端及服务器端。在服务器端,又分为一个PushSever (https://push.ubuntu.com)及一个App Server。App server是用来管理我们的用户的Nick Name及Token的。在它的里面,有一个数据库。


为了测试,开发者必须有一个Ubuntu One的账号。我们需要在手机的“系统设置”里的账号中创建这个账号。


当一个QML应用在使用:


import Ubuntu.PushNotifications 0.1

PushClient {
    id: pushClient
    Component.onCompleted: {
        notificationsChanged.connect(messageList.handle_notifications)
        error.connect(messageList.handle_error)
    }
    appId: "com.ubuntu.developer.push.hello_hello"
}


当我们使用上面的API后,push server将向我们的客户端发送一个token。这个token依赖于手机自己的参数及上面所看到的“appId”。利用这个token,我们可以向我们的应用服务器注册,并存于应用服务器端中。当我们需要发送信息的时候,我们必须注册一个类似nickname的东西。这个nickname将和我们手机客户端的token绑定。每当另外一个nickname想像我们发送信息时,应用服务器端可以通过数据库的查询来得到我们的token,从而更进一步通过push server来向我们的客服端推送信息。如果我们的客户端想向其它的客户端发送信息,这其中的道理,也是和刚才一样。


目前,在我们的开发者网站并没有PushClient的具体的介绍。我们可以使用在文章“ 如何得到QML package的详细API接口”中的方法来了解这个API。

Push server是用来推送信息。它位于 https://push.ubuntu.com。它只有一个endpoint:/notify。为了向一个用户发送推送信息。应用服务器可以向Push Sever发送一个含有“Content-type: application/json”的HTTP POST信息来推送我们的信息。下面是一个POST body的一个样板内容:


{
        "appid": "com.ubuntu.music_music",
        "expire_on": "2014-10-08T14:48:00.000Z",
        "token": "LeA4tRQG9hhEkuhngdouoA==",
        "clear_pending": true,
        "replace_tag": "tagname",
        "data": {
                "message": "foobar",
                "notification": {
                        "card": {
                                "summary": "yes",
                                "body": "hello",
                                "popup": true,
                                "persist": true
                        }
                        "sound": "buzz.mp3",
                        "tag": "foo",
                        "vibrate": {
                                "duration": 200,
                                "pattern": (200, 100),
                                "repeat": 2
                        }
                        "emblem-counter": {
                                "count": 12,
                                "visible": true
                        }
                }
        }
}



appid: ID of the application that will receive the notification, as described in the client side documentation.
expire_on: Expiration date/time for this message, in ISO8601 Extendend format
token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
data: A JSON object.
从上面的信息格式,我们可以看出来,token是非常重要的一个信息。有了它,我们就可以向我们需要的终端发送推送信息。


我们可以利用我们的SDK来创建一个简单的例程。下面简单介绍一下我们的主要的文件main.qml:


import QtQuick 2.0
import Qt.labs.settings 1.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
import Ubuntu.PushNotifications 0.1
import "components"

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

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

    automaticOrientation: true
    useDeprecatedToolbar: false

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

    Settings {
        property alias nick: chatClient.nick
        property alias nickText: nickEdit.text
        property alias nickPlaceholder: nickEdit.placeholderText
        property alias nickEnabled: nickEdit.enabled
    }

    states: [
        State {
            name: "no-push-token"
            when: (pushClient.token == "")
            PropertyChanges { target: nickEdit; readOnly: true}
            PropertyChanges { target: nickEdit; focus: true}
            PropertyChanges { target: messageEdit; enabled: false}
            PropertyChanges { target: loginButton; enabled: false}
            PropertyChanges { target: loginButton; text: "Login"}
        },
        State {
            name: "push-token-not-registered"
            when: ((pushClient.token != "") && (chatClient.registered == false))
            PropertyChanges { target: nickEdit; readOnly: false}
            PropertyChanges { target: nickEdit; text: ""}
            PropertyChanges { target: nickEdit; focus: true}
            PropertyChanges { target: messageEdit; enabled: false}
            PropertyChanges { target: loginButton; enabled: true}
            PropertyChanges { target: loginButton; text: "Login"}
        },
        State {
            name: "registered"
            when: ((pushClient.token != "") && (chatClient.registered == true))
            PropertyChanges { target: nickEdit; readOnly: true}
            PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick}
            PropertyChanges { target: messageEdit; focus: true}
            PropertyChanges { target: messageEdit; enabled: true}
            PropertyChanges { target: loginButton; enabled: true}
            PropertyChanges { target: loginButton; text: "Logout"}
        }
    ]

    state: "no-push-token"

    ChatClient {
        id: chatClient
        onError: {messageList.handle_error(msg)}
        token: {
            var i = {
                "from" : "",
                "to" :  "",
                "message" : "Token: " + pushClient.token
            }

            if ( pushClient.token )
                messagesModel.insert(0, i);
            console.log("token is changed!");
            return pushClient.token;
        }
    }

    PushClient {
        id: pushClient
        Component.onCompleted: {
            notificationsChanged.connect(messageList.handle_notifications)
            error.connect(messageList.handle_error)
            onTokenChanged: {
                console.log("token: +" + pushClient.token );
                console.log("foooooo")
            }
        }
        appId: "com.ubuntu.developer.ralsina.hello_hello"

    }

    TextField {
        id: nickEdit
        placeholderText: "Your nickname"
        inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase
        anchors.left: parent.left
        anchors.right: loginButton.left
        anchors.top: parent.top
        anchors.leftMargin: units.gu(.5)
        anchors.rightMargin: units.gu(1)
        anchors.topMargin: units.gu(.5)
        onAccepted: { loginButton.clicked() }
    }

    Button {
        id: loginButton
        anchors.top: nickEdit.top
        anchors.right: parent.right
        anchors.rightMargin: units.gu(.5)
        onClicked: {
            if (chatClient.nick) { // logout
                chatClient.nick = ""
            } else { // login
                chatClient.nick = nickEdit.text
            }
        }
    }

    TextField {
        id: messageEdit
        inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase
        anchors.right: parent.right
        anchors.left: parent.left
        anchors.top: nickEdit.bottom
        anchors.topMargin: units.gu(1)
        anchors.rightMargin: units.gu(.5)
        anchors.leftMargin: units.gu(.5)
        placeholderText: "Your message"
        onAccepted: {
            console.log("sending " + text)
            var idx = text.indexOf(":")
            var nick_to = text.substring(0, idx).trim()
            var msg = text.substring(idx+1, 9999).trim()
            var i = {
                "from" :  chatClient.nick,
                "to" :  nick_to,
                "message" : msg
            }
            var o = {
                enabled: annoyingSwitch.checked,
                persist: persistSwitch.checked,
                popup: popupSwitch.checked,
                sound: soundSwitch.checked,
                vibrate: vibrateSwitch.checked,
                counter: counterSlider.value
            }
            chatClient.sendMessage(i, o)
            i["type"] = "sent"
            messagesModel.insert(0, i)
            text = ""
        }
    }
    ListModel {
        id: messagesModel
        ListElement {
            from: ""
            to: ""
            type: "info"
            message: "Register by typing your nick and clicking Login."
        }
        ListElement {
            from: ""
            to: ""
            type: "info"
            message: "Send messages in the form \"destination: hello\""
        }
        ListElement {
            from: ""
            to: ""
            type: "info"
            message: "Slide from the bottom to control notification behaviour."
        }
    }

    UbuntuShape {
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: notificationSettings.bottom
        anchors.top: messageEdit.bottom
        anchors.topMargin: units.gu(1)
        ListView {
            id: messageList
            model: messagesModel
            anchors.fill: parent
            delegate: Rectangle {
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        if (from != "") {
                            messageEdit.text = from + ": "
                            messageEdit.focus = true
                        }
                    }
                }
                height: label.height + units.gu(2)
                width: parent.width
                Rectangle {
                    color: {
                        "info": "#B5EBB9",
                        "received" : "#A2CFA5",
                        "sent" : "#FFF9C8",
                        "error" : "#FF4867"}[type]
                    height: label.height + units.gu(1)
                    anchors.fill: parent
                    radius: 5
                    anchors.margins: units.gu(.5)
                    Text {
                        id: label
                        text: "<b>" + ((type=="sent")?to:from) + ":</b> " + message
                        wrapMode: Text.Wrap
                        width: parent.width - units.gu(1)
                        x: units.gu(.5)
                        y: units.gu(.5)
                        horizontalAlignment: (type=="sent")?Text.AlignRight:Text.AlignLeft
                    }
                }
            }

            function handle_error(error) {
                messagesModel.insert(0, {
                     "from" :  "",
                     "to" :  "",
                     "type" :  "error",
                     "message" : "<b>ERROR: " + error + "</b>"
                })
            }

            function handle_notifications(list) {
                list.forEach(function(notification) {
                    var item = JSON.parse(notification)
                    item["type"] = "received"
                    messagesModel.insert(0, item)
                })
            }
        }
    }

    Panel {
        id: notificationSettings
        anchors {
            left: parent.left
            right: parent.right
            bottom: parent.bottom
        }
        height: item1.height * 9
        UbuntuShape {
            anchors.fill: parent
            color: Theme.palette.normal.overlay
            Column {
                id: settingsColumn
                anchors.fill: parent
                ListItem.Header {
                    text: "<b>Notification Settings</b>"
                }
                ListItem.Standard {
                    id: item1
                    text: "Enable Notifications"
                    control: Switch {
                        id: annoyingSwitch
                        checked: true
                    }
                }
                ListItem.Standard {
                    text: "Enable Popup"
                    enabled: annoyingSwitch.checked
                    control: Switch {
                        id: popupSwitch
                        checked: true
                    }
                }
                ListItem.Standard {
                    text: "Persistent"
                    enabled: annoyingSwitch.checked
                    control: Switch {
                        id: persistSwitch
                        checked: true
                    }
                }
                ListItem.Standard {
                    text: "Make Sound"
                    enabled: annoyingSwitch.checked
                    control: Switch {
                        id: soundSwitch
                        checked: true
                    }
                }
                ListItem.Standard {
                    text: "Vibrate"
                    enabled: annoyingSwitch.checked
                    control: Switch {
                        id: vibrateSwitch
                        checked: true
                    }
                }
                ListItem.Standard {
                    text: "Counter Value"
                    enabled: annoyingSwitch.checked
                    control: Slider {
                        id: counterSlider
                        value: 42
                    }
                }
                Button {
                    text: "Set Counter Via Plugin"
                    onClicked: { pushClient.count = counterSlider.value; }
                }
                Button {
                    text: "Clear Persistent Notifications"
                    onClicked: { pushClient.clearPersistent([]); }
                }
            }
        }
    }
}

这里,在上面创建一个nickname的输入框及一个login按钮。紧接着,我们创建一个输入信息的对话框。再紧接着,我们创建了一个listview来显示状态,提示信息,或来往的信息。




ChatClient.qml文件的定义如下:


import QtQuick 2.0
import Ubuntu.Components 0.1

Item {
    property string nick
    property string token
    property bool registered: false
    signal error (string msg)
    onNickChanged: {
        if (nick) {
            console.log("Nick is changed!");
            register()
        } else {
            registered = false
        }
    }
    onTokenChanged: {
        console.log("Token is changed!");
        register()
    }

    function register() {
        console.log("registering ", nick, token);
        if (nick && token) {
            console.log("going to make a request!");

            var req = new XMLHttpRequest();
            req.open("post", "http://direct.ralsina.me:8001/register", true);
//            req.open("post", "http://127.0.0.1:8001/register", true);
            req.setRequestHeader("Content-type", "application/json");
            req.onreadystatechange = function() { // Call a function when the state changes.
                if(req.readyState == 4) {
                    if (req.status == 200) {
                        console.log("response: " + JSON.stringify(req.responseText));
                        registered = true;
                    } else {
                        error(JSON.parse(req.responseText)["error"]);
                    }
                }
            }

            console.log("content: " + JSON.stringify(JSON.stringify({"nick" : nick.toLowerCase(),
                                                                     "token": token
                                                                    })));
            req.send(JSON.stringify({
                "nick" : nick.toLowerCase(),
                "token": token
            }))
        }
    }

    /* options is of the form:
      {
          enabled: false,
          persist: false,
          popup: false,
          sound: "buzz.mp3",
          vibrate: false,
          counter: 5
      }
    */
    function sendMessage(message, options) {
        var to_nick = message["to"]
        var data = {
            "from_nick": nick.toLowerCase(),
            "from_token": token,
            "nick": to_nick.toLowerCase(),
            "data": {
                "message": message,
                "notification": {}
            }
        }
        if (options["enabled"]) {
            data["data"]["notification"] = {
                "card": {
                    "summary": nick + " says:",
                    "body": message["message"],
                    "popup": options["popup"],
                    "persist": options["persist"],
                    "actions": ["appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version"]
                }
            }
            if (options["sound"]) {
                data["data"]["notification"]["sound"] = options["sound"]
            }
            if (options["vibrate"]) {
                data["data"]["notification"]["vibrate"] = {
                    "duration": 200
                }
            }
            if (options["counter"]) {
                data["data"]["notification"]["emblem-counter"] = {
                    "count": Math.floor(options["counter"]),
                    "visible": true
                }
            }
        }
        var req = new XMLHttpRequest();
        req.open("post", "http://direct.ralsina.me:8001/message", true);
        req.setRequestHeader("Content-type", "application/json");
        req.onreadystatechange = function() {//Call a function when the state changes.
            if(req.readyState == 4) {
                if (req.status == 200) {
                    registered = true;
                } else {
                    error(JSON.parse(req.responseText)["error"]);
                }
            }
        }
        req.send(JSON.stringify(data))
    }
}

这个是用来向应用服务器发送注册信息及发送信息的。这里我们使用了一个已经建立好的应用服务器在http://direct.ralsina.me:8001。


这里,我们必须在手机或者我们的模拟器中创建一个Ubuntu One的账号,否则应用将不会运行成功。


我们同时运行我们的手机和模拟器,我们可以看到如下的画面:

   


  


整个“hello”的源码在:git clone https://gitcafe.com/ubuntu/example-client.git


整个server的源码在地址:git clone https://gitcafe.com/ubuntu/example-server.git


为了能够运行应用服务器,我们必须在服务器上安装相应的component,并选好自己的口地址(比如8001),这个在服务器代码中的config.js中可以找到:


module.exports = config = {
    "name" : "pushAppServer"
    ,"app_id" : "appEx"
    ,"listen_port" : 8000
    ,"mongo_host" : "localhost"
    ,"mongo_port" : 27017
    ,"mongo_opts" : {}
    ,"push_url": "https://push.ubuntu.com"
    ,"retry_batch": 5
    ,"retry_secs" : 30
    ,"happy_retry_secs": 5
    ,"expire_mins": 120
    ,"no_inbox": true
    ,"play_notify_form": true
}

然后运行:

$nodejs server.js

这样服务器就搭建好了。





作者:UbuntuTouch 发表于2015/4/9 17:47:56 原文链接
阅读:272 评论:0 查看评论

Read more
UbuntuTouch

我们发现在使用SDK创建HTML5应用的时候,模版应用会产生如下的代码:


    <link href="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/css/appTemplate.css" rel="stylesheet" type="text/css" />    
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/fast-buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/core.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/page.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/pagestacks.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/popovers.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/list.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/toolbars.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/tabs.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/shape.js"></script>


上述代码显示,当我们的应用在不同的平台上依赖于系统所提供ambiance文件,同样的应用可能会有不同的表现形式。为了使得我们的HTML在不同的平台上显示相同,我们可以使用一个工具来完成。


1)首先我们来检查我们的awk


在Shell中打入如下的命令:

 ls -altr /etc/alternatives/awk



在我的地方显示为gawk,如果你的版本是:

liuxg@liuxg:~$ ls -altr /etc/alternatives/awk
lrwxrwxrwx 1 root root 13  9月 27  2014 /etc/alternatives/awk -> /usr/bin/mawk
liuxg@liuxg:~$ 

请按照如下的步骤来安装gwak:

$sudo apt-get install gawk

2)下载Ubuntu-html5-theme脚本


我们可以在地址

http://bazaar.launchpad.net/~dbarth/ubuntu-html5-theme/cmdline-tool/download/head:/ubuntuhtml5theme-20150319111737-5oucox80hsx3rmj1-1/ubuntu-html5-theme

下载ubuntu-html5-theme脚本,并把它修改为可以执行的文件:

$chmod +x ubuntu-html5-theme

我们可以把这个脚本放到任何一个可以被执行的目录中,比如/usr/local/bin/,这样这个文件可以在任何一个地方就像一个系统的命令一样来执行.


3)创建我们HTML5应用

我们可以按照我们的SDK的步骤来创建我们的HTML5应用,并进入我们的HTML5应用的根目录。打入如下的指令:

# To list the available HTML5 toolkit / theme releases:
$ ubuntu-html5-theme list
trunk
14.10
14.04
13.10
rtm-14.09

# To install a toolkit release in the current project directory
$ ubuntu-html5-theme install 14.10
Downloading release 14.10...
Branched 177 revisions.

# To convert the index.html file of an existing project
$ ubuntu-html5-theme convert

当我们打入“ubuntu-html5-theme install 14.10”命令后,我们的项目的根目录下会产生一个叫做“ambiance”的子目录:






当我们打入“ubuntu-html5-theme convert”命令后,我们的index.html文件中的如下行发生变化:

    <script src="ambiance/js/fast-buttons.js"></script>
    <script src="ambiance/js/core.js"></script>
    <script src="ambiance/js/buttons.js"></script>
    <script src="ambiance/js/dialogs.js"></script>
    <script src="ambiance/js/page.js"></script>
    <script src="ambiance/js/pagestacks.js"></script>
    <script src="ambiance/js/tab.js"></script>
    <script src="ambiance/js/tabs.js"></script>

我们可以看到在文章一开始中的“/usr/share/ubuntu-html5-ui-toolkit/0.1/”被剔除了,我们使用本地应用自己所带的文件。

在本应用中所用的源码在地址:git clone https://gitcafe.com/ubuntu/html5test.git



作者:UbuntuTouch 发表于2015/4/8 15:27:13 原文链接
阅读:243 评论:0 查看评论

Read more
UbuntuTouch

我们知道很多的开发者想把自己的应用设置为全屏的应用,这样可以使得应用能更多地占用屏幕的有效面积以使得自己的应用更好看。在默认的SDK的样板中,在应用的最上面,有一个“title”的地方占用很多的空间。对于一些应用来说,在主界面中,这个可能并不有用,但是对于使用PageStack的应用来说,这个区域显示一个向左的箭头以返回上一个页面的。 最近我也有这样的问题,我既想使用PageStack给予我的方便,又想拥有全屏的功能。在这篇文章中,我们来介绍如何做到全屏的应用。另外我们值得指出的是:我们的全屏的应用不能覆盖手机最上面的状态栏。


我们使用我们的Ubuntu SDK来创建一个最基本的应用(QML App with Simple UI)。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: "fullscreen.ubuntu"

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

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

            Label {
                id: label
                objectName: "label"

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

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

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

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


默认的情况下,我们运行我们的应用,显示如下:


  


从上面的图上可以看出来,无论是在手机或者是在手机上,有一个“Simple”的header在那里,占用一些空间。为了得到更多的空间,我们把Page的title设为空,也即:


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: "fullscreen.ubuntu"

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

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

            Label {
                id: label
                objectName: "label"

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

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

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

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


重新运行我们的应用:


  


我们显然可以看到,“title”区域不见了。应用所占用的区间更大了。


不想使用PageStack的应用来说,我们也不必使用Page。我们可以直接使用如下的方法直接占用整个有效的屏幕区域:


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: "fullscreen.ubuntu"

    /*
     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)

    Rectangle {
        anchors.fill: parent
        color: "red"
    }

}

  


通过这样的方法,我们就可以得到全屏的应用了。



作者:UbuntuTouch 发表于2015/4/23 8:43:59 原文链接
阅读:476 评论:0 查看评论

Read more
UbuntuTouch

我们知道对于很多的网路应用来说,网路的连接信息对于我们来说非常重要。我们有必要对网路的连接信息进行监测。一旦网路连接断开,我们需要提醒用户或做一些处理。在Ubuntu平台上,我们可以使用connectivity库来查看。


我们可以利用SDK的模版来创建一个最简单的QML应用。因为我们要使用connectivity,所以我们必须加入“connectivity”的security policy。




在我们的开发者网站上虽然也有NetworkStatus的介绍,但是可能并不是很全,我们可以使用如下的命令来得到更多的信息:


$qmlplugindump Ubuntu.Connectivity 1.0


显示的结果如下:


import QtQuick.tooling 1.1

// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump Ubuntu.Connectivity 1.0'

Module {
    Component {
        name: "NetworkingStatus"
        prototype: "ubuntu::connectivity::NetworkingStatus"
        exports: ["NetworkingStatus 1.0"]
        isCreatable: false
        isSingleton: true
        exportMetaObjectRevisions: [0]
        Property { name: "online"; type: "bool"; isReadonly: true }
        Property { name: "limitedBandwith"; type: "bool"; isReadonly: true }
        Signal {
            name: "onlineChanged"
            Parameter { name: "value"; type: "bool" }
        }
        Signal {
            name: "limitedBandwithChanged"
            Parameter { name: "value"; type: "bool" }
        }
    }
    Component {
        name: "ubuntu::connectivity::NetworkingStatus"
        prototype: "QObject"
        Enum {
            name: "Limitations"
            values: {
                "Bandwith": 0
            }
        }
        Enum {
            name: "Status"
            values: {
                "Offline": 0,
                "Connecting": 1,
                "Online": 2
            }
        }
        Property { name: "limitations"; type: "QVector<Limitations>"; isReadonly: true }
        Property { name: "status"; type: "Status"; isReadonly: true }
        Signal {
            name: "statusChanged"
            Parameter { name: "value"; type: "Status" }
        }
    }
}

这里我们可以看到有一个叫做“status”的属性。我们可以通过监测这个属性的变化而得到网路的连接状态的变化。我们的main.qml的文件如下:


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

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

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

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "networkstatus.ubuntu"

    /*
     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)

    property real margins: units.gu(2)
    property real buttonWidth: units.gu(9)

    Connections {
        target: NetworkingStatus
        // full status can be retrieved from the base C++ class
        // status property
        onStatusChanged: {
            console.log("name: " + value );

            if (value === NetworkingStatus.Offline)
                console.log("Status: Offline")
            if (value === NetworkingStatus.Connecting)
                console.log("Status: Connecting")
            if (value === NetworkingStatus.Online)
                console.log("Status: Online")
        }
    }

    Page {
        title: i18n.tr("Networking Status")

        Column {
            anchors.centerIn: parent
            Label {
                // use the online property
                text: NetworkingStatus.online ? "Online" : "Not online"
                fontSize: "large"
            }
            Label {
                // use the limitedBandwith property
                text: NetworkingStatus.limitedBandwith ? "Bandwith limited" : "Bandwith not limited"
                fontSize: "large"
            }
        }
    }
}

最终运行我们的应用,我们可以看到在wifi断开和连接上时的状态变化:


  


测试代码在: git clone https://gitcafe.com/ubuntu/networkstatus.git




作者:UbuntuTouch 发表于2015/4/13 11:12:13 原文链接
阅读:225 评论:2 查看评论

Read more
UbuntuTouch

对于一些应用来说,我们希望使用手势来做一些动作。比如利用手势来放大图片,或旋转图片。对于pdf阅读器来说也是一个好的方法来放大自己的字体。在这篇文章中,我们来介绍如何使用手势。


在QML中,有一个元素PinchArea。在这篇文章中也有一些介绍“Qt Quick 事件处理之捏拉缩放与旋转”。这里不再累述,我们直接贴上我们的例程:


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: "pinch.ubuntu"

    /*
     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 {
        id:main
        title: i18n.tr("Simple")

        Flickable {
            id: flick
            anchors.fill: parent
            contentWidth: 768
            contentHeight: 1024

            PinchArea {
                width: Math.max(flick.contentWidth, flick.width)
                height: Math.max(flick.contentHeight, flick.height)

                pinch.maximumScale: 20;
                pinch.minimumScale: 0.2;
                pinch.minimumRotation: 0;
                pinch.maximumRotation: 1;

                property real initialWidth
                property real initialHeight

                onPinchStarted: {
                    initialWidth = flick.contentWidth
                    initialHeight = flick.contentHeight
                }

                onPinchUpdated: {
                    // adjust content pos due to drag
                    flick.contentX += pinch.previousCenter.x - pinch.center.x
                    flick.contentY += pinch.previousCenter.y - pinch.center.y

                    console.log("rotation: " + pinch.rotation );
                    if ( pinch.rotation > 0 )
                        flick.rotation += 0.2;
                    else
                        flick.rotation -= 0.2;

                    // resize content
                    flick.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
                }

                onPinchFinished: {
                    // Move its content within bounds.
                    flick.returnToBounds()
                }

                Rectangle {
                    width: flick.contentWidth
                    height: flick.contentHeight
                    color: "white"
                    Image {
                        id: image
                        anchors.fill: parent
                        source: "images/sky.jpg"
                        MouseArea {
                            anchors.fill: parent
                        }
                    }
                }
            }
        }
    }
}


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

    


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



作者:UbuntuTouch 发表于2015/4/13 13:26:50 原文链接
阅读:287 评论:0 查看评论

Read more
UbuntuTouch

这是一个利用Ubuntu SDK来创建一个QML应用的视频。这个例程的原教程可以在我们的开发者网站“构建首个QML应用程序”。它的源码在地址:


bzr branch lp:ubuntu-sdk-tutorials


下载。大家可以通过这个视频对Ubuntu在手机上的开发流程有一个更加清楚的认识。视频的地址在:


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

作者:UbuntuTouch 发表于2015/4/14 15:40:52 原文链接
阅读:277 评论:0 查看评论

Read more
UbuntuTouch

对于有些应用来说,获取屏幕分辨率这个信息可能是重要的。比如有些游戏或阅读器应用,希望在应用启动后,马上得到屏幕的分辨率,这样可以和容易地适配不同屏幕尺寸的手机或装置。有些应用可以是用QtQuick.Window的Screen来得到这个信息,但是我们可以看一下在文章中如下的提醒:


Note that the Screen type is not valid at Component.onCompleted, because the Item or Window has not been displayed on a screen by this time.


这也就是说我们不能使用上述的方法来得到屏幕的尺寸。通过我的试验。显示的结果为“0”。


为了能够在应用启动时得到屏幕的分辨率,我们可以使用C++的代码来实现。


screen.h

#ifndef SCREEN_H
#define SCREEN_H

#include <QObject>
#include <QGuiApplication>
#include <QScreen>
#include <QDebug>

class Screen : public QObject
{
    Q_OBJECT

public:
    Q_PROPERTY(int height READ height)
    Q_PROPERTY(int width READ width)
    explicit Screen(QObject *parent = 0); 
    int height() { return m_height; };
    int width() { return m_width; };

private:
    int m_height;
    int m_width;
};

#endif // FILEIO_H

screen.cpp


#include "screen.h"

Screen::Screen(QObject *parent) : QObject(parent)
{
	QScreen* screen = QGuiApplication::primaryScreen();
	QSize screenSize =  screen->size();
	qDebug() << "width: " << screenSize.width();
	m_width = screenSize.width();
	m_height = screenSize.height();
}

我们可以使用Ubuntu SDK提供的“QML App with C++ plugin (cmake)”来创建一个应用来测试。测试应用的代码如下:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Screen 1.0
import QtQuick.Window 2.0

/*!
    \brief MainView with Tabs element.
           First Tab has a single Label and
           second Tab has a single ToolbarAction.
*/

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

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

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

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

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

    MyScreen {
        id: screen
    }

    Page {
        title: i18n.tr("App with backend")

        MyType {
            id: myType

            Component.onCompleted: {
                myType.helloWorld = i18n.tr("Hello world..")
            }
        }

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

            Label {
                id: label
                objectName: "label"

                text: myType.helloWorld
            }

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

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

                onClicked: {
                    myType.helloWorld = i18n.tr("..from Cpp Backend")
                }
            }
        }

        Component.onCompleted: {
            console.log("screen width: " + screen.width);
            console.log("screen height: " + screen.height);
            console.log("SCREEN width: " + Screen.width );
            console.log("SCREEN height: " + Screen.height)
        }
    }
}


我们应用的输出为:


qml: screen width: 768
qml: screen height: 1280
qml: SCREEN width: 0
qml: SCREEN height: 0

我们可以看出来,MyScreen得到了正确的分辨率,但是“Screen.width”及“Screen.height”得到的是“0”。


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


作者:UbuntuTouch 发表于2015/4/23 13:41:23 原文链接
阅读:287 评论:0 查看评论

Read more
UbuntuTouch

[原]如何在QML应用中读写文件

我们知道,在QML应用中,有时我们需要来读写一些文件,但是在我们的QML语言中并没有相应的API接口来供我们做(虽然有API接口来存储设置文件等)。那么我们怎么来做这个事情呢?我们可以通过Qt C++的方法来实现这个功能。


1)创建一个简单的模版应用


我们使用Ubuntu SDK的模版来创建一个最简单的应用:

  

 

我们选择“QML App with C++ plugin”模版来做我们的应用。


2)添加文件读写的文件到项目中

我们添加如下的C++ "FileIO类到我们的backend plugin中:

#ifndef FILEIO_H
#define FILEIO_H

#include <QObject>
#include <QTextCodec>
#include <QDebug>

class FileIO : public QObject
{
    Q_OBJECT

public:
    Q_PROPERTY(QString source
               READ source
               WRITE setSource
               NOTIFY sourceChanged)
    explicit FileIO(QObject *parent = 0);

    Q_INVOKABLE QString read();
    Q_INVOKABLE bool write(const QString& data);

    QString source() { return mSource; };

public slots:
    void setSource(const QString& source) { mSource = source; };

signals:
    void sourceChanged(const QString& source);
    void error(const QString& msg);

private:
    QString getenv(const QString envVarName) const;

private:
    QString mSource;
    QString datapath;
};

inline QString GBK2UTF8(const QString &inStr)
{
    QList<QByteArray> codecs = QTextCodec::availableCodecs();

    for ( int i = 0; i < codecs.length(); i ++ ) {
//        qDebug() << "codec: " + QTextCodec::codecForMib(1015)->toUnicode(codecs.at(i));
        qDebug() << "codec: " << QString::fromLocal8Bit(codecs.at(i));
    }

    QTextCodec *gbk = QTextCodec::codecForName("GBK");

    QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());
//    QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");

    QString g2u = gbk->toUnicode(gbk->fromUnicode(inStr)); // gbk  convert utf8
    return g2u;
}

#endif // FILEIO_H

#include "fileio.h"
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QFileInfo>
#include <QTextCodec>

FileIO::FileIO(QObject *parent) : QObject(parent)
{
    datapath = getenv("TMPDIR")  + "/";
    qDebug() << "datapath: " + datapath;
}

QString FileIO::read()
{    
    qDebug() << "reading ....!";

    if (mSource.isEmpty()){
        emit error("source is empty");
        return QString();
    }

    QFile file(datapath + mSource);
    QFileInfo fileInfo(file.fileName());
    qDebug() << "file path: " << fileInfo.absoluteFilePath();

    QString fileContent;
    if ( file.open(QIODevice::ReadOnly) ) {
        QString line;

        QTextCodec *gbk = QTextCodec::codecForName("GBK");
        QTextStream t( &file );
        t.setCodec(gbk);

        do {
            line = t.readLine();
            fileContent += line;
        } while (!line.isNull());

        file.close();
        return fileContent;
    } else {
        emit error("Unable to open the file");
        return QString();
    }
}

bool FileIO::write(const QString& data)
{
    qDebug() << "writing.....";

    if (mSource.isEmpty())
        return false;

    QFile file(datapath + mSource);
    QFileInfo fileInfo(file.fileName());
    qDebug() << "file path: " << fileInfo.absoluteFilePath();
    if (!file.open(QFile::WriteOnly | QFile::Truncate))
        return false;

    QTextStream out(&file);
    out << data;

    file.close();

    return true;
}

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

这个类是可以向我们指定的文件地址读写文件。注意,我们使用了getenv来获取可以读写的文件目录。Ubuntu应用不是可以打开任何一个文件目录进行读写的。具体可以参考文章“Ubuntu OS应用Runtime Enviroment”来得到更多的了解。在这个例程中,我们指定了文件的编码方式为GBK。你们可以不指定或指定为你所需要的编码方式。

在backend的CMakeLists.txt中加入:

    modules/ReadFileQML/fileio.cpp

同时在backend.cpp中加入:

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

    qmlRegisterType<MyType>(uri, 1, 0, "MyType");
    qmlRegisterType<FileIO>(uri, 1, 0, "FileIO"); // added line
}

这样就完成了我们的plugin的设计。


3)在应用中调用

为了测试我们的plugin,我们修改我们的readfileqml.qml文件如下:

import QtQuick 2.0
import Ubuntu.Components 1.1
import ReadFileQML 1.0

/*!
    \brief MainView with Tabs element.
           First Tab has a single Label and
           second Tab has a single ToolbarAction.
*/

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

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

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

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

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

    Page {
        title: i18n.tr("Read File QML")

        Text {
            id: myText
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: button.top

            wrapMode: Text.Wrap
        }

        Button {
            id: button
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom:parent.bottom
            height: units.gu(5)

            text: "Reload file"

            onClicked: {
                console.log("button is clicked!");
                //reload the file
                myText.text =  myFile.read();
            }
        }

        FileIO {
            id: myFile
            source: "good.txt"
            onError: console.log(msg)
        }

        Component.onCompleted: {
            console.log( "WRITE: "+ myFile.write("this is really cool!"));
            console.log("source: " + myFile.source );
            myText.text =  myFile.read();
        }
    }
}

这里,我们定义了:


        FileIO {
            id: myFile
            source: "good.txt"
            onError: console.log(msg)
        }

我们可以通过它向我们的文件“good.txt”来读写文件了。注意good.txt文件的地址。



所有的源码在地址: git clone https://gitcafe.com/ubuntu/readfileqml.git
作者:UbuntuTouch 发表于2015/4/16 10:01:26 原文链接
阅读:324 评论:3 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将介绍如何在QML中使用C++代码。在以前的文章“ 使用C++拓展QML 类型及Property binding!”中,我们可以可以通过C++ plugin的方法来拓展我们的QML功能。那个项目是CMake项目。对于qmake项目来说,我们也可以做同样的事。可以使用一个plugin,并在QML中调用它。


今天,我们将不使用plugin的方法,我们希望在qmake项目中直接调用C++代码。那么我们将如何做呢?这里注意qmake只对15.04及以上的ubuntu手机target (模拟器及手机)适用。


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


我们使用我们的SDK,并使用“QtQuick App with QML UI (qmake)”模版。具体步骤如下:

   

   

这样我们就创建了我们一个最基本的QML和C++的混合应用(qmake)。


2)加入我们需要的文件


我们把上次我们做过的trafficlight的代码下载到我们想要的目录:

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

并把其中的“trafficlight.cpp”及“trafficlight.h”考入到我们项目的源码的目录中(位于main.cpp所在的目录中)。为了能够编译我们新加入的trafficlight.cpp文件, 我们需要对trafficligth.pro文件做写修改:

TEMPLATE = app
TARGET = trafficlight

load(ubuntu-click)

QT += core qml quick

SOURCES += main.cpp trafficlight.cpp
HEADERS += trafficlight.h

RESOURCES += trafficlight.qrc

OTHER_FILES += trafficlight.apparmor \
               trafficlight.desktop \
               trafficlight.png

#specify where the config files are installed to
config_files.path = /trafficlight
config_files.files += $${OTHER_FILES}
message($$config_files.files)
INSTALLS+=config_files

# Default rules for deployment.
target.path = $${UBUNTU_CLICK_BINARY_PATH}
INSTALLS+=target

这里我们添加了“core”:
QT += core qml quick

这样可以使得我们可以对QObject类进行编译。同时我们加入“trafficlight.cpp”了:

SOURCES += main.cpp trafficlight.cpp

这样我们可以对这个文件进行编译。同时为了能够在qt creator中显示“trafficlight.h”,我们加入了如下的句子:

HEADERS += trafficlight.h

编译我们的项目,确保我们的代码没有任何的问题。

3)注册我们的C++代码,并使之能够在我们的QML中被利用


打开我们的main.cpp文件,我们修改它为:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQmlContext>

#include <trafficlight.h>

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

    qmlRegisterType<TrafficLight>("Light", 1,0, "TrafficLight");

    int count = 3;

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

    view.rootContext()->setContextProperty("total", QVariant::fromValue(count));

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

这里,我们通过:

 qmlRegisterType<TrafficLight>("Light", 1,0, "TrafficLight");

来注册我们的TrafficLight,这样它就可以在QML中被利用了。

另外,我们通过如下的方式:

    int count = 3;
   view.rootContext()->setContextProperty("total", QVariant::fromValue(count));

来把count传给QML,这样它的值就可以在QML中被引用。

最后,我们来修改我们的main.qml文件:

import QtQuick 2.0
import Ubuntu.Components 1.1
import Light 1.0

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

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

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

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

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

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

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

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

            Repeater {
                model: total
                delegate:
                    TrafficLight {
                    anchors.centerIn: parent.Center
                    width: units.gu(20)
                    height: width
                    color: "red"
                }
            }
        }
    }
}

在这个文件中,我们直接导入我们的TrafficLight:

import Light 1.0

这样,TrafficLight就可以直接被使用:

                    TrafficLight {
                    anchors.centerIn: parent.Center
                    width: units.gu(20)
                    height: width
                    color: "red"

运行我们的应用:



所有的源码在: git clone https://gitcafe.com/ubuntu/trafficlight_new.git
作者:UbuntuTouch 发表于2015/5/12 9:48:29 原文链接
阅读:158 评论:0 查看评论

Read more
UbuntuTouch

我们在使用Ubuntu SDK中的Slider的时候,我们发现,它没有orientation的属性尽管在Qt官方网站的slider是有这个属性的。在默认的情况下,这个Slider是水平的。那么我们该如实现这个呢?


我们的任何一个QML Item都有一个属性叫做rotation。我们可以通过这个属性来得到一个旋转90度的水平Slider。这样我们就可以用如下的代码来实现了:


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: "slider.liu-xiao-guo"

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

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

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

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

        Slider {
            x:parent.width/2 - width/2
            y:parent.height/2 - height/2
            function formatValue(v) { return v.toFixed(2) }
            minimumValue: -3.14
            maximumValue: 3.14
//            rotation: 90
            value: 0.0
            live: true
        }

        Slider {
            x:parent.width/2 - width/2
            y:parent.height/2 - height/2
            function formatValue(v) { return v.toFixed(2) }
            minimumValue: -3.14
            maximumValue: 3.14
//            rotation: 90
//            orientation: Qt.Horizontal
            value: 0.0
            live: true
        }
    }
}

这里创建了连个Slider,一个是是水平的(默认情况下的),另外一个是垂直的(旋转90度的)。显示的结果如下:




作者:UbuntuTouch 发表于2015/4/28 11:48:58 原文链接
阅读:316 评论:0 查看评论

Read more