Canonical Voices

UbuntuTouch

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

1)创建数据库及文档

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

import U1db 1.0 as U1db

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

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

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

    anchorToKeyboard: true


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

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

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

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

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

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

如果我们有如下的定义:

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

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


2)显示及修改数据库

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

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

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

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

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

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

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

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

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

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

    

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



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

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

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

Read more
UbuntuTouch

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


1)创建一个基本的应用


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

import QtQuick.LocalStorage 2.0

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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

我们来运行该应用:



在手机上的数据库文件:



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

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





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

Read more
UbuntuTouch

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


developernews.png


让我们开始吧。

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

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


ubuntu-sdk-simple-project.png



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

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


分辨率无关

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

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


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




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

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


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

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



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

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



3)加入一个PageStack

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

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

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

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

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

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

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

4)加入我们自己的控件

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

     

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

4)定义ArticleListView

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

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

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


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


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


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


   Scrollbar {
       flickableItem: listView
   }
}

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

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



5)使用ArticleListView

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

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

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


6)创建一个新的Component

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

import QtQuick 2.0
import Ubuntu.Components 1.1

Item {
   property alias text: content.text

   Flickable {
       id: flickableContent
       anchors.fill: parent

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

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


   Scrollbar {
       flickableItem: flickableContent
   }
}


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

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


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

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

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

   signal clicked(var instance)

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

onClicked: listView.clicked(model)



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

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

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

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

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

            }
        }

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

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



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

     

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

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

8)添加一个reload按钮


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

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

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

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

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

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


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

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

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


5.png


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

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

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

  id: mainView

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

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

   automaticOrientation: true
   useDeprecatedToolbar: false

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


6.png   


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

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

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

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


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

Read more
UbuntuTouch

[原]如何在Ubuntu中使用条件布局

我们知道现代手机可以随着手持的方位发生改变而使得手机的方位也随着发生改变。对有些应用来说,我们也希望手机的布局也能跟随发生变化。另外一种情况是当我们的应用安装到不同屏幕尺寸的平台上,我们希望我们的布局会随着屏幕的尺寸不同而发生不同的变化。我们可以利用剩余的空间显示更多的内容。在Ubuntu平台中,我们使用一个称作为conditinal layout的机制来使得我们的布局发生改变。在conditional layout的上面可以阅读更多的内容。


1)下载我们在上节中设计好的应用

我们可以在如下的地址:

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


下载我们的源码。我们可以安装到手机上并熟悉该应用。


2)使用conditional layout

conditional layout能够使得我们根据屏幕的尺寸来安排我们的控件。下面我们来具体讲解怎么实现它:


1)在我们的main.qml中加入如下的库:

import Ubuntu.Layouts 1.0

2)在main.qml中的PageStack之前,加入如下的代码:


 Layouts {
       id: mainLayout
       anchors.fill: parent

       layouts: [
           ConditionalLayout {
               name: 'flat'
               when: mainLayout.width >= units.gu(70)
               Page {
                   id: flatPage
                   title: i18n.tr("Developer News")

                   tools: ToolbarItems {
                       ToolbarButton {
                           action: reloadAction
                       }
                   }

                   Row {
                          anchors.fill: parent





                       ItemLayout {
                           item: "articleList"
                           width: parent.width >= units.gu(100) ? units.gu(50) : parent.width/2
                           height: parent.height
                       }
                       ItemLayout {
                           item: "articleContent"
                           width: parent.width - articleList.width
                           height: parent.height
                       }
                   }
               }
           }
       ]


       onCurrentLayoutChanged: {
           if (mainLayout.currentLayout != 'flat') {
               mainView.activeLeafNode = pageStack.currentPage
           }
       }















3)在PageStack中的ArticleListView加入:

 Layouts.item: "articleList"

4)在ArticleListView中的onClicked的如下代码:

 pageStack.push(contentPage)

替换为:

                   if (mainLayout.currentLayout != "flat") {
                       contentPage.title = instance.title
                       pageStack.push(contentPage)
                   }

5)在ArticleContent的定义中加入:

Layouts.item: "articleContent"


6)在main.qml的最后端加入:

}

现在我们已经完成了我们的工作。我们现在运行一下我们的应用。




当我们把应用的尺寸变大后,应用显示为:




最终的源码在如下地址可以找到:


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




作者:UbuntuTouch 发表于2014-9-3 13:41:04 原文链接
阅读:121 评论:0 查看评论

Read more
UbuntuTouch

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


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

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





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

import Ubuntu.History 0.1

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

            ComboButton {
                id: type
                expanded: false

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

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

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

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

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

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

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

    HistoryThreadModel {
        id: historyThreadModel        <pre name="code" class="javascript"><span style="white-space:pre">	</span>filter: HistoryFilter {}
sort: HistorySort { sortField: "lastEventTimestamp" sortOrder: HistorySort.DescendingOrder } }


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

filter: HistoryFilter {}

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

            ListView {
                id: listView

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

                Component {
                    id: sectionDelegate

                    Text {
                        text: section
                    }
                }

                model: historyEventModel

               delegate:
               ...
            }

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



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

$adb shell

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

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

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



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



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




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

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





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

Read more
UbuntuTouch

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


1)动态载入一个Component


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

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

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

        Component {
            id: imageText

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

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

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

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

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

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

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

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

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

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

运行的效果图如下:



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

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

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

2)使用Loader载入qml文件

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

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

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

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


    //automaticOrientation: true

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

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

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

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

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

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


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

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


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

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

import QtQuick 2.0
import Ubuntu.Components 1.1

Rectangle {
    id:page1
    anchors.fill: parent

    signal handlerLoader;

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

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

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

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

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

       

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

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

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

Read more
UbuntuTouch

[原]如何在Ubuntu OS上读取电话本信息

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


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

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


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

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

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

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

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

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

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

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

        ContactModel {
            id: contactModel

            manager: "galera"
        }

        ListView {
            id: contactView

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

            model: contactModel

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

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

        ContactModel {
            id: contactModel
            manager: "galera"
        }

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

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

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

==ou

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



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

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

2)读取favorite contact信息


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

        ContactModel {
            id: favouritesContactsModel

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

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

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

            filter: DetailFilter {
                id: favouritesFilter

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

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

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

        ListView {
            id: contactView
            anchors.fill:parent

            model: root.showFavourites ? favouritesContactsModel : contactModel

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



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

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





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

Read more
UbuntuTouch

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


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


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



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



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

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


运行我们的应用:



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

2)添加Balloon Component

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



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



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

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

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

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

    //automaticOrientation: true

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

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

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

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

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



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

3)完成Balloon模块

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



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

import QtQuick 2.0

Image {
    id: balloon
    width: 100
    height: 250

    source: "images/red.svg"
}

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



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

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

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

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

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

再重新运行应用:



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

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

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

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

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

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

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

import QtMultimedia 5.0

Image {
    ...

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

   ...
}

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



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

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

    states: [
        State {
            name: "exploded"

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

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

            PropertyChanges {
                target: balloon
                opacity: 0
            }

            PropertyChanges {
                target: balloon
                scale: 0
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

...
}

并在state变化时让它运行:

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

运行应用,效果图如下:

    

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


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

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











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

Read more
UbuntuTouch

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


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


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

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

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

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

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

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

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

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

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

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

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

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

5)动态生成QML Component

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

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

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

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

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

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

        Button {
            z: 2
            id: restartButton

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

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

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

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

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

var component;

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


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

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

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

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

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

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

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

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

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

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

    return dynamicObject;
}

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

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

import "logic.js" as Logic

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


    

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

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

6)加入sensor 到应用中去

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

import QtSensors 5.0

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

        Accelerometer {
            id: accel
            active: true
            dataRate: 20

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

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

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

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

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

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

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

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

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

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

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

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

            }
        }

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

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

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





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

Read more
niemeyer

As detailed in the preliminary release of qml.v1 for Go a couple of weeks ago, my next task was to finish the improvements in its OpenGL API. Good progress has happened since then, and the new API is mostly done and available for experimentation. At the same time, there’s still work to do on polishing edges and on documenting the extensive API. This blog post aims to present the improvements made, their internal design, and also to invite help for finishing the pending details.

Before diving into the new, let’s first have a quick look at how a Go application using OpenGL might look like with qml.v0. This is an excerpt from the old painting example:

import (
        "gopkg.in/qml.v0"
        "gopkg.in/qml.v0/gl"
)

func (r *GoRect) Paint(p *qml.Painter) {
        width := gl.Float(r.Int("width"))
        height := gl.Float(r.Int("height"))
        gl.Enable(gl.BLEND)
        // ...
}

The example imports both the qml and the gl packages, and then defines a Paint method that makes use of the GL types, functions, and constants from the gl package. It looks quite reasonable, but there are a few relevant shortcomings.

One major issue in the current API is that it offers no means to tell even at a basic level what version of the OpenGL API is being coded against, because the available functions and constants are the complete set extracted from the gl.h header. For example, OpenGL 2.0 has GL_ALPHA and GL_ALPHA4/8/12/16 constants, but OpenGL ES 2.0 has only GL_ALPHA. This simplistic choice was a good start, but comes with a number of undesired side effects:

  • Many trivial errors that should be compile errors fail at runtime instead
  • When the code does work, the developer is not sure about which API version it is targeting
  • Symbols for unsupported API versions may not be available for linking, even if unused

That last point also provides a hint of another general issue: portability. Every system has particularities for how to load the proper OpenGL API entry points. For example, which libraries should be linked with, where they are in the local system, which entry points they support, etc.

So this is the stage for the improvements that are happening. Before detailing the solution, let’s have a look at the new painting example in qml.v1, that makes use of the improved API:

import (
        "gopkg.in/qml.v1"
        "gopkg.in/qml.v1/gl/2.0"
)

func (r *GoRect) Paint(p *qml.Painter) {
        gl := GL.API(p)
        width := float32(r.Int("width"))
        height := float32(r.Int("height"))
        gl.Enable(GL.BLEND)
        // ...
}

With the new API, rather than importing a generic gl package, a version-specific gl/2.0 package is imported under the name GL. That choice of package name allows preserving familiar OpenGL terms for both the functions and the constants (gl.Enable and GL.BLEND, for example). Inside the new Paint method, the gl value obtained from GL.API holds only the functions that are defined for the specific OpenGL API version imported, and the constants in the GL package are also constrained to those available in the given version. Any improper references become build time errors.

To support all the various OpenGL versions and profiles, there are 23 independent packages right now. These packages are of course not being hand-built. Instead, they are generated all at once by a tool that gathers information from various sources. The process can be tersely described as:

  1. A ragel-based parser processes Qt’s qopenglfunctions_*.h header files to collect version-specific functions
  2. The Khronos OpenGL Registry XML is parsed to collect version-specific constants
  3. A number of tweaks defined in the tool’s code is applied to the state
  4. Packages are generated by feeding the state to text templates

Version-specific functions might also be extracted from the Khronos Registry, but there’s a good reason to use information from the Qt headers instead: Qt already solved the portability issue. It works in several platforms, and if somebody is using QML successfully, it means Qt is already using that system’s OpenGL capabilities. So rather than designing a new mechanism to solve the same problem, the qml package now leverages Qt for resolving all the GL function entry points and the linking against available libraries.

Going back to the example, it also demonstrates another improvement that comes with the new API: plain types that do not carry further meaning such as gl.Float and gl.Int were replaced by their native counterparts, float32 and int32. Richer types such as Enum were preserved, and as suggested by David Crawshaw some new types were also introduced to represent entities such as programs, shaders, and buffers. The custom types are all available under the common gl/glbase package that all version-specific packages make use of.

So this is all working and available for experimentation right now. What is left to do is almost exclusively improving the list of function tweaks with two goals in mind, which will be highlighted below as those are areas where help would be appreciated, mainly due to the footprint of the API.

Documentation importing

There are a few hundred functions to document, but a large number of these are variations of the same function. The previous approach was to simply link to the upstream documentation, but it would be much better to have polished documentation attached to the functions themselves. This is the new documentation for MultMatrixd, for example. For now the documentation is being imported manually, but the final process will likely consist of some automation and some manual polishing.

Function polishing

The standard C OpenGL API can often be translated automatically (see BindBuffer or BlendColor), but in other cases the function prototype has to be tweaked to become friendly to Go. The translation tool already has good support for defining most of these tweaks independently from the rest of the machinery. For example, the following logic changes the ShaderSource function from its standard from into something convenient in Go:

name: "ShaderSource",
params: paramTweaks{
        "glstring": {rename: "source", retype: "...string"},
        "length":   {remove: true},
        "count":    {remove: true},
},
before: `
        count := len(source)
        length := make([]int32, count)
        glstring := make([]unsafe.Pointer, count)
        for i, src := range source {
                length[i] = int32(len(src))
                if len(src) > 0 {
                        glstring[i] = *(*unsafe.Pointer)(unsafe.Pointer(&src))
                } else {
                        glstring[i] = unsafe.Pointer(uintptr(0))
                }
        }
`,

Other cases may be much simpler. The MultMatrixd tweak, for instance, simply ensures that the parameter has the proper length, and injects the documentation:

name: "MultMatrixd",
before: `
        if len(m) != 16 {
                panic("parameter m must have length 16 for the 4x4 matrix")
        }
`,
doc: `
        multiplies the current matrix with the provided matrix.
        ...
`,

and as an even simpler example, CreateProgram is tweaked so that it returns a glbase.Program instead of the default uint32.

name:   "CreateProgram",
result: "glbase.Program",

That kind of polishing is where contributions would be most appreciated right now. One valid way of doing this is picking a range of functions and importing and polishing their documentation manually, and while doing that keeping an eye on required tweaks that should be performed on the function based on its documentation and prototype.

If you’d like to help somehow, or just ask questions or report your experience with the new API, please join us in the project mailing list.

Read more

(This article is was originally posted on design.canonical.com)

On release day we can get up to 8,000 requests a second to ubuntu.com from people trying to download the new release. In fact, last October (13.10) was the first release day in a long time that the site didn't crash under the load at some point during the day (huge credit to the infrastructure team).

Ubuntu.com has been running on Drupal, but we've been gradually migrating it to a more bespoke Django based system. In March we started work on migrating the download section in time for the release of Trusty Tahr. This was a prime opportunity to look for ways to reduce some of the load on the servers.

Choosing geolocated download mirrors is hard work for an application

When someone downloads Ubuntu from ubuntu.com (on a thank-you page), they are actually sent to one of the 300 or so mirror sites that's nearby.

To pick a mirror for the user, the application has to:

  1. Decide from the client's IP address what country they're in
  2. Get the list of mirrors and find the ones that are in their country
  3. Randomly pick them a mirror, while sending more people to mirrors with higher bandwidth

This process is by far the most intensive operation on the whole site, not because these tasks are particularly complicated in themselves, but because this needs to be done for each and every user - potentially 8,000 a second while every other page on the site can be aggressively cached to prevent most requests from hitting the application itself.

For the site to be able to handle this load, we'd need to load-balance requests across perhaps 40 VMs.

Can everything be done client-side?

Our first thought was to embed the entire mirror list in the thank-you page and use JavaScript in the users' browsers to select an appropriate mirror. This would drastically reduce the load on the application, because the download page would then be effectively static and cache-able like every other page.

The only way to reliably get the user's location client-side is with the geolocation API, which is only supported by 85% of users' browsers. Another slight issue is that the user has to give permission before they could be assigned a mirror, which would slightly hinder their experience.

This solution would inconvenience users just a bit too much. So we found a trade-off:

A mixed solution - Apache geolocation

mod_geoip2 for Apache can apply server rules based on a user's location and is much faster than doing geolocation at the application level. This means that we can use Apache to send users to a country-specific version of the download page (e.g. the German desktop thank-you page) by adding &country=GB to the end of the URL.

These country specific pages contain the list of mirrors for that country, and each one can now be cached, vastly reducing the load on the server. Client-side JavaScript randomly selects a mirror for the user, weighted by the bandwidth of each mirror, and kicks off their download, without the need for client-side geolocation support.

This solution was successfully implemented shortly before the release of Trusty Tahr.

Read more
Robin Winslow

On release day we can get up to 8,000 requests a second to ubuntu.com from people trying to download the new release. In fact, last October (13.10) was the first release day in a long time that the site didn’t crash under the load at some point during the day (huge credit to the infrastructure team).

Ubuntu.com has been running on Drupal, but we’ve been gradually migrating it to a more bespoke Django based system. In March we started work on migrating the download section in time for the release of Trusty Tahr. This was a prime opportunity to look for ways to reduce some of the load on the servers.

Choosing geolocated download mirrors is hard work for an application

When someone downloads Ubuntu from ubuntu.com (on a thank-you page), they are actually sent to one of the 300 or so mirror sites that’s nearby.

To pick a mirror for the user, the application has to:

  1. Decide from the client’s IP address what country they’re in
  2. Get the list of mirrors and find the ones that are in their country
  3. Randomly pick them a mirror, while sending more people to mirrors with higher bandwidth

This process is by far the most intensive operation on the whole site, not because these tasks are particularly complicated in themselves, but because this needs to be done for each and every user – potentially 8,000 a second while every other page on the site can be aggressively cached to prevent most requests from hitting the application itself.

For the site to be able to handle this load, we’d need to load-balance requests across perhaps 40 VMs.

Can everything be done client-side?

Our first thought was to embed the entire mirror list in the thank-you page and use JavaScript in the users’ browsers to select an appropriate mirror. This would drastically reduce the load on the application, because the download page would then be effectively static and cache-able like every other page.

The only way to reliably get the user’s location client-side is with the geolocation API, which is only supported by 85% of users’ browsers. Another slight issue is that the user has to give permission before they could be assigned a mirror, which would slightly hinder their experience.

This solution would inconvenience users just a bit too much. So we found a trade-off:

A mixed solution – Apache geolocation

mod_geoip2 for Apache can apply server rules based on a user’s location and is much faster than doing geolocation at the application level. This means that we can use Apache to send users to a country-specific version of the download page (e.g. the German desktop thank-you page) by adding &country=GB to the end of the URL.

These country specific pages contain the list of mirrors for that country, and each one can now be cached, vastly reducing the load on the server. Client-side JavaScript randomly selects a mirror for the user, weighted by the bandwidth of each mirror, and kicks off their download, without the need for client-side geolocation support.

This solution was successfully implemented shortly before the release of Trusty Tahr.

(This article was also posted on robinwinslow.co.uk)

Read more

Docker is a fantastic tool for running virtual images and managing light Linux containers extremely quickly.

One thing this has been very useful for in my job at Canonical is quickly running older versions of Ubuntu - for example to test how to install specific packages on Precise when I'm running Trusty.

Installing Docker

The simplest way to install Docker on Ubuntu is using the automatic script:

curl -sSL https://get.docker.io/ubuntu/ | sudo sh

You may then want to authorise your user to run Docker directly (as opposed to using sudo) by adding yourself to the docker group:

sudo gpasswd -a [YOUR-USERNAME] docker

You need to log out and back in again before this will take effect.

Spinning up an old version of Ubuntu

With docker installed, you should be able to run it as follows. The below example is for Ubuntu Precise, but you can replace "precise" with any available ubuntu version:

mkdir share  # Shared folder with docker image - optional
docker run -v `pwd`/share:/share -i -t ubuntu:precise /bin/bash  # Run ubuntu, with a shared folder
root@cba49fae35ce:/#  # We're in!

The -v `pwd`/share:/share part mounts the local ./share/ folder at /share/ within the Docker instance, for easily sharing files with the host OS. Setting this up is optional, but might well be useful.

There are some import things to note:

  • This is a very stripped-down operating system. You are logged in as the root user, your home directory is the filesystem root (/), and very few packages are installed. Almost always, the first thing you'll want to run is apt-get update. You'll then almost certainly need to install a few packages before this instance will be of any use.
  • Every time you run the above command it will spin up a new instance of the Ubuntu image from scratch. If you log out, retrieving your current instance in that same state is complicated. So don't logout until you're done. Or learn about managing Docker containers.
  • In some cases, Docker will be unable to resolve DNS correctly, meaning that apt-get update will fail. In this case, follow the guide to fix DNS.

Read more

Fix Docker's DNS

Docker is really useful for a great many things - including, but not limited to, quickly testing older versions of Ubuntu. If you've not used it before, why not try out the online demo?.

Networking issues

Sometimes docker is unable to use the host OS's DNS resolver, resulting in a DNS resolve error within your Docker container:

$ sudo docker run -i -t ubuntu /bin/bash  # Start a docker container
root@0cca56c41dfe:/# apt-get update  # Try to Update apt from within the container
Err http://archive.ubuntu.com precise Release.gpg
Temporary failure resolving 'archive.ubuntu.com'  # DNS resolve failure
..
W: Some index files failed to download. They have been ignored, or old ones used instead.

How to fix it

We can fix this by explicitly telling Docker to use Google's DNS public server (8.8.8.8).

However, within some networks (for example, Canonical's London office) all public DNS will be blocked, so we should find and explicitly add the network's DNS server as a backup as well:

Get the address of your current DNS server

From the host OS, check the address of the DNS server you're using locally with nm-tool, e.g.:

$ nm-tool
...
  IPv4 Settings:
    Address:         192.168.100.154
    Prefix:          21 (255.255.248.0)
    Gateway:         192.168.100.101

    DNS:             192.168.100.101  # This is my DNS server address
...

Add your DNS server as a 2nd DNS server for Docker

Now open up the docker config file at /etc/default/docker, and update or replace the DOCKER_OPTS setting to add Google's DNS server first, but yours as a backup: --dns 8.8.8.8 --dns=[YOUR-DNS-SERVER]. E.g.:

# /etc/default/docker
# ...
# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="--dns 8.8.8.8 --dns 192.168.100.102"
# Google's DNS first ^, and ours ^ second

Restart Docker

sudo service docker restart

Success?

Hopefully, all should now be well:

$ sudo docker run -i -t ubuntu /bin/bash  # Start a docker container
root@0cca56c41dfe:/# apt-get update  # Try to Update apt from within the container
Get:1 http://archive.ubuntu.com precise Release.gpg [198 B]  # DNS resolves properly
...

Read more
Joseph Salisbury

Meeting Minutes

IRC Log of the meeting.

Meeting minutes.

Agenda

20140826 Meeting Agenda


Release Metrics and Incoming Bugs

Release metrics and incoming bug data can be reviewed at the following link:
- http://people.canonical.com/~kernel/reports/kt-meeting.txt


Status: Utopic Development Kernel

The Utopic kernel remains based on the v3.16.1 upstream stable kernel
and is available for testing in the archive, ie. linux-3.16.0-11.16.
Please test and let us know your results.
—–
Important upcoming dates:
Thurs Aug 28 – Utopic Beta 1 (~2 days)
Mon Sep 22 – Utopic Final Beta Freeze (~4 weeks away)
Thurs Sep 25 – Utopic Final Beta (~4 weeks away)
Thurs Oct 9 – Utopic Kernel Freeze (~6 weeks away)
Thurs Oct 16 – Utopic Final Freeze (~7 weeks away)
Thurs Oct 23 – Utopic 14.10 Release (~8 weeks away)
o/
o/


Status: CVE’s

The current CVE status can be reviewed at the following link:

http://people.canonical.com/~kernel/cve/pkg/ALL-linux.html


Status: Stable, Security, and Bugfix Kernel Updates – Trusty/Precise/Lucid

o/
Status for the main kernels, until today (Aug. 26):

  • Lucid – verification & testing
  • Precise – verification & testing
  • Trusty – verification & testing

    Current opened tracking bugs details:

  • http://kernel.ubuntu.com/sru/kernel-sru-workflow.html

    For SRUs, SRU report is a good source of information:

  • http://kernel.ubuntu.com/sru/sru-report.html

    Schedule:

    cycle: 08-Aug through 29-Aug
    ====================================================================
    08-Aug Last day for kernel commits for this cycle
    10-Aug – 16-Aug Kernel prep week.
    17-Aug – 23-Aug Bug verification & Regression testing.
    24-Aug – 29-Aug Regression testing & Release to -updates.

    cycle: 29-Aug through 20-Sep
    ====================================================================
    29-Aug Last day for kernel commits for this cycle
    31-Sep – 06-Sep Kernel prep week.
    07-Sep – 13-Sep Bug verification & Regression testing.
    14-Sep – 20-Sep Regression testing & Release to -updates.


Open Discussion or Questions? Raise your hand to be recognized

No open discussion.

Read more

If you glance up to the address bar, you will see that this post is being served securely. I've done this because I believe strongly in the importance of internet privacy, and I support the Reset The Net campaign to encrypt the web.

I've done this completely for free. Here's how:

Get a free certificate

StartSSL isn't the nicest website in the world to use. However, they will give you a free certificate without too much hassle. Click "Sign up" and follow the instructions.

Get an OpenShift Bronze account

Sign up to a RedHat OpenShift Bronze account. Although this account is free to use, as long as you only use one 1-3 gears, it does require you to provide card details.

Once you have an account, create a new application. On the application screen, open the list of domain aliases by clicking on the aliases link (might say "change"):

Application page - click on aliases

Edit your selected domain name and upload the certificate, chain file and private key. NB: Make sure you upload the chain file. If the chain file isn't uploaded initially it may not register later on.

Pushing your site

Now you can push any website to the created application and it should be securely hosted.

Given that you only get 1-3 gears for free, if you have a static site, it's more likely to handle high load. For instance, this site gets about 250 visitors a day and runs perfectly fine on the free resources from OpenShift.

Read more
Nicholas Skaggs

As we continue to iterate on new ubuntu touch images, it's important for everyone to be able to enjoy the ubuntu phone experience in their native language. This is where you can help!

We need your input and help to make sure the phone images are well localized for your native language. If you've never contributed a translation before, this is a perfect opportunity for you to learn. There's a wiki guide to help you, along with translation teams who speak your language and can help.

Don't worry, you don't need a ubuntu phone to do this work. The wiki guide details how to translate using a phone, emulator, or even just your desktop PC running ubuntu. If nothing else, you can help review other folks translations by simply using launchpad in your web browser.

If this sounds interesting to you and the links don't make sense or you would like some more personal help, feel free to contact me. English is preferred, but in the spirit of translation feel free to contact me in French, Spanish or perhaps even German :-).

Happy Translating everyone!

P.S. If you are curious about the status of your language translation, or looking for known missing strings, have a look at the stats page kept by David Planella.

Read more
facundo

Jardinera


Sí, ya sé que estoy denso con las fotitos de Malena... ¿¿¿pero qué quieren que haga???

<baba>
Malena jardinera
</baba>

Read more
Ben Howard

For years, the Ubuntu Cloud Images have been built on a timer (i.e. cronjob or Jenkins). Every week, you can reasonably expect that stable and LTS releases to be built twice a week while our development build is build once a day.  Each of these builds is given a serial in the form of YYYYMMDD. 

While time-based building has proven to be reliable, different build serials may be functionally the same, just put together at a different point in time. Many of the builds that we do for stable and LTS releases are pointless.

When the whole heartbleed fiasco hit, it put the Cloud Image team into over-drive, since it required manually triggering builds the LTS releases. When we manually trigger builds, it takes roughly 12-16 hours to build, QA, test and release new Cloud Images. Sure, most of this is automated, but the process had to be manually started by a human. This got me thinking: there has to be a better way.

What if we build the Cloud Images when the package set changes?

With that, I changed the Ubuntu 14.10 (Utopic Unicorn) build process from time-based to archive trigger-based. Now, instead of building every day at 00:30 UTC, the build starts when the archive has been updated and the packages in the prior cloud image build is older than the archive version. In the last three days, there were eight builds for Utopic. For a development version of Ubuntu, this just means that developers don't have to wait 24 hours for the latest package changes to land in a Cloud Image.

Over the next few weeks, I will be moving the 10.04 LTS, 12.04 LTS and 14.04 LTS build processes from time to archive trigger-based. While this might result less frequent daily builds, the main advantage is that the daily builds will contain the latest package sets. And if you are trying to respond to the latest CVE, or waiting on a bug fix to land, it likely means that you'll have a fresh daily that you can use the following day.

Read more
Dustin Kirkland


Docker 1.0.1 is available for testing, in Ubuntu 14.04 LTS!

Docker 1.0.1 has landed in the trusty-proposed archive, which we hope to SRU to trusty-updates very soon.  We would love to have your testing feedback, to ensure both upgrades from Docker 0.9.1, as well as new installs of Docker 1.0.1 behave well, and are of the highest quality you have come to expect from Ubuntu's LTS  (Long Term Stable) releases!  Please file any bugs or issues here.

Moreover, this new version of the Docker package now installs the Docker binary to /usr/bin/docker, rather than /usr/bin/docker.io in previous versions. This should help Ubuntu's Docker package more closely match the wealth of documentation and examples available from our friends upstream.

A big thanks to Paul Tagliamonte, James Page, Nick Stinemates, Tianon Gravi, and Ryan Harper for their help upstream in Debian and in Ubuntu to get this package updated in Trusty!  Also, it's probably worth mentioning that we're targeting Docker 1.1.2 (or perhaps 1.2.0) for Ubuntu 14.10 (Utopic), which will release on October 23, 2014.

Here are a few commands that might help your testing...

Check What Candidate Versions are Available

$ sudo apt-get update
$ apt-cache show docker.io | grep ^Version:

If that shows 0.9.1~dfsg1-2 (as it should), then you need to enable the trusty-proposed pocket.

$ echo "deb http://archive.ubuntu.com/ubuntu/ trusty-proposed universe" | sudo tee -a /etc/apt/sources.list
$ sudo apt-get update
$ apt-cache show docker.io | grep ^Version:

And now you should see the new version, 1.0.1~dfsg1-0ubuntu1~ubuntu0.14.04.1, available (probably in addition to 1.0.1~dfsg1-0ubuntu1~ubuntu0.14.04.1).

Upgrades

Check if you already have Docker installed, using:

$ dpkg -l docker.io

If so, you can simply upgrade.

$ sudo apt-get upgrade

And now, you can check your Docker version:

$ sudo dpkg -l docker.io | grep -m1 ^ii | awk '{print $3}'
0.9.1~dfsg1-2

New Installations

You can simply install the new package with:

$ sudo apt-get install docker.io

And ensure that you're on the latest version with:

$ dpkg -l docker.io | grep -m1 ^ii | awk '{print $3}'
1.0.1~dfsg1-0ubuntu1~ubuntu0.14.04.1

Running Docker

If you're already a Docker user, you probably don't need these instructions.  But in case you're reading this, and trying Docker for the first time, here's the briefest of quick start guides :-)

$ sudo docker pull ubuntu
$ sudo docker run -i -t ubuntu /bin/bash

And now you're running a bash shell inside of an Ubuntu Docker container.  And only bash!

root@1728ffd1d47b:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:42 ? 00:00:00 /bin/bash
root 8 1 0 13:43 ? 00:00:00 ps -ef

If you want to do something more interesting in Docker, well, that's whole other post ;-)

:-Dustin

Read more