Canonical Voices

Prakash

Tripping valuations of unicorns might have set a dull tone for startups this year, but many industry experts believe that companies that are doing things “the right way” will continue to secure funding. Companies like Big Basket, Practo, Car Dekho, MSwipe and Urban Ladder have continued to show progress in their respective business models in spite of a weak environment.

Prakash AdvaniCanonical’s Regional Director, Sales & Alliances – India & South East Asia said that companies should raise capital as the last resort. If they can quickly convert their ideas into a profitable business then they don’t need to give up on equity.

Read More: https://www.entrepreneur.com/article/274986

Read more
UbuntuTouch

在最新的Scope Widget中,有一个新的Content Sharing Widget.我们可以利用这个Widget来分享我们的图片到信息,Facebook,Twitter等渠道.比如,在我们的Scope Preview中,点击图片上的分享按钮,就可以把我们的内容分享出去.


  


 

它的设计也非常简单明了.

我们还是从我们已经做过的练习中下载代码:

$ git clone https://github.com/liu-xiao-guo/scopetemplates_comment_input

1)添加新的图片资源

query.cpp

void Query::pushResult(sc::SearchReplyProxy const& reply,
                       const string renderer, int i) {
    stringstream ss;
    ss << i;
    string str = ss.str();

    auto cat = reply->register_category( "id" + str, "Template " + str ,
                                         "", sc::CategoryRenderer(renderer) );
    sc::CategorisedResult r(cat);
    r.set_uri( URI.toStdString() );
    r.set_art( images_[0].toStdString() );
//    r.set_art("http://api.map.baidu.com/images/weather/night/leizhenyu.png");
    r["subtitle"] = "Subtitle " + str;
    r.set_title("Title " + str);
    r["summary"] = "Summary: " + str;
    r["fulldesc"] = "fulldesc: " + str;
    r["mascot"] = icons_[0].toStdString();
    r["emblem"] = icons_[1].toStdString();
    r["background"] = background_.toStdString();
    r["overlay-color"] = "#FF0000";

    r["comment_icon"] = icons_[3].toStdString();
    r["share_icon"] = icons_[4].toStdString();
    r["share_pic"] = icons_[5].toStdString();

    QString likes = QString("%1 %2").arg(qstr(u8"\u261d "), "100");
    QString views = QString("%1 %2").arg(qstr(u8"   \u261f "), "99");
    std::string both = qstr("%1 %2").arg(likes,views).toStdString();
    sc::VariantBuilder builder;
    builder.add_tuple({
        {"value", Variant(both)}
    });
    builder.add_tuple({
        {"value", Variant("")}
    });
    r["attributes"] = builder.end();

    r["musicSource"] = "http://qqmp3.djwma.com/mp3/魔音神据极品私货这锯子拉的耳膜都要碎了.mp3";
    r["videoSource"] = "http://techslides.com/demos/sample-videos/small.mp4";
    r["screenshot"] = icons_[2].toStdString();

    // add an array to show the gallary of it
    sc::VariantArray arr;

    for(const auto &datum : icons_) {
        arr.push_back(Variant(datum.toStdString()));
    }

    r["array"] = sc::Variant(arr);

    if (!reply->push(r))
        return;
}

void Query::pushResult(sc::SearchReplyProxy const& reply,
                       const std::shared_ptr<const Category> *cat, int i) {

    stringstream ss;
    ss << i;
    string str = ss.str();

    sc::CategorisedResult r(*cat);
    r.set_uri( URI.toStdString() );
    r.set_art( images_[i].toStdString() );
    r["subtitle"] = "Subtitle " + str;
    r.set_title("Title " + str);
    r["summary"] = "Summary: " + str;
    r["fulldesc"] = "fulldesc: " + str;
    r["mascot"] = icons_[0].toStdString();
    r["emblem"] = icons_[1].toStdString();

    r["comment_icon"] = icons_[3].toStdString();
    r["share_icon"] = icons_[4].toStdString();
    r["share_pic"] = icons_[5].toStdString();

    QString likes = QString("%1 %2").arg(qstr(u8"\u261d "), "100");
    QString views = QString("%1 %2").arg(qstr(u8"   \u261f "), "99");
    std::string both = qstr("%1 %2").arg(likes,views).toStdString();
    sc::VariantBuilder builder;
    builder.add_tuple({
        {"value", Variant(both)}
    });
    builder.add_tuple({
        {"value", Variant("")}
    });
    r["attributes"] = builder.end();

    r["musicSource"] = "";
    r["videoSource"] = "";
    r["screenshot"] = "";
    // This is to ensure that the preview can well for the grid/carousel/lists...
    VariantArray arr;
    r["array"] = sc::Variant(arr);;

    if (!reply->push(r))
        return;
}

在上面我们添加了两个新的图片:

    r["share_icon"] = icons_[4].toStdString();
    r["share_pic"] = icons_[5].toStdString();

注意这里的"share_icon"是在Preview窗口中显示的图片.它可以和我们实际分享的照片是不一样的.在"share_pic"中真正定义的是我们想要分享的图片.在实际的应用中,被分享的图片也可以是一个数组.请开发者自己参考我们的文档来实践.


2)在Preview中添加我们的Widget

preview.cpp

void Preview::run(sc::PreviewReplyProxy const& reply) {
    Result result = PreviewQueryBase::result();

    ColumnLayout layout1col(1);
    std::vector<std::string> ids = { "image", "header", "summary", "tracks",
                                    "videos", "gallery_header", "gallerys", "reviews", "exp",
                                     "review_input", "rating_input", "inputId", "img" };
//    std::vector<std::string> ids = { "inputId", "img" };

    PreviewWidgetList widgets;

    ColumnLayout layout2col(2);
    layout2col.add_column(ids);
    layout2col.add_column({});

    ColumnLayout layout3col(3);
    layout3col.add_column(ids);
    layout3col.add_column({});
    layout3col.add_column({});

    // Define the header section
    sc::PreviewWidget header("header", "header");
    // It has title and a subtitle properties
    header.add_attribute_mapping("title", "title");
    header.add_attribute_mapping("subtitle", "subtitle");
    widgets.emplace_back(header);

    // Define the image section
    sc::PreviewWidget image("image", "image");
    // It has a single source property, mapped to the result's art property
    image.add_attribute_mapping("source", "art");
    widgets.emplace_back(image);

    // Define the summary section
    sc::PreviewWidget description("summary", "text");
    // It has a text property, mapped to the result's description property
    description.add_attribute_mapping("text", "description");
    widgets.emplace_back(description);

    PreviewWidget listen("tracks", "audio");
    {
        VariantBuilder builder;
        builder.add_tuple({
            {"title", Variant("This is the song title")},
            {"source", Variant(result["musicSource"].get_string().c_str())}
        });
        listen.add_attribute_value("tracks", builder.end());
    }

    PreviewWidget video("videos", "video");
    video.add_attribute_value("source", Variant(result["videoSource"].get_string().c_str()));
    video.add_attribute_value("screenshot", Variant(result["screenshot"].get_string().c_str()));

    PreviewWidget header_gal("gallery_header", "header");
    header_gal.add_attribute_value("title", Variant("Gallery files are:"));

    PreviewWidget gallery("gallerys", "gallery");
    gallery.add_attribute_mapping("sources", "array");

    if ( result["musicSource"].get_string().length() != 0 ) {
        widgets.emplace_back(listen);
    }

    if( result["videoSource"].get_string().length() != 0 ) {
        widgets.emplace_back(video);
    }

    if( result["array"].get_array().size() != 0 ) {
        widgets.emplace_back(header_gal);
        widgets.emplace_back(gallery);
    }

    // The following shows the review
    PreviewWidget review("reviews", "reviews");
    VariantBuilder builder;
    builder.add_tuple({
                          {"author", Variant("John Doe")},
                          {"review", Variant("very good")},
                          {"rating", Variant(3.5)}
                      });
    builder.add_tuple({
                          {"author", Variant("Mr. Smith")},
                          {"review", Variant("very poor")},
                          {"rating", Variant(5)}
                      });
    review.add_attribute_value("reviews", builder.end());
    widgets.emplace_back(review);

    // The following shows the expandable
    PreviewWidget expandable("exp", "expandable");
    expandable.add_attribute_value("title", Variant("This is an expandable widget"));
    expandable.add_attribute_value("collapsed-widgets", Variant(1));
    PreviewWidget w1("w1", "text");
    w1.add_attribute_value("title", Variant("Subwidget 1"));
    w1.add_attribute_value("text", Variant("A text"));
    PreviewWidget w2("w2", "text");
    w2.add_attribute_value("title", Variant("Subwidget 2"));
    w2.add_attribute_value("text", Variant("A text"));
    expandable.add_widget(w1);
    expandable.add_widget(w2);
    widgets.emplace_back(expandable);

    // The following shows a review rating-input
    PreviewWidget w_review("review_input", "rating-input");
    w_review.add_attribute_value("submit-label", Variant("Send"));
    w_review.add_attribute_value("visible", Variant("review"));
    w_review.add_attribute_value("required", Variant("review"));
    std::string reply_label = "Reply";
    std::string max_chars_label = "140 characters max";
    w_review.add_attribute_value("review-label", Variant(reply_label + ": " + max_chars_label));
    widgets.emplace_back(w_review);

    // The follwing shows a rating rating-input
    PreviewWidget w_rating("rating_input", "rating-input");
    w_rating.add_attribute_value("visible", Variant("rating"));
    w_rating.add_attribute_value("required", Variant("rating"));
    w_rating.add_attribute_value("rating-label", Variant("Please rate this"));
    widgets.emplace_back(w_rating);

    PreviewWidget w_image("img", "image");
    w_image.add_attribute_value("source", Variant(result["share_icon"].get_string().c_str()));
    VariantMap share_data;
//    share_data["uri"] = Variant("http://img2.imgtn.bdimg.com/it/u=442803940,143587648&fm=21&gp=0.jpg");
    share_data["uri"] = Variant(result["share_pic"].get_string().c_str());
    share_data["content-type"] = Variant("pictures");
    w_image.add_attribute_value("share-data",  sc::Variant(share_data));
    widgets.emplace_back(w_image);

    PreviewWidget w_commentInput("inputId", "comment-input");
    w_commentInput.add_attribute_value("submit-label", Variant("Post"));
    widgets.emplace_back(w_commentInput);

    // In the following, fake some comments data
    QList<Comment> comment_list;
    std::string comment_str = "Comment ";
    for(int i = 0; i < 3;  i++) {
        Comment comment;

        comment.id         = 1.0;
        comment.publishTime = "2015-3-18";
        comment.text = comment_str;
        comment.text += std::to_string(i+1);
        comment_list.append(comment);
    }

    int index = 0;
    Q_FOREACH(const auto & comment, comment_list) {
        std::string id = "commentId_" + std::to_string(index++);
        ids.emplace_back(id);

        PreviewWidget w_comment(id, "comment");
        w_comment.add_attribute_value("comment", Variant(comment.text));
        w_comment.add_attribute_value("author", Variant("Author"));
        w_comment.add_attribute_value("source", Variant(result["comment_icon"].get_string().c_str()));
        w_comment.add_attribute_value("subtitle", Variant(comment.publishTime));
        widgets.emplace_back(w_comment);
    }

    layout1col.add_column(ids);
    reply->register_layout({layout1col, layout2col, layout3col});
    reply->push( widgets );
}

在上面的代码中,我们添加了

    PreviewWidget w_image("img", "image");
    w_image.add_attribute_value("source", Variant(result["share_icon"].get_string().c_str()));
    VariantMap share_data;
//    share_data["uri"] = Variant("http://img2.imgtn.bdimg.com/it/u=442803940,143587648&fm=21&gp=0.jpg");
    share_data["uri"] = Variant(result["share_pic"].get_string().c_str());
    share_data["content-type"] = Variant("pictures");
    w_image.add_attribute_value("share-data",  sc::Variant(share_data));
    widgets.emplace_back(w_image);

同时,我们也在下面的Ids中添加了新增加的img ID:

    std::vector<std::string> ids = { "image", "header", "summary", "tracks",
                                    "videos", "gallery_header", "gallerys", "reviews", "exp",
                                     "review_input", "rating_input", "inputId", "img" };

通过这样的方法,当我们的照片被显示时,在图片的左下方就会出现一个分享的按钮.



点击我们的按钮就可以把我们在上面"share_pic"中定义的照片分享出去.这个照片也可以是网路上的一张照片.


作者:UbuntuTouch 发表于2016/3/30 8:25:59 原文链接
阅读:551 评论:0 查看评论

Read more
UbuntuTouch

我刚看了一下AbstractButton.这个API提供了一个最基本的供我们写我们的自己按钮的功能,比如它有clicked, pressAndHold信号供我们使用,同时也提供了一个最基本的一下属性,比如hover及触觉功能等.在最新的API中,它也提供了一个最新的功能:


sensingMargins
sensingMargins.left : real
sensingMargins.right : real
sensingMargins.top : real
sensingMargins.bottom : real
sensingMargins.all : real


这个属性是为了能够使得我们定义我们按钮的触发区域,比如,我们想在延伸我们的按钮的区域使得我们可以在更大的区域对我们的按钮产生按钮事件.在下面的例程中,我们来做一个简单的实验:


Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

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

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

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

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

        Rectangle {
            id: red
            anchors.centerIn: parent
            width: units.gu(10)
            height: units.gu(10)
            border.color: "red"

            AbstractButton {
                anchors.fill: parent
                sensingMargins {
                    left: units.gu(1)
                    top: units.gu(1)
                    bottom: units.gu(1)
                    right: units.gu(10)
                }

                onClicked: {
                    console.log("it is clicked in red")
                }
            }
        }

        Rectangle {
            id: blue
            anchors.left: red.right
            anchors.top: red.top
            border.color: "blue"
            width: units.gu(10)
            height: units.gu(10)

//            MouseArea {
//                anchors.fill: parent
//                onClicked: {
//                    console.log("it is clicked in blue")
//                }
//            }
        }
    }
}

在上面,我们定义了一个红色区域的按钮,同时我们也定义了一个蓝色的区域.在红色的按钮中,我们定义了:

 sensingMargins {
                    left: units.gu(1)
                    top: units.gu(1)
                    bottom: units.gu(1)
                    right: units.gu(10)
                }

qml: it is clicked in red
qml: it is clicked in red
qml: it is clicked in red
qml: it is clicked in red

这是因为我们在红色按钮中把它可以触碰的区域延伸到了蓝色的方框中.当然如果,我们把我们上面的代码修改为如下:

        Rectangle {
            id: blue
            anchors.left: red.right
            anchors.top: red.top
            border.color: "blue"
            width: units.gu(10)
            height: units.gu(10)

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    console.log("it is clicked in blue")
                }
            }
        }

那么当我们在蓝色区域触屏时,我们再也看不到上面的输出了.



值得指出的是,由于这个sensingMargins比较新,所以它需要最新的软件版本才能够支持.具体要求可以参阅bug

作者:UbuntuTouch 发表于2016/3/21 11:51:23 原文链接
阅读:250 评论:0 查看评论

Read more
UbuntuTouch

我们知道一个好的工具可以帮我们更好更快地开发我们的应用.在今天的文章中,我来介绍一下我最新开发的一个文件浏览器工具.这个工具可以很方便地帮我们打开我们所需要的文件,并快速地查看我们应用/Scope输出的信息,以便更方便地调试我们的应用.由于该应用使用了"unconfined"安全策略,所有不能上传到应用商店.如果开发者感兴趣的话,可以直接从我的github中下载,并安装到自己的手机中.目前应用还在完善中.如果大家有什么建议的话,欢迎提出来.我会在下一个版本中考虑进去的.


应用截图:


    


   


  




具体使用方法:


1)在目录的图片上向右滑动,进入到下一个目录中

2)在窗口中向左滑动,退回到上一个目录中

3)双击当前的文件,查看文件的内容.目前只支持.txt,.log,.conf,jpg/jpeg/png文件的查看

4)点击右上的"h"图标来查看hidden文件

5)点击右上"log"图标,只显示log文件.用于快速定位log文件.如果想看到其它格式的文件,就需要取消只显示log文件图标.

6)点击"syslog"图标,显示/var/log/syslog中的内容,供调试


对于我们开发者开发Scope来说,我们可以查看/home/phablet/.cache/upstart/scope_registry.log文件来查看我们的输出以帮助我们来更好地调试我们的Scope.


具体安装步骤,请参阅github中的"readme.txt"文件.

项目文件:https://github.com/liu-xiao-guo/filebrowser

更多想下载有趣的"unconfined"应用的开发者可以参阅网址https://open.uappexplorer.com/apps.里面也有一个文件浏览器,但是它打不开log文件等.操作起来应该没有我的这个方便 :)

作者:UbuntuTouch 发表于2016/3/21 13:29:53 原文链接
阅读:326 评论:0 查看评论

Read more
UbuntuTouch

由于一些原因,在Ubuntu手机上,只支持一个前台运行的应用.每当应用被推到后台后,就会被自动挂起(suspended),该应用将不再被运行.这也是Ubuntu手机和安卓系统不一样的地方.更确切地说,Ubuntu手机应用的设计更类似于iPhone的应用.只允许一个前台运行的应用.当然如果把手机接到一个无线鼠标和键盘,那么我们的手机会自动进入到Desktop模式,也就自动具有多任务的功能.

对于很多开发者来说,没有了多任务,就好像没有了一些创新的应用,比如,我们不可能把导航的应用推到后台就不运行了.在实际的一些应用中,能够在后台运行,也是一个非常有用的功能.在今天的文章中,我们将介绍如何实现能够在后台运行的应用.在该应用中,我们使用了"unconfined"安全策略.必须注意的一点是,这个样的应用将不能进入到Ubuntu Store.如果有可能的话,可以上传到https://open.uappexplorer.com/apps商店供一些特别需要的用户使用.


1)使用unconfined安全策略


就像我们上面提到的,为了能够访问我们一般应用访问不到的文件地址,我们必须在我们的apparmor文件中定义一些特别的安全策略:

{
    "policy_groups": [
        "networking"
    ],
    "policy_version": 1.3,
    "template": "unconfined"
}

有了上面unconfined定义,我们的应用就可以访问到一些我们不能访问到的资源.从某种意义上讲,已经打破了我们在Ubuntu手机上所设置的安全机制.必须注意的是这样的不明来源的应用可能对我们的手机造成伤害


2)修改应用的lifecycleException标示



一旦一个应用的这个lifecycleException标志被修改,我们的应用在被推到后台时,将不被系统所挂起(suspended).那么我们怎么修该我们的应用的这个标示呢?我们可以仿照在launchpad.net上的https://launchpad.net/tweakgeek项目.我们可以通过上面的方法来获取我们当前应用的信息,并做适当的修改.

Main.cpp

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

#include "applicationmodel.h"

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

    qmlRegisterType<ApplicationModel>("TweakGeek", 1, 0, "ApplicationModel");
    qmlRegisterUncreatableType<ApplicationItem>("TweakGeek", 1, 0, "ApplicationItem", "bla");

    QQuickView view;
    view.setSource(QUrl(QStringLiteral("qrc:///Main.qml")));
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.show();
    return app.exec();
}

在上面我们把ApplicationModel及ApplicationItem数据类型进行登记,这样我们可以在我们的QML中运用它们.

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3
import TweakGeek 1.0
import QtQuick.Layouts 1.1

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

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

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

    property int index: 0
    property var myapp

    ApplicationModel {
        id: applicationModel
    }

    Timer {
        interval: 500; running: true; repeat: true
        onTriggered: {
            index ++
            console.log("index: " + index)
        }
    }

    function findMyApp() {
        var count = applicationModel.count();
        for ( var i = 0 ; i < count; i++ ) {
            var app = applicationModel.get(i)
            // console.log("app name: " + app.name)
            if ( app.name === "keeprunningapp" ) {
                // if our own app is found, make it not die
                console.log("My app setLifecycleException: " + app.lifecycleException)
                myapp = app
            }
        }
    }

    Page {
        header: PageHeader {
            title: "keeprunningapp"
        }

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

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

            Label {
                text: "current app is: " + Qt.application.active
                fontSize: "large"
            }

            Label {
                text: "index: " + index
                fontSize: "large"
            }

            RowLayout {
                spacing: units.gu(5)

                Label {
                    text: "Prevent suspending"
                    Layout.fillWidth: true
                    fontSize: "large"
                }

                Switch {
                    checked: myapp.lifecycleException
                    onClicked: myapp.lifecycleException = checked
                }
            }
        }
    }

    Component.onCompleted: {
        findMyApp();
    }
}

我们的实现也非常地简单.我们首先通过findMyApp来寻找到我们自己应用的实例,并在Switch中:
                Switch {
                    checked: myapp.lifecycleException
                    onClicked: myapp.lifecycleException = checked
                }
进行绑定.

在我们的应用,为了显示我们的应用是否正在运行,我们特别设计了一个定时器,每隔500毫秒,输出一行字.

    Timer {
        interval: 500; running: true; repeat: true
        onTriggered: {
            index ++
            console.log("index: " + index)
        }
    }

我们可以运行我们的应用:



当我们的应用的"Prevent suspending"开关设为"关"时,每当应用被推到后台,上面的定时器的输出就会停止,表明应用被suspended.而当我们的开关设为"开"时,每当应用被推到后台时,在我们的应用窗口还是可以看到连绵不断地输出:

qml: index: 418
qml: index: 419
qml: index: 420
qml: index: 421
qml: index: 422
qml: index: 423
qml: index: 424
qml: index: 425
qml: index: 426
qml: index: 427

整个项目的源码在:https://github.com/liu-xiao-guo/keeprunningapp




作者:UbuntuTouch 发表于2016/3/22 14:13:17 原文链接
阅读:381 评论:0 查看评论

Read more
UbuntuTouch

我们知道对于一些手机应用来说,能够得到屏幕的尺寸信息已经屏幕的方向,那么对我们的应用的布局非常中.我们可以通过对这个事件的感知,进一步来对我们的应用的布局来重新调整.


下面我们来通过一个简单的例程来显示在Ubuntu Screen API中有那些信息.


Main.qml

import QtQuick 2.4
import Ubuntu.Components 1.3
import QtQuick.Window 2.2

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"

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

    property var orientations: ["PrimaryOrientation", "PortraitOrientation",
                    "LandscapeOrientation","", "InvertedPortraitOrientation","", "","",
                    "InvertedLandscapeOrientation" ]


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

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

            Label {
                text: "desktopAvailableHeight: " + Screen.desktopAvailableHeight
            }

            Label {
                text: "devicePixelRatio: " + Screen.devicePixelRatio
            }

            Label {
                text: "desktopAvailableWidth: " + Screen.desktopAvailableWidth
            }

            Label {
                text: "height: " + Screen.height
            }

            Label {
                text: "width: " + Screen.width
            }

            Label {
                text: "name: " + Screen.name
            }

            Label {
                text: "orientation: " + orientations[Screen.orientation]
            }

            Label {
                text: "primaryOrientation: " + orientations[Screen.primaryOrientation]
            }

            Label {
                text: "orientationUpdateMask: " + Screen.orientationUpdateMask
            }

            Label {
                text: "pixelDensity: " + Screen.pixelDensity
            }
        }
    }
}

在我们的MX4手机上运行我们的应用:

    

从上面的信息中,我们可以看出来,我们的Screen API给出的所有的信息.细心的开发者将会发现这里得到的信息和我们之前在文章"如何得到屏幕和可用显示区域的大小尺寸及运用分辨率无关的编程"得到的屏幕尺寸是一样的.


作者:UbuntuTouch 发表于2016/3/22 17:08:29 原文链接
阅读:242 评论:0 查看评论

Read more
UbuntuTouch

地图在我们的生活中扮演非常重要的角色.我们可以利用Ubuntu手机中提供的位置服务得到位置信息并显示自己的位置在百度地图上.在显示位置信息时,我们必须记住一点,我们必须要进行必要的坐标转换,否则我们所显示的地图信息不是精确的.具体更多的信息可以访问"坐标转换API Web服务API".


下面我们来通过一个例程来显示如何进行坐标转换,并显示正确的地图信息.


Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3
import "WebApi.js" as API
import QtPositioning 5.0

MainView {
    id:  mainScreen

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

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

    property string longitude: "116.3883"
    property string latitude: "39.9289"

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

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

            mainScreen.longitude = position.coordinate.longitude;
            mainScreen.latitude = position.coordinate.latitude;

            before.source = API.getStaticMap(longitude, latitude)

            // Do the conversion here
            API.convertCoordinates(longitude, latitude, gotConverted)
        }

        onSourceErrorChanged: {
            console.log("Source error: " + sourceError);
        }
    }


    function gotConverted(o) {
        after.source = API.getStaticMap(o.longitude, o.latitude)
    }

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

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

            Image {
                id: before
                width: parent.width
                height: parent.height/2                

                Label {
                    text: "Before conversion"
                    fontSize: "large"
                }
            }

            Image {
                id: after
                width: parent.width
                height: parent.height/2

                Label {
                    text: "After conversion"
                    fontSize: "large"
                }
            }
        }
    }
}

在上面的代码中,我们使用:

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

            mainScreen.longitude = position.coordinate.longitude;
            mainScreen.latitude = position.coordinate.latitude;

            before.source = API.getStaticMap(longitude, latitude)

            // Do the conversion here
            API.convertCoordinates(longitude, latitude, gotConverted)
        }

        onSourceErrorChanged: {
            console.log("Source error: " + sourceError);
        }
    }

来得到我们的当前的位置信息:经度,维度.我们可以通过百度的staticmap API接口进行显示地图信息.
在上面的代码中,我们显示两个图片,一个是没有经过转换的图片,另外一个是经过坐标转换后的地图图片.经过比对,我们会发现,经过转换过的地图信息更贴近我们的实际的位置.
运行我们的项目:



不怕大家知道,上面显示的正是我家的地址.非常精确.
整个项目的源码在:https://github.com/liu-xiao-guo/baidumap


作者:UbuntuTouch 发表于2016/3/23 14:12:58 原文链接
阅读:315 评论:0 查看评论

Read more
UbuntuTouch

在有些设计中,比如,我们想在鼠标所在的区域以外响应我们的鼠标按钮事件来消除一个popup,那么我们可以利用Ubuntu SDK中所提供的InverseMouseArea来实现这个功能.

下面我们来通过一个例子来展示如何利用InverseMouseArea来实现这个功能.

首先,我们来创建一个Popup.qml文件

Popup.qml

import QtQuick 2.4
import Ubuntu.Components 1.3

Rectangle {
    anchors.centerIn: parent
    width: parent.width/2
    height: width
    color: "darkgray"
    radius: 10

    InverseMouseArea {
       anchors.fill: parent
       acceptedButtons: Qt.LeftButton
       onPressed: parent.destroy()
    }
}


在上面的Popup定义了一个InverseMouseArea.当我们在这个Popup以外的地方进行点击的时候,那么它将自动销毁.

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \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: "inversemouse.liu-xiao-guo"

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

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

        Rectangle {
            width: parent.width
            height: parent.height/2
            border.color: "red"

            Button {
                id: button
                text: "Press me"
                onClicked: {
                    var component = Qt.createComponent("Popup.qml");
                    var obj = component.createObject(parent);
                    obj.visible = true;
                }
            }
        }
    }
}

在我们的Main.qml中,我们利用一个按钮"Press me".当它被点击的时候,动态地生产一个Popup的控件.当我们在控件上点鼠标时,没有任何事情发生,但是如果,我们在它之外的任何一个地方点击的时候,那么这个Popup就会被销毁.

运行我们的应用:

  

当然如果我们想在我们的Popup中得到鼠标的按钮信息的话,我们也可以在我们的Popup中加入MouseArea来进行捕获.

Popup.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

Rectangle {
    anchors.centerIn: parent
    width: parent.width/2
    height: width
    color: "darkgray"
    radius: 10

    MouseArea {
        anchors.fill: parent

        onClicked: {
            console.log("it is clicked inside popup")
        }
    }

    InverseMouseArea {
       anchors.fill: parent
       acceptedButtons: Qt.LeftButton
       onPressed: parent.destroy()
    }
}

整个项目的代码在:https://github.com/liu-xiao-guo/inversemouse

另外如果我们想对鼠标的点击区域进行限制的话,我们可以通过利用:

sensingArea : Item

来做限制,比如下面的代码:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

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

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

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

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

        Rectangle {
            width: units.gu(40)
            height: units.gu(71)
            border.color: "yellow"
            border.width: units.gu(1)

            MouseArea {
                anchors.fill: parent
                onClicked: console.log("clicked on the root component")
            }

            Rectangle {
                id: blueRect
                width: units.gu(30)
                height: units.gu(51)
                anchors.centerIn: parent
                color: "blue"

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

                    InverseMouseArea {
                        anchors.fill: parent
                        sensingArea: blueRect
                        onClicked: console.log("clicked on the blue rect")
                    }
                }
            }
        }
    }
}

在上面的代码中,我们通过blueRect来限制InverseMouseArea的作用范围.也就是在蓝色和红色直接的范围才会显示:

qml: clicked on the blue rect

而在其它的任何地方点击时都会显示:

qml: clicked on the root component

我们的应用的显示如下:




更多关于这个API的阅读,请参阅连接InverseMouseArea


作者:UbuntuTouch 发表于2016/3/24 11:01:02 原文链接
阅读:269 评论:0 查看评论

Read more
UbuntuTouch

[原]Ubuntu屏幕尺寸及字体大小

在今天的这篇文章中,我们们来做一个应用来显示Ubuntu字体及屏幕大小.这篇文章的内容在以前的一些文章中也有提及.在这里,我把所有的内容综合到一起,做成一个应用.这样大家可以一目了然.


先贴我们的应用:


import QtQuick 2.4
import Ubuntu.Components 1.3
import QtQuick.Window 2.2

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

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

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

    property string fontsize: listview.currentItem.fontsize
    property var orientations: ["PrimaryOrientation", "PortraitOrientation",
        "LandscapeOrientation","", "InvertedPortraitOrientation","", "","",
        "InvertedLandscapeOrientation" ]

    Component {
        id: highlightBar
        Rectangle {
            width: 200; height: 50
            color: "#FFFF88"
            y: listview.currentItem.y;
            Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
        }
    }

    Page {
        title: i18n.tr("Ubuntu Sizes")

        Rectangle {
            id: rect
            anchors.fill: parent

            Component.onCompleted: {
                client.text = "client area width: " + rect.width/units.gu(1) + "gu height: " + rect.height/units.gu(1) + "gu"
            }

            onWidthChanged: {
                clientgu.text = "client area width: " + Math.round(rect.width/units.gu(1)) + "gu height: " + Math.round(rect.height/units.gu(1)) + "gu"
                client.text = "client area (" + rect.width + ", " + height + ")"
            }

            onHeightChanged: {
                client.text = "client area (" + rect.width + ", " + height + ")"
                clientgu.text = "client area width: " + Math.round(rect.width/units.gu(1)) + "gu height: " + Math.round(rect.height/units.gu(1)) + "gu"
            }
        }

        Flickable {
            anchors.fill: parent
            contentHeight:content.childrenRect.height

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

                Text {
                    id: unitsgu
                    text: "1 units.gu = " + units.gu(1) + " pixels"
                }

                Label {
                    text: "desktopAvailableHeight: " + Screen.desktopAvailableHeight
                }

                Label {
                    text: "devicePixelRatio: " + Screen.devicePixelRatio
                }

                Label {
                    text: "desktopAvailableWidth: " + Screen.desktopAvailableWidth
                }

                Label {
                    text: "Screen height: " + Screen.height
                }

                Label {
                    text: "Sreen width: " + Screen.width
                }

                Label {
                    text: "orientation: " + orientations[Screen.orientation]
                }

                Label {
                    text: "primaryOrientation: " + orientations[Screen.primaryOrientation]
                }

                Label {
                    text: "orientationUpdateMask: " + Screen.orientationUpdateMask
                }

                Label {
                    text: "pixelDensity: " + Screen.pixelDensity
                }

                Label {
                    id: info
                    text: "screen width: " + Math.round(Screen.width/units.gu(1)) + "gu  height: " + Math.round(Screen.height/units.gu(1)) + "gu"
                }

                Label {
                    id: clientgu
                }

                Label {
                    id: client
                }

                Label {
                    text: "1 dp = " + units.dp(1) + " pixels"
                }

                Row {
                    spacing: units.gu(1)

                    Label {
                        id: mylabel
                        anchors.verticalCenter: parent.verticalCenter
                        text: "I love"
                        fontSize: fontsize
                    }

                    Text {
                        anchors.verticalCenter: parent.verticalCenter
                        text: mylabel.fontSize
                    }

                    Text {
                        anchors.verticalCenter: parent.verticalCenter
                        text: mylabel.font.pixelSize + " pixels"
                    }

                    Text {
                        anchors.verticalCenter: parent.verticalCenter
                        text: (mylabel.font.pixelSize/units.gu(1)).toFixed(2)+ " units.gu"
                    }
                }

                Row {
                    width: parent.width

                    Label {
                        width: parent.width/2
                        text: "Font Size"
                        font.bold: true
                        fontSize: "large"

                    }

                    Label {
                        width: parent.width/2
                        text: "ModularScale"
                        font.bold: true
                        fontSize: "large"
                    }

                }

                ListView {
                    id: listview
                    anchors.horizontalCenter: parent.horizontalCenter
                    width: parent.width
                    // height: parent.height - mylabel.height
                    height: units.gu(20)
                    highlight: highlightBar
                    model: ["xx-small","x-small", "small", "medium", "large", "x-large" ]
                    delegate: Item {
                        width: listview.width
                        height: layout.height
                        property string fontsize: modelData

                        Row {
                            id: layout
                            width: parent.width

                            Label {
                                width: parent.width/2
                                text: modelData
                                fontSize: "large"
                            }

                            Label {
                                width: parent.width/2
                                text: (FontUtils.modularScale(modelData)).toFixed(2)
                                fontSize: "large"
                            }

                        }

                        MouseArea {
                            anchors.fill: parent
                            onClicked: {
                                listview.currentIndex = index
                            }
                        }
                    }
                }
            }
        }
    }
}

运行我们的应用:


   


大家可以在Ubuntu商店中下载UbuntuSizes应用来测试自己的应.

项目源码:https://github.com/liu-xiao-guo/ubuntusizes

作者:UbuntuTouch 发表于2016/3/28 9:42:40 原文链接
阅读:314 评论:0 查看评论

Read more
UbuntuTouch

ViewItems是依附于一个ListView或ListItem中的属性.我们可以利用它来管理我们的ListView或ListItem中项的drag-and-drop.下面我们来通过一个简单的例程来展示如何利用它来实现一些我们需要的功能:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

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

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

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

    Page {
        id: page
        header: PageHeader {
            title: "viewitems"
        }

        ListModel {
            id: mymodel
            ListElement { name: "images/image1.jpg" }
            ListElement { name: "images/image2.jpg" }
            ListElement { name: "images/image3.jpg" }
            ListElement { name: "images/image4.jpg" }
            ListElement { name: "images/image5.jpg" }
            ListElement { name: "images/image6.jpg" }
            ListElement { name: "images/image7.jpg" }
            ListElement { name: "images/image8.jpg" }
            ListElement { name: "images/image9.jpg" }
            ListElement { name: "images/image10.jpg" }
            ListElement { name: "images/image11.jpg" }
            ListElement { name: "images/image1.jpg" }
            ListElement { name: "images/image2.jpg" }
            ListElement { name: "images/image3.jpg" }
        }

        ListView {
            width: parent.width
            height: parent.height - page.header.height
            anchors.top: page.header.bottom

            model: mymodel
            delegate: ListItem {
                Image {
                    anchors.verticalCenter: parent.verticalCenter
                    source: name
                    height: units.gu(8)
                    width: height
                }

                Label {
                    text: index
                    anchors.horizontalCenter: parent.horizontalCenter
                }

                dragMode: false

                color: dragMode ? "white" : "lightgray"
                onPressAndHold: ListView.view.ViewItems.dragMode =
                                !ListView.view.ViewItems.dragMode

                onDragModeChanged: {
                    console.log("dragMode: " + dragMode)
                }
            }

            ViewItems.dragMode: true
            ViewItems.onDragUpdated: {
                if (event.status === ListItemDrag.Started) {
                    console.log("event.from: " + event.from + " " + "event.to: " + event.to)
                    if (event.from < 5) {
                        // deny dragging on the first 5 element
                        event.accept = false;
                    } else if (event.from >= 5 && event.from <= 10 ) {
                        // specify the interval
                        event.minimumIndex = 5;
                        event.maximumIndex = 10;
                    } else if (event.from > 10) {
                        // prevent dragging to the first 11 items area
                        event.minimumIndex = 11;
                    }
                } else {
                    console.log("moving ....")
                    model.move(event.from, event.to, 1);
                }
            }
        }
    }
}

我们来运行我们的代码.



在上面的实现中,我们可以通过对event.status === ListItemDrag.Started的检测来限制那些项是可以移动的,或定义它们的移动的范围.

            ViewItems.onDragUpdated: {
                if (event.status === ListItemDrag.Started) {
                    console.log("event.from: " + event.from + " " + "event.to: " + event.to)
                    if (event.from < 5) {
                        // deny dragging on the first 5 element
                        event.accept = false;
                    } else if (event.from >= 5 && event.from <= 10 ) {
                        // specify the interval
                        event.minimumIndex = 5;
                        event.maximumIndex = 10;
                    } else if (event.from > 10) {
                        // prevent dragging to the first 11 items area
                        event.minimumIndex = 11;
                    }
                } else {
                    console.log("moving ....")
                    model.move(event.from, event.to, 1);
                }
            }

在上面的例子中,当我们在最前面的5个列表项中去移动时,它根本没有反应.当为我们在5-10的范围去移动我们的项时,如果在我们drop时,落下的项不在5-10之间,也不起任何的作用.也就是说5-10之间的项只能在它们之间进行交换.如果是10以外的项,它们只能在10以外的地方进行交换.



作者:UbuntuTouch 发表于2016/3/28 10:40:26 原文链接
阅读:292 评论:0 查看评论

Read more
UbuntuTouch

对于大多数的网页开发者来说,我们可以充分利用我们的QML UI框架来实现一些比较炫的界面,我们也可以利用HTML5现有的应用架构来实现我们基于Web的应用.在今天的例程中,我们来展示如何把这两个方面充分结合起来.这样可以使得我们的应用更加炫,更加充分利用现有的Ubuntu QML APIs来实现我们的一些功能.


1)设计我们的HTML文件


<html>
<head>
<script>
    document.addEventListener("QMLmessage", function (event) {
        document.body.innerHTML += "<p><font size='20' color='red'>Message received.  You said " + event.detail.greeting + "</font></p>";
    });
</script>
</head>

<body>
<h1>Hello</h1>
</body>
</html>

为了说明文档的方便,我设计了一个简单的界面.


在上面的界面中,html显示的内容不包括上面的按钮部分.只显示一个"Hello".同时我们定义了一个如下的一个事件响应器:

 document.addEventListener("QMLmessage", function (event) {
        document.body.innerHTML += "<p><font size='20' color='red'>Message received.  You said " + event.detail.greeting + "</font></p>";
    });

每当有一个叫做QMLmessage的实际响应的时候,我们会在界面上自动加入我们想要的一行字.比如:




2)触发QMLmessage事件


在下面的Main.qml代码中:

import QtQuick 2.4
import Ubuntu.Components 1.3
import Ubuntu.Web 0.2
import com.canonical.Oxide 1.9 as Oxide

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

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

    property string usContext: "messaging://"

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

    Page {
        title: i18n.tr("webview-oxide")

        Rectangle {
            anchors.fill: parent

            // Both the UserScript and the call to sendMessage need to share the same
            // context, which should be in the form of a URL.  It doesn't seem to matter
            // what it is, though.
            property string usContext: "messaging://"

            WebView {
                id: webview
                anchors {
                    top: parent.top
                    left: parent.left
                    right: parent.right
                    bottom: button.top
                }
                context: webcontext
                url: {
                    console.log("address: " + Qt.resolvedUrl("index.html"))
                    return Qt.resolvedUrl("index.html")
                }
            }

            WebContext {
                id: webcontext
                userScripts: [
                    Oxide.UserScript {
                        context: usContext
                        url: Qt.resolvedUrl("oxide-user.js")
                    }
                ]
            }

            Button {
                id: button
                anchors {
                    bottom: parent.bottom
                    left: parent.left
                    right: parent.right
                }
                text: "Press Me"
                onClicked: {
                    var req = webview.rootFrame.sendMessage(usContext, "MESSAGE", {greeting: "Hello"})
                    req.onreply = function (msg) {
                        console.log("Got reply: " + msg.str);
                    }
                    req.onerror = function (code, explanation) {
                        console.log("Error " + code + ": " + explanation)
                    }
                }
            }
        }
    }
}

我们通过按钮"button"来向我们的WebView发送我们需要的QMLmessage事件.在这里我们使用了:

            WebContext {
                id: webcontext
                userScripts: [
                    Oxide.UserScript {
                        context: usContext
                        url: Qt.resolvedUrl("oxide-user.js")
                    }
                ]
            }

上面的oxide-user的内容为:

oxide.addMessageHandler("MESSAGE", function (msg) {
    var event = new CustomEvent("QMLmessage", {detail: msg.args});
    document.dispatchEvent(event);
    msg.reply({str: "OK"});
});

也就是在我们发送完一个信息后,我们同时也得到一个返回的信息"OK".这个可以在为我们的应用输出中可以看出:



这样我们实现了从QML到HTML文件的交互.这对于我们设计一些基于地图定位或导航的应用来说是非常中要的手段.



作者:UbuntuTouch 发表于2016/3/29 12:10:45 原文链接
阅读:294 评论:0 查看评论

Read more
UbuntuTouch

在新的Scope设计中,有一个新的Comment-input PreviewWidget.这个可以帮我用来列举我们收到评论,比如在点评中,对一个餐馆的评论文字.


在上面的显示中,我们显示了Author,时间,及Comments.在今天的实验中,我们将介绍如何实现这样的功能.


我们首先还是从我们以前做过的练习中来讲述.下载我们的代码:

$ git clone https://github.com/liu-xiao-guo/scopetemplates_rating_input

在我们的代码中,我们添加如下的东西:

query.cpp


void Query::pushResult(sc::SearchReplyProxy const& reply,
                       const string renderer, int i) {
    stringstream ss;
    ss << i;
    string str = ss.str();

    auto cat = reply->register_category( "id" + str, "Template " + str ,
                                         "", sc::CategoryRenderer(renderer) );
    sc::CategorisedResult r(cat);
    r.set_uri( URI.toStdString() );
    r.set_art( images_[0].toStdString() );
//    r.set_art("http://api.map.baidu.com/images/weather/night/leizhenyu.png");
    r["subtitle"] = "Subtitle " + str;
    r.set_title("Title " + str);
    r["summary"] = "Summary: " + str;
    r["fulldesc"] = "fulldesc: " + str;
    r["mascot"] = icons_[0].toStdString();
    r["emblem"] = icons_[1].toStdString();
    r["background"] = background_.toStdString();
    r["overlay-color"] = "#FF0000";

    r["comment_icon"] = icons_[3].toStdString();

    QString likes = QString("%1 %2").arg(qstr(u8"\u261d "), "100");
    QString views = QString("%1 %2").arg(qstr(u8"   \u261f "), "99");
    std::string both = qstr("%1 %2").arg(likes,views).toStdString();
    sc::VariantBuilder builder;
    builder.add_tuple({
        {"value", Variant(both)}
    });
    builder.add_tuple({
        {"value", Variant("")}
    });
    r["attributes"] = builder.end();

    r["musicSource"] = "http://qqmp3.djwma.com/mp3/魔音神据极品私货这锯子拉的耳膜都要碎了.mp3";
    r["videoSource"] = "http://techslides.com/demos/sample-videos/small.mp4";
    r["screenshot"] = icons_[2].toStdString();

    // add an array to show the gallary of it
    sc::VariantArray arr;

    for(const auto &datum : icons_) {
        arr.push_back(Variant(datum.toStdString()));
    }

    r["array"] = sc::Variant(arr);

    if (!reply->push(r))
        return;
}

在上面,我们加入了:

    r["comment_icon"] = icons_[3].toStdString();

这是我们需要显示Comment时最左边的图标.

preview.cpp

void Preview::run(sc::PreviewReplyProxy const& reply) {
    // Support three different column layouts

    ColumnLayout layout1col(1);
    std::vector<std::string> ids = { "image", "header", "summary", "tracks",
                                    "videos", "gallery_header", "gallerys", "reviews", "exp",
                                     "review_input", "rating_input", "inputId" };

    ColumnLayout layout2col(2);
    layout2col.add_column(ids);
    layout2col.add_column({});

    ColumnLayout layout3col(3);
    layout3col.add_column(ids);
    layout3col.add_column({});
    layout3col.add_column({});

    // Define the header section
    sc::PreviewWidget header("header", "header");
    // It has title and a subtitle properties
    header.add_attribute_mapping("title", "title");
    header.add_attribute_mapping("subtitle", "subtitle");

    // Define the image section
    sc::PreviewWidget image("image", "image");
    // It has a single source property, mapped to the result's art property
    image.add_attribute_mapping("source", "art");

    // Define the summary section
    sc::PreviewWidget description("summary", "text");
    // It has a text property, mapped to the result's description property
    description.add_attribute_mapping("text", "description");

    Result result = PreviewQueryBase::result();
    PreviewWidget listen("tracks", "audio");
    {
        VariantBuilder builder;
        builder.add_tuple({
            {"title", Variant("This is the song title")},
            {"source", Variant(result["musicSource"].get_string().c_str())}
        });
        listen.add_attribute_value("tracks", builder.end());
    }

    PreviewWidget video("videos", "video");
    video.add_attribute_value("source", Variant(result["videoSource"].get_string().c_str()));
    video.add_attribute_value("screenshot", Variant(result["screenshot"].get_string().c_str()));

    sc::PreviewWidget header_gal("gallery_header", "header");
    header_gal.add_attribute_value("title", Variant("Gallery files are:"));

    PreviewWidget gallery("gallerys", "gallery");
//    gallery.add_attribute_value("sources", Variant(result["array"]));
    gallery.add_attribute_mapping("sources", "array");

    PreviewWidgetList widgets({ image, header, description });

    if ( result["musicSource"].get_string().length() != 0 ) {
        widgets.emplace_back(listen);
    }

    if( result["videoSource"].get_string().length() != 0 ) {
        widgets.emplace_back(video);
    }

    if( result["array"].get_array().size() != 0 ) {
        widgets.emplace_back(header_gal);
        widgets.emplace_back(gallery);
    }

    // The following shows the review
    PreviewWidget review("reviews", "reviews");
    VariantBuilder builder;
    builder.add_tuple({
                          {"author", Variant("John Doe")},
                          {"review", Variant("very good")},
                          {"rating", Variant(3.5)}
                      });
    builder.add_tuple({
                          {"author", Variant("Mr. Smith")},
                          {"review", Variant("very poor")},
                          {"rating", Variant(5)}
                      });
    review.add_attribute_value("reviews", builder.end());
    widgets.emplace_back(review);

    // The following shows the expandable
    PreviewWidget expandable("exp", "expandable");
    expandable.add_attribute_value("title", Variant("This is an expandable widget"));
    expandable.add_attribute_value("collapsed-widgets", Variant(1));
    PreviewWidget w1("w1", "text");
    w1.add_attribute_value("title", Variant("Subwidget 1"));
    w1.add_attribute_value("text", Variant("A text"));
    PreviewWidget w2("w2", "text");
    w2.add_attribute_value("title", Variant("Subwidget 2"));
    w2.add_attribute_value("text", Variant("A text"));
    expandable.add_widget(w1);
    expandable.add_widget(w2);
    widgets.emplace_back(expandable);

    // The following shows a review rating-input
    PreviewWidget w_review("review_input", "rating-input");
    w_review.add_attribute_value("submit-label", Variant("Send"));
    w_review.add_attribute_value("visible", Variant("review"));
    w_review.add_attribute_value("required", Variant("review"));
    std::string reply_label = "Reply";
    std::string max_chars_label = "140 characters max";
    w_review.add_attribute_value("review-label", Variant(reply_label + ": " + max_chars_label));
    widgets.emplace_back(w_review);

    // The follwing shows a rating rating-input
    PreviewWidget w_rating("rating_input", "rating-input");
    w_rating.add_attribute_value("visible", Variant("rating"));
    w_rating.add_attribute_value("required", Variant("rating"));
    w_rating.add_attribute_value("rating-label", Variant("Please rate this"));
    widgets.emplace_back(w_rating);

    PreviewWidget w_commentInput("inputId", "comment-input");
    w_commentInput.add_attribute_value("submit-label", Variant("Post"));
    widgets.emplace_back(w_commentInput);

    // In the following, fake some comments data
    QList<Comment> comment_list;
    std::string comment_str = "Comment ";
    for(int i = 0; i < 3;  i++) {
        Comment comment;

        comment.id         = 1.0;
        comment.publishTime = "2015-3-18";
        comment.text = comment_str;
        comment.text += std::to_string(i+1);
        comment_list.append(comment);
    }

    int index = 0;
    Q_FOREACH(const auto & comment, comment_list) {
        std::string id = "commentId_" + std::to_string(index++);
        ids.emplace_back(id);

        PreviewWidget w_comment(id, "comment");
        w_comment.add_attribute_value("comment", Variant(comment.text));
        w_comment.add_attribute_value("author", Variant("Author"));
        w_comment.add_attribute_value("source", Variant(result["comment_icon"].get_string().c_str()));
        w_comment.add_attribute_value("subtitle", Variant(comment.publishTime));
        widgets.emplace_back(w_comment);
    }

    layout1col.add_column(ids);
    reply->register_layout({layout1col, layout2col, layout3col});
    reply->push( widgets );
}


在上面的函数中,我们加入了如下的代码:

    PreviewWidget w_commentInput("inputId", "comment-input");
    w_commentInput.add_attribute_value("submit-label", Variant("Post"));
    widgets.emplace_back(w_commentInput);

    // In the following, fake some comments data
    QList<Comment> comment_list;
    std::string comment_str = "Comment ";
    for(int i = 0; i < 3;  i++) {
        Comment comment;

        comment.id         = 1.0;
        comment.publishTime = "2015-3-18";
        comment.text = comment_str;
        comment.text += std::to_string(i+1);
        comment_list.append(comment);
    }

    int index = 0;
    Q_FOREACH(const auto & comment, comment_list) {
        std::string id = "commentId_" + std::to_string(index++);
        ids.emplace_back(id);

        PreviewWidget w_comment(id, "comment");
        w_comment.add_attribute_value("comment", Variant(comment.text));
        w_comment.add_attribute_value("author", Variant("Author"));
        w_comment.add_attribute_value("source", Variant(result["comment_icon"].get_string().c_str()));
        w_comment.add_attribute_value("subtitle", Variant(comment.publishTime));
        widgets.emplace_back(w_comment);
    }

    layout1col.add_column(ids);

在上面我们创建了一个叫做comment-input的PreviewWidget.同时,我们在显示每项comment时,使用了一个叫做comment的PreviewWidget.由于没有online数据,我们自己创建了一下fake的数据.运行我们的Scope,我们可以看到结果就像我们上面图显示的那样.

注意在上面的实现中,我们必须动态地添加每个创建的comment PreviewWidget,所以,有上面的语句:

layout1col.add_column(ids);

整个项目的源码在:https://github.com/liu-xiao-guo/scopetemplates_comment_input



作者:UbuntuTouch 发表于2016/3/29 15:10:08 原文链接
阅读:276 评论:0 查看评论

Read more
UbuntuTouch

在我们许多的项目中,我们需要用到一些资源.这些资源我们想分别放入到自己分别的目录中,比如images, videos, audios或assets.当我们打包我们的qmake应用时,我们需要把这些目录也打入到我们的中,并保证我们的应用所引用的文件保持相应的文件架构(相对文件路径).否则我们的应用不会真确运行.也有很多的web应用,我们希望把所有的文件一起打入到我们的应用中,这样我们可以可以使用WebView来访问.


我们还是拿我们先前的一个例子:

$ git clone https://github.com/liu-xiao-guo/audio

在那个例子中,我们的.pro文件如下:


TEMPLATE = aux
TARGET = audio

RESOURCES += audio.qrc

QML_FILES += $$files(*.qml,true) \
             $$files(*.js,true)

QML_SOUNDS += $$files(sounds/*.mp3,true) \
             $$files(sounds/*.wav,true)
CONF_FILES +=  audio.apparmor \
               audio.desktop \
               audio.png
OTHER_FILES += $${CONF_FILES} \
               $${QML_FILES}
#specify where the qml/js files are installed to
qml_files.path = /audio
qml_files.files += $${QML_FILES}
qm_sounds.path = /audio/sounds
qm_sounds.files += $${QML_SOUNDS}
#specify where the config files are installed to
config_files.path = /audio
config_files.files += $${CONF_FILES}
INSTALLS+=config_files qml_files qm_sounds

在这里我们使用了:

QML_SOUNDS += $$files(sounds/*.mp3,true) \
             $$files(sounds/*.wav,true)

qm_sounds.path = /audio/sounds
qm_sounds.files += $${QML_SOUNDS}


来把我们的文件打包到我们需要的文件目录中.整个的过程非常麻烦.

在今天的例程中,我们来直接把我们的.pro文件修改为:


TEMPLATE = aux
TARGET = audio

#SUBDIRS += sounds

RESOURCES += audio.qrc

QML_FILES += $$files(*.qml,true) \
             $$files(*.js,true) \
             sounds

#QML_SOUNDS += $$files(sounds/*.mp3,true) \
#             $$files(sounds/*.wav,true)

CONF_FILES +=  audio.apparmor \
               audio.desktop \
               audio.png

OTHER_FILES += $${CONF_FILES} \
               $${QML_FILES}

#specify where the qml/js files are installed to
qml_files.path = /audio
qml_files.files += $${QML_FILES}

#qm_sounds.path = /audio/sounds
#qm_sounds.files += $${QML_SOUNDS}

#specify where the config files are installed to
config_files.path = /audio
config_files.files += $${CONF_FILES}

INSTALLS+=config_files 

phablet@ubuntu-phablet:/opt/click.ubuntu.com/audio.liu-xiao-guo/current/audio$ ls
total 64
drwxrwxr-x 3 clickpkg clickpkg  4096 Mar 31 15:31 .
drwxr-xr-x 4 clickpkg clickpkg  4096 Mar 31 15:31 ..
-rw-r--r-- 1 clickpkg clickpkg   114 Mar 31 15:31 audio.apparmor
-rw-r--r-- 1 clickpkg clickpkg   167 Mar 31 15:31 audio.desktop
-rw-r--r-- 1 clickpkg clickpkg 38968 May  7  2015 audio.png
-rw-r--r-- 1 clickpkg clickpkg  1291 Aug 23  2015 Main.qml
drwxrwxr-x 2 clickpkg clickpkg  4096 Mar 31 15:31 sounds

在这里,我们只在上面的QML_FILES中加入"sounds"即可.这样整个"sounds"将会整个打包入我们的应用中.

整个项目的源码:https://github.com/liu-xiao-guo/audio_path

如果大家感兴趣的话,大家可以把一个西班牙开发的项目uNav变为一个Qt Creator的项目.我已经帮他做了:




呵呵,这是我家的位置哦!


作者:UbuntuTouch 发表于2016/3/31 15:34:33 原文链接
阅读:257 评论:0 查看评论

Read more
UbuntuTouch

[原]如何快速地安装Ubuntu SDK

我在先前的文章"Ubuntu SDK 安装"中已经详细地介绍了如何安装Ubuntu SDK.但是很多的开发者可能在最后安装SDK所需要的chroots时候会失败.这里面的原因是SDK在安装chroots时,它不支持断点续传.也就是说在安装chroots时,由于网路的原因或某种原因,造成chroots的安装失败时,我们需要再次重新安装它.一般我们需要删除现有的已经安装失败的chroots.每个chroot(armhf及i386)在安装后的大小约为1.6G.这篇文章详细介绍如何快速地安装我们的Ubuntu SDK.


1)安装Ubuntu SDK


首先,我们可以安装好我们的Ubuntu桌面系统.我们一般推荐安装最新的Ubuntu桌面系统,比如目前即将面世的16.04 LTS版本.如果已经有一个Ubuntu桌面系统,我们可以它通过如下的命令来升级我们的桌面系统到最新的系统:

$ update-manager -d

依照现有的文章"Ubuntu SDK 安装"中介绍的那样,安装最新的Ubuntu SDK.

$sudo apt-get update
$sudo apt-get upgrade
$sudo apt-get dist-upgrade
$sudo add-apt-repository ppa:ubuntu-sdk-team/ppa
$sudo apt-get install ubuntu-sdk
$sudo udo apt-get install ubuntu-sdk-dev ubuntu-sdk-ide

在上面的先开始的命令中,我们先把我们的Ubuntu桌面系统更新到最新的状态,这样使得我们的最新的SDK依赖的包都被安装到系统中以使得后面的SDK的安装能够顺利进行.否侧在我们的实际安装中,如果有的包不在系统中或是最新的,那么后面SDK的安装可能失败.

在这个步骤中,由于我们使用了ppa,所有它可以支持断点续传.如果失败了,它可以在下次安装时再次从上次中断的地方继续下载安装.一般来说我们并不担心这一步的失败.对于有些开发者来说,可以尝试使用VPN的方法来提高安装的速度.成功安装后,我们可以在dash中找到我们所需要的Ubuntu SDK:



对于网路情况不是很好的开发者来说,请直接跳到下面的第三节下载已经成功安装过的chroots来安装而不需要下面的第二步.


2)安装chroots


在这个步骤中,由于它不支持断点续传,所有它是最容易导致安装失败的环节.如果我们的网路情况好的话,我们可以直接在我们的命令行中打入下面的命令来安装我们的chroots.在这个步骤中,我们可以来安装我们所需要版本的chroots.我们可以通过如下的方式找到我们手机所有支持的framework:
liuxg@liuxg:~$ adb shell
phablet@ubuntu-phablet:~$ click framework list



一般来说在我们开发时,会选择我们所需要的framework(相当于iOS的版本).如果我们所定义的framework在手机中不存在,那么我的应用在手机中将不能被运行.目前我们建议使用15.04的chroots.

- armhf chroot的安装


我们可以通过如下的命令来安装我们的armhf chroot:
$sudo click chroot -aarmhf -f ubuntu-sdk-15.04 create  
如果安装失败,我们必须使用如下的命令删除已经安装的半成品,然后再用上面的命令来安装我们的chroot.
$sudo click chroot -a armhf -f ubuntu-sdk-15.04  destroy   

- i386 chroot的安装


我们可以通过如下的命令来安装我们的armhf chroot:
$sudo click chroot -ai386 -f ubuntu-sdk-15.04 create   
如果安装失败,我们必须使用如下的命令删除已经安装的半成品,然后再用上面的命令来安装我们的chroot.
$sudo click chroot -a i386 -f ubuntu-sdk-15.04  destroy  

3)直接下载已有的chroots进行安装


在实际的安装中,我发现有很多的开发者在进行上面的安装时由于网路的原因而导致上面的安装不能成功.基于这个原因,我们把我已经成功安装过的chroots上传到我们的百度网盘供大家下载.等下载完后,我们再把它们解压到我们所需要的路径中.这样的安装好处是,我们可以使用各种方法进行断点下载我们打包过的chroots,并成功拷贝到相应的目录中.这个方法的缺点是:chroots可能不是最新的.开发者在以后的SDK中可以进行自动更新或手动更新我们的chroots.不过一般来说,这个chroots已经够用即使在不更新的情况下.

删除任何已经安装或安装不成功的chroots


我们可以通过上一节中介绍的方法来删除曾经没有安装成功的chroots以保证我们有干净的安装环境:
$sudo click chroot -a armhf -f ubuntu-sdk-15.04  destroy 
$sudo click chroot -a i386 -f ubuntu-sdk-15.04  destroy  
通过上面的方法,我们确保在我们的桌面系统中没有任何我们曾经安装失败后残存的chroots文件.

下载我们上传的chroots


开发者可以到我们的网址下载我们已经上传的chroots.这个chroots是到上传时间最新的chroot.对于绝大多数的应用开发应该是没有任何问题的.当然开发者可以在以后的SDK中进行更新.



就如同我们上面显示的那样.在我们上次的chroots中,有两部分文件.

- chroot.d:


 这个是需要安装到/etc/schroot/chroot.d目录中的文件.安装后的文件架构是:
liuxg@liuxg:/etc/schroot/chroot.d$ tree
.
├── click-ubuntu-sdk-15.04-armhf
└── click-ubuntu-sdk-15.04-i386
在实际的拷贝中,我们需要使用sudo来拷贝的方法这两个文件.这里的liuxg是我自己的电脑liuxg上用户名.在你们自己安装时,这个名字应该是你们自己的用户名.打开这个两个文件,同样我们需要使用sudo来编辑这两个文件,比如click-ubuntu-sdk-15.04-armhf:
[click-ubuntu-sdk-15.04-armhf]
description=Build chroot for click packages on armhf
users=root,liuxg
root-users=root,liuxg
source-root-users=root,liuxg
type=directory
profile=default
setup.fstab=click/fstab
# Not protocols or services see
# debian bug 557730
setup.nssdatabases=sbuild/nssdatabases
union-type=overlayfs
directory=/var/lib/schroot/chroots/click-ubuntu-sdk-15.04-armhf

我们可以使用vi或gedit来编辑上面的文件.替换上面的"liuxg"为自己电脑上的用户名.然后存下来.我们使用同样的方法来对click-ubuntu-sdk-15.04-i386进行同样的操作.

- chroots.tar.gz


我们把这个文件拷贝到/var/lib/schroot/目录,然后我们通过如下的方法来解压缩:
liuxg@liuxg:/var/lib/schroot/chroots$ sudo tar -xvf chroots.tar.gz
解药缩后的文件架构为:
liuxg@liuxg:/var/lib/schroot/chroots$ tree -L 1
.
├── click-ubuntu-sdk-15.04-armhf
└── click-ubuntu-sdk-15.04-i386

同样上面的"liuxg"是我自己的用户名.在实际应用中,它应该是你自己电脑上的用户名.

- 检查我们已经安装好的chroots


我们可以通过如下的方法来检查我们的chroots的安装是否已经成功:
liuxg@liuxg:~$ schroot -l
chroot:click-ubuntu-sdk-15.04-armhf
chroot:click-ubuntu-sdk-15.04-i386
source:click-ubuntu-sdk-15.04-armhf
source:click-ubuntu-sdk-15.04-i386

如果我们已经看到上面的显示,表明我们的安装已经是成功的.

- 打开我们的Ubuntu SDK来检查我们的chroots


在SDK中,我们怎么来检查我们已经安装过的chroots是否已经成功呢?我们首先打开我们的SDK,然后点击菜单:
Tools ==> Options ==> Ubuntu ==>Click


如果我们已经看到我们的chroot已经在上面所示的图片中,表明我们的chroot是已经安装成功的.我们可以点击上面的Update来更新我们的chroots到最新状态.当然我们也可以点击Maintain来添加或删除我们所需要的模块或库.


4)最后一招


如果上面的所有方法都已经试过了,还是不可以的话,可以在地址下载debian文件进行安装.需要安装的文件在页面的最下面.目前使用于16.04 LTS的desktop系统.


在安装我们上面的文件时,我们必须按照上面的方法先删除我们先前安装过的文件。我们可以使用如下的命令:

$sudo click chroot -a armhf -f ubuntu-sdk-15.04  destroy 
$sudo click chroot -a i386 -f ubuntu-sdk-15.04  destroy  

- 检验我们已经安装的Ubuntu SDK


我们可以通过文章"创建第一个Ubuntu for phone应用"来检查我们的SDK的安装是否成功.如果一切顺利,我们可以把我们的应用部署到我们的设备或模拟器中.具体安装模拟器的方法,可以参阅文章"Ubuntu SDK 安装".这里将不再重复.
作者:UbuntuTouch 发表于2016/4/10 8:52:46 原文链接
阅读:1905 评论:1 查看评论

Read more
UbuntuTouch

最近我看了一篇关于在Scope中link query的文章.坦率地说,不是特别理解,如果没有具体的例程也不知道它的作用.简单地说,就是在我们的Scope设计中,我们可以点击一个Object,他可以让我们发起另外一个不同的query.

首先,我们可以下载我们已经设计好的例程:

$ git clone https://github.com/liu-xiao-guo/scopetemplates_release

在query.cpp中,

void Query::run(sc::SearchReplyProxy const& reply) {
    try {
        // The default is vertical
        pushResult(reply, CAT_RENDERER1, 1);
        pushResult(reply, CAT_RENDERER101, 101);
        pushResult(reply, CAT_RENDERER2, 2);
        pushResult(reply, CAT_RENDERER3, 3);
        pushResult(reply, CAT_RENDERER4, 4);
        pushResult(reply, CAT_RENDERER5, 5);
        pushResult(reply, CAT_RENDERER6, 6);
        pushResult(reply, CAT_RENDERER7, 7);
        pushResult(reply, CAT_RENDERER10, 10);
        pushResult(reply, CAT_RENDERER11, 11);

        int count = images_.count();

        auto cat = reply->register_category( "Grid", "Grid" ,
                                             "", sc::CategoryRenderer(CAT_RENDERER8) );

        for ( int i = 0; i < count/2; i ++ ) {
            pushResult( reply, &cat, i);
        }

        cat = reply->register_category( "Carousel", "Carousel" ,
                                         "", sc::CategoryRenderer(CAT_RENDERER9) );

        for ( int i = 0; i < count; i ++ ) {
            pushResult( reply, &cat, i);
        }

        cat = reply->register_category( "vertical-journal", "vertical-journal" ,
                                         "", sc::CategoryRenderer(CAT_RENDERER12) );

        for ( int i = 0; i < count; i ++ ) {
            pushResult( reply, &cat, i);
        }

        cat = reply->register_category( "horizontal-list", "horizontal-list" ,
                                         "", sc::CategoryRenderer(CAT_RENDERER13) );

        for ( int i = 0; i < count; i ++ ) {
            pushResult( reply, &cat, i);
        }

        cat = reply->register_category( "Linking queries", "Linking queries" ,
                                         "", sc::CategoryRenderer(CAT_RENDERER1) );

        // The link icon's index is 8
        pushResult( reply, &cat, 8, true);

        pushResult(reply, CR_CAROUSEL, 12);


    } catch (domain_error &e) {
        // Handle exceptions being thrown by the client API
        cerr << e.what() << endl;
        reply->error(current_exception());
    }
}

我们加入了如下的语句:

        cat = reply->register_category( "Linking queries", "Linking queries" ,
                                         "", sc::CategoryRenderer(CAT_RENDERER1) );

        // The link icon's index is 8
        pushResult( reply, &cat, 8, true);

在下面的函数中:

void Query::pushResult(sc::SearchReplyProxy const& reply,
                       const std::shared_ptr<const Category> *cat, int i,
                       bool linkquery) {

    stringstream ss;
    ss << i;
    string str = ss.str();

    sc::CategorisedResult r(*cat);

    // If linkquery is true, we need to redirect it to another scope or
    // a scope department id locally in this scope
    if ( linkquery ) {
        sc::CannedQuery second_query("com.canonical.scopes.news_unity-scope-news");
        second_query.set_department_id("dept-finance");
        r.set_uri(second_query.to_uri());
    } else {
        r.set_uri( URI.toStdString() );
    }
   ...
}

当linkquery为真时,我们把它重新定向为发起另外一个叫做com.canonical.scopes.news_unity-scope-news Scope中的dept-finance department.

运行我们的Scope:

 


当我们点击link queries下面的图片时,在news中的dept-finance将被自动query并显示结果.





作者:UbuntuTouch 发表于2016/4/12 16:02:36 原文链接
阅读:160 评论:0 查看评论

Read more
UbuntuTouch

在我们的许多的应用中,我们希望键盘的布局只允许数字或字母的输入.对密码的输入,我们希望通过隐藏输入以免别人看见.那么怎么在Ubuntu的手机设计中实现这个功能呢?我们可以使用TextArea中的inputMethodHints来实现这个功能.


下面我们还是通过一个短小的例程来展示:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.2

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

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

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

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

        Connections {
            target: Qt.inputMethod
            onVisibleChanged: console.log("OSK visible: " + visible)
        }

        property var model: [
            "Qt.ImhHiddenText",
            "Qt.ImhSensitiveData",
            "Qt.ImhNoAutoUppercase",
            "Qt.ImhPreferNumbers",
            "Qt.ImhPreferUppercase",
            "Qt.ImhPreferLowercase",
            "Qt.ImhNoPredictiveText",
            "Qt.ImhDate",
            "Qt.ImhTime",
            "Qt.ImhDigitsOnly",
            "Qt.ImhFormattedNumbersOnly",
            "Qt.ImhUppercaseOnly",
            "Qt.ImhLowercaseOnly",
            "Qt.ImhDialableCharactersOnly",
            "Qt.ImhEmailCharactersOnly",
            "Qt.ImhUrlCharactersOnly"
        ]

        property var inputMethods: [
            Qt.ImhHiddenText,
            Qt.ImhSensitiveData,
            Qt.ImhNoAutoUppercase,
            Qt.ImhPreferNumbers,
            Qt.ImhPreferUppercase,
            Qt.ImhPreferLowercase,
            Qt.ImhNoPredictiveText,
            Qt.ImhDate,
            Qt.ImhTime,
            Qt.ImhDigitsOnly,
            Qt.ImhFormattedNumbersOnly,
            Qt.ImhUppercaseOnly,
            Qt.ImhLowercaseOnly,
            Qt.ImhDialableCharactersOnly,
            Qt.ImhEmailCharactersOnly,
            Qt.ImhUrlCharactersOnly
        ]

        Flickable {
            anchors.fill: parent
            contentHeight: layout.childrenRect.height + units.gu(2)

            Column {
                id: layout
                width: parent.width

                OptionSelector {
                    id: option
                    text: page.model[selectedIndex]
                    model: page.model

                    // update the text
                    onSelectedIndexChanged: {
                        console.log("selectedIndex: " + selectedIndex )
                        text = page.model[selectedIndex]
                        textField.inputMethodHints = page.inputMethods[selectedIndex]
                    }
                }

                TextField {
                    id: textField
                    width:page.width*.7
                    placeholderText: "Please input something"
                }

                TextField {
                    id: textField1
                    width:page.width*.7
                    placeholderText: "Please input a password"
                    echoMode: TextInput.Password
                }
            }
        }
    }
}

运行上面的应用:

  

  


从上面的例程中可以看出来,我们可以通过选择不同的inputMethodHints值来实现不同的键盘布局.

作者:UbuntuTouch 发表于2016/4/14 9:46:52 原文链接
阅读:152 评论:0 查看评论

Read more
liam zheng

我们与我们的物联网业务主管 Maarten 进行了一次有趣的聊天。大家畅谈了投影仪与支持应用的软件无线电 (limeSDR.org) 相遇会发生什么。下面就是它们的结合带来的一些很酷的玩法,包括通过手势与无人机交流、进入无线电对讲机系统以及开辟实际上并不存在的覆盖区域。

我们与我们的物联网业务主管 Maarten 进行了一次有趣的聊天。大家畅谈了投影仪与支持应用的软件无线电 (limeSDR.org) 相遇会发生什么。下面就是它们的结合带来的一些很酷的玩法,包括通过手势与无人机交流、进入无线电对讲机系统以及开辟实际上并不存在的覆盖区域。

  • 使用软件无线电 (SDR) 接收电视广播,并使用投影仪作为电视屏幕

  • 添加 WiFi 应用但不必多交一部设备的电费

  • 添加 4G 应用,通过 REST API 获取电信提供商的频谱,并为建筑物内没有信号的地方提供信号覆盖

  • 探测进入房间的人是否随身携带了手机并开启投影仪,在周围没有人时关闭投影仪。如果照明、供暖等设备可通过 API 来控制,那么这些设备也包括在内

  • 创建你自己的物联网无线协议,从而可以连接房子周围的感应器,并将带有实时数据的仪表板投影到屏幕上

  • 使用支持蓝牙的手势感应器探测手的动作,通过手势与无人机或玩具车交流并控制它们,将无人机或玩具车看到的东西投影到屏幕上观看

  • 使用 3 台投影仪,根据每个人的蓝牙或手机信号以三角法定位每个人在办公室内的行动位置。连接他们的在线日历,按照他们的日程安排自动打开 Webex 或 Google Hangout。连接室内的无线监控摄像头,将监控画面传送给其他会议邀请。使用内置麦克风收听会议所讲的全部内容

  • 通过港口的 AIS 信号传输设备跟踪船舶航行或通过 Mode S 跟踪飞机飞行,并通过投影仪显示在 Google Map 上绘制的船舶和飞机航线

  • 通过直接式胎压监测系统 (TPMS) 将车辆状态的实时数据投影到屏幕上

  • 与诸如无线电对讲机的各类 2.4GHz 设备通话,以及通过投影仪连接婴儿监视器来显示婴儿当时的活动

  • 构建你自己的低功率 WAN 接收器解决方案(例如 LoRA),并在屏幕上显示接收到的内容

 

Read more
Grazina Borosko

April marks the release of Xerus 16.4 and with it we bring a new design of our iconic wallpaper. This post will take you through our design process and how we have integrated our Suru visual language.

Evolution

The foundation of our recent designs are based on our Suru visual language, which encompasses our core brand values, bringing consistency across the Ubuntu brand.

Our Suru language is influenced by the minimalist nature of Japanese culture. We have taken elements of their Zen culture that give us a precise yet simplistic rhythm and used it in our designs. Working with paper metaphors we have drawn inspiration from the art of origami that provides us with a solid and tangible foundation to work from. Paper is also transferable, meaning it can be used in all areas of our brand in two and three dimensional forms.

Design process

We started by looking at previously released wallpapers across Ubuntu to see how each has evolved from each other. After seeing the previous designs we started to apply our new Suru patterns, which inspired us to move in a new direction.

Ubuntu 14.10 ‘Utopic Unicorn’’

wallpaper_unicorn

Ubuntu 15.04 ‘Vivid Vervet’

suru-desktop-wallpaper-ubuntu-vivid (1)

Ubuntu 15.10 ‘Wily Werewolf’

ubuntu-1510-wily-werewolf-wallpaper

Step-by-step process

Step 1. Origami animal

Since every new Ubuntu release is named after animal, the Design Team wanted to bring this idea closer to the wallpaper and the Suru language. The folds are part of the origami animal and become the base of which we start our design process.

Origarmi

To make your own origami Xerus squirrel, you can find the instructions here.

Step.2 Searching for the shape

We started to look at different patterns by using various techniques with origami paper. We zoomed into particular folds of the paper, experimented with different light sources, photography, and used various effects to enhance the design.

The idea was to bring actual origami to the wallpaper as much as possible. We had to think about composition that would work across all screen sizes, especially desktop. As the wallpaper is a prominent feature in a desktop environment, we wanted to make sure that it was user friendly, allowing users to find documents and folders located on the computer screen easily. The main priority was to not let the design get in the way of everyday usage, but enhance it aesthetically and provide a great user experience.

After all the experiments with fold patterns and light sources, we started to look at colour. We wanted to integrate both the Ubuntu orange and Canonical aubergine to balance the brightness and played with gradient levels.

We balanced the contrast of the wallpaper color palette by using a long and subtle gradient that kept the bright and feel look. This made the wallpaper became brighter and more colorful.

Step.3 Final product

The result was successful. The new concept and usage of Suru language helped to create a brighter wallpaper that fitted into our overall visual aesthetic. We created a three-dimensional look and feel that gives the feeling of actual origami appearance. The wallpaper is still recognizable as Ubuntu, but at the same time looks fresh and different.

Ubuntu 16.04 Xenial Xerus

Xerus - purple

Ubuntu 16.04 Xenial Xerus ( light version)

Xerus - Grey

What is next?

The Design Team is now looking at ways to bring the Suru language into animation and fold usage. The idea is to bring an overall seamless and consistent experience to the user, whilst reflecting our tone of voice and visual identity.

Read more
liam zheng

大会来了!三星年度大型开发者峰会即将于4月27日至28日在旧金山揭幕,旧金山是一个极具吸引力的地方,很多开发者、创新者聚集在这里讨论最新的技术和未来创新。

大会来了!三星年度大型开发者峰会即将于4月27日至28日在旧金山揭幕,旧金山是一个极具吸引力的地方,很多开发者、创新者聚集在这里讨论最新的技术和未来创新。

今年,物联网(IoT)将是一个巨大的焦点,特别是智能家庭和针对物联网设计的芯片——ARTIK,届时我们的同事Didier也会出现在大会现场做演示,也会发表关于ARTIK的演讲。

我们的演示将是一个可以让你方便在家庭网关(Home gateway)上部署其他应用的app。让你直观感受智能手机和家庭机器人之间是如何交流、通信从而为你服务的。当你到家时,机器人将用摄像头来识别是你回来了,同时麦克风也可以提供语音交互。

除此,你还可以部署其他app在家庭网关上,包括一个接入点(家庭WiFi),视频服务,本地的服务器(比如Skype和Google Hangout的数据都存储在本地),自动化家用设备等。

并且,我们的往期博客也有关于本次活动的内容,比如这里

Read more
liam zheng

空中移动基站来到英国

英国最大的移动运营商 EE(现已归属 BT 旗下)近日宣布,将与新一轮开源移动网络技术大潮领导者之一的 Lime Micro 以及 Canonical (Ubuntu) 合作,共同确保英国实现更好的移动网络覆盖。

英国最大的移动运营商 EE(现已归属 BT 旗下)近日宣布,将与新一轮开源移动网络技术大潮领导者之一的 Lime Micro 以及 Canonical (Ubuntu) 合作,共同确保英国实现更好的移动网络覆盖。

EE 正在大力投资网络建设,争取到 2020 年实现 95% 的 4G 网络地理覆盖率。但是,并不是所有地区都可以或者都适合建造新的大型信号塔。他们想充分利用现有基础设施,例如灯塔、高层建筑、山岭等等。另外的一大难题是,依靠现有手段覆盖偏远地区在经济上或技术上是不可行的。正因为如此,EE 才选择与技术创新公司合作开发成本更低、体积更小、弹性更高、效果更好的解决方案。

Lime Micro 即将以众筹形式推出首款支持应用的开源软件无线电,即 LimeSDR。通过 4G 应用,LimeSDR 将构成一个功能完善的基站的基础。将这个基站安装到热气球或无人机上,可以覆盖通过其他方式难以到达的地区。另外,将这些基站嵌入自动售货机、银行设施、智能灯杆、数字标牌等其他用途的设备内部,还将降低部署连接的成本。

EE 希望偏远社区也能加入网络建设当中。这些社区可以提出他们需要的功能,甚至可以参与网络的维护。这可以让社区与运营商以全新的方式合作。并且有了当地人的支持,甚至可以减少受过培训的技术人员长途跋涉解决问题的需要。例如,如果一个基站只是需要重新启动,那么派遣一位工程师从爱丁堡跋涉 300 英里前往诸岛将是一件很不经济(或者不切实际)的事情。

EE 将在英国多所大学举办挑战赛,鼓励大家集思广益,就如何连接未联网地区和降低运营成本提出更多创新和开源的想法。任何有好创意的人都可以参加。Ubuntu Core 具有开源、支持应用和随时可用于生产的特点。你只要购买一个 LimeSDR,下载 Ubuntu Core,就能向世界展示你的应用或设备可以怎样降低网络运营成本。帮助未联网地区实现网络覆盖将促进经济发展,因此你的努力将会造福我们的社会。我们非常期待看到你如何将无线网络的未来变得更加美好.

LimeSDR Crowd Supply 宣传视频(优酷)

Read more