Canonical Voices

What Ubuntu Touch Development in CSDN (Chinese) talks about

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 原文链接
阅读:421 评论: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 原文链接
阅读:226 评论: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 原文链接
阅读:247 评论: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 原文链接
阅读:223 评论: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 原文链接
阅读:223 评论: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 原文链接
阅读:212 评论:0 查看评论

Read more
UbuntuTouch

有兴趣的朋友可以阅读我们全球开发者网站的文章"On Screen Keyboard tricks".里面讲了许多的东西.在今天的文章中,我们直接来一个活生生的例程来阐述这个问题.


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

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

    // anchorToKeyboard: true

    Page {
        title: i18n.tr("onscreenkeyboard")
        anchors.fill: parent

        Flickable {
            id: sampleFlickable

            clip: true
            contentHeight: mainColumn.height + units.gu(5)
            anchors {
                top: parent.top
                left: parent.left
                right: parent.right
                bottom: createButton.top
                bottomMargin: units.gu(2)
            }

            Column {
                id: mainColumn

                spacing: units.gu(2)

                anchors {
                    top: parent.top
                    left: parent.left
                    right: parent.right
                    margins: units.gu(2)
                }

                TextField {
                    id: username
                    width: parent.width
                    placeholderText: "username"
                }

                TextField {
                    id: password
                    width: parent.width
                    placeholderText: "password"
                }

                TextField {
                    id: email
                    width: parent.width
                    placeholderText: "email"
                }
            }
        }

        Button {
            id: createButton
            text: "Create Account"
            anchors {
                horizontalCenter: parent.horizontalCenter
                bottom: parent.bottom
                margins: units.gu(2)
            }
            onClicked: {
                console.log("it is clicked")
            }
        }
    }
}

在上面的例程中,我们有意识地把:

    anchorToKeyboard: true

注释掉,我们可以运行我们的应用:


从上面的截图中可以看出来,当我们点击最上面的任何一个输入框时,我们最下面的"Createt Account"按钮就被键盘所在的位置遮住了.那么我们怎么才能可以见到我们的按钮呢?答案就是在我们的MainView中,把如下的开关打开:

    anchorToKeyboard: true

再次重新运行我们的应用:



从上面我们可以看出来.当我们点击email进行输入的时候,"Create Account"按钮也自动跑到我们键盘的上面.这样当我们输入完我们的内容的时候,我们可以直接按下按钮来进行创建一个账号,而不用先把键盘弄消失掉,再提交.

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

我们来用另外一个例程来展示OSK的用法:

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

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

  //  anchorToKeyboard: true

    Page {
        header: PageHeader {
            id: pageHeader
            title: i18n.tr("osk1")
            StyleHints {
                foregroundColor: UbuntuColors.orange
                backgroundColor: UbuntuColors.porcelain
                dividerColor: UbuntuColors.slate
            }
        }

        TextField {
            anchors.bottom: parent.bottom
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.bottomMargin: units.gu(2)
            placeholderText: "please input something"
        }
    }
}

在上面的例程中,当我们注释掉:

    anchorToKeyboard: true

运行我们的应用,并在最下面的输入框中点击进行输入:

  

从上面可以看出来,当我们进行输入的时候,我们的输入框被键盘挡住了.我们看不见我们的输入框.而当我们把下面的属性设为真时:

  anchorToKeyboard: true

在重新运行我们的应用:

  



从上面我们可以看出来,当我们输入的时候我们可以看见我们的输入框,并可以输入为我们所需要的内容.

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



作者:UbuntuTouch 发表于2016/4/18 13:54:51 原文链接
阅读:162 评论: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 原文链接
阅读:205 评论:0 查看评论

Read more
UbuntuTouch

细心开发者如果发现我的以前开发的应用,主页面能够切换的地方,我放置了一个向右的箭头.比如在文章"从零开始创建一个Ubuntu应用 -- 一个小小的RSS阅读器 (1)"最下面的图片中,我们可以看到在列表中,有一个向右的箭头.



在我们的Ubuntu应用中,我们可以利用ListItemLayout来作为我们列表中的每一项的delegate.在这种情况下,我们可以充分利用ProgressionSlot来为我们添加上一个向右的">"符号.比如在下面的例子中:


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

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

    Page {
        header: PageHeader {
            id: pageHeader
            title: i18n.tr("progressionslot")
            StyleHints {
                foregroundColor: UbuntuColors.orange
                backgroundColor: UbuntuColors.porcelain
                dividerColor: UbuntuColors.slate
            }
        }

        ListItem {
            anchors {
                left: parent.leftt
                right: parent.right
                top: parent.header.bottom
            }

            height: layout.height + (divider.visible ? divider.height : 0)
            ListItemLayout {
                id: layout
                title.text: "Hello developers!"
                subtitle.text: "I'm a subtitle, I'm tiny!"
                summary.text: "Ubuntu!"
                CheckBox { SlotsLayout.position: SlotsLayout.Leading }
                Icon {
                    name: "message"
                    SlotsLayout.position: SlotsLayout.Trailing;
                    width: units.gu(2)
                }

                ProgressionSlot {}
            }
        }
    }
}

我们利用了一个ProgressionSlot.这样我们的ListItem显示为:



我们可以在上面的显示中,清楚地看见一个向右的箭头.这就是使用ProgressionSlot所带来的.


作者:UbuntuTouch 发表于2016/4/29 11:04:55 原文链接
阅读:113 评论: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系统.


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


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

Read more
UbuntuTouch

[原]利用SVG在QML中画图

在我先前的文章"在QML应用中使用Canvas来画图"中,我展示如何利用QML中的Canvas来画我们的曲线.今天在我们的文章中,我们来展示如何利用SVG在我们的例程中画我们所需要的曲线.


我们可以直接使用一个已经存在的SVG画画库simple-svg.它的源码位于"https://code.google.com/archive/p/simple-svg/downloads".对于一下开发者来说可能需要VPN才可以下载哦.


我们可以利用我们的SDK中提供的一个标准的"QML App with C++ plugin (qmake)"来创建一个简单的模版.同时,我们修改我们的plugin的代码如下:

mytype.h


#ifndef MYTYPE_H
#define MYTYPE_H

#include <QObject>

class MyType : public QObject
{
    Q_OBJECT
    Q_PROPERTY( QString helloWorld READ helloWorld WRITE setHelloWorld NOTIFY helloWorldChanged )

public:
    explicit MyType(QObject *parent = 0);
    ~MyType();
    Q_INVOKABLE void draw(const int &width, const int &height, const QString &array);

Q_SIGNALS:
    void helloWorldChanged();

protected:
    QString helloWorld() { return m_message; }
    void setHelloWorld(QString msg) { m_message = msg; Q_EMIT helloWorldChanged(); }

    QString m_message;
};

#endif // MYTYPE_H

在上面我们添加了一个叫做"draw"的方法.它的定义如下:

mytype.cpp


#include "mytype.h"
#include "simple_svg.h"

using namespace svg;

MyType::MyType(QObject *parent) :
    QObject(parent),
    m_message("")
{

}

MyType::~MyType() {

}

void MyType::draw(const int &width, const int &height, const QString &array)
{
    // Create the SVG doc
    Dimensions dimensions(width, height);
    Document doc("../svggraph/svggraph/graph.svg", Layout(dimensions, Layout::BottomLeft));

    // Parse our string into an array
    std::istringstream buf(array.toStdString());
    std::istream_iterator<std::string> beg(buf), end;
    std::vector<std::string> tokens(beg, end);

    // Create a line
    Polyline polyline_a(Stroke(1.5, Color::Cyan));

    // Iterate over our array to define line start/end points
    for( int a = 0; a < tokens.size(); a = a + 1 )
    {
        if (tokens.size() < 2) {
            polyline_a << Point(width/(tokens.size())*(a), atoi(tokens[a].c_str())) << Point(width/(tokens.size())*(a+1), atoi(tokens[a].c_str()));
        } else {
            polyline_a << Point(width/(tokens.size()-1)*(a), atoi(tokens[a].c_str()));
        }
    }
    doc << polyline_a;

    // Save the doc
    doc.save();
}

在这里,我们把从地址https://code.google.com/archive/p/simple-svg/downloads下载的文件重新命令为"simple_svg.h",并把它存于位于plugins所在目录的根目录中,也即:

svggraph/backend/Svggraph

我们可以重新编译我们的应用.

在我们的Main.qml中,我们可以利用我们刚刚创建的plugin来画我们的UI.和先前不同的是,这里,我们使用一个叫做Image的而不是一个Canvas.

Main.qml

import QtQuick 2.4
import Ubuntu.Components 1.3
import Svggraph 1.0
/*!
    \brief MainView with a Label and Button elements.
*/

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

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

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

    MyType {
        id: myType
    }

    Page {
        id: page
        header: PageHeader {
            id: pageHeader
            title: i18n.tr("svggraph")
            StyleHints {
                foregroundColor: UbuntuColors.orange
                backgroundColor: UbuntuColors.porcelain
                dividerColor: UbuntuColors.slate
            }
        }

        Rectangle {
            anchors {
                top:page.header.bottom
                left: page.header.left
                right: page.header.right
            }
            height: parent.height - page.header.height

            TextField{
                id:txt
                width:parent.width - units.gu(4)
                anchors.top:parent.top
                anchors.horizontalCenter:parent.horizontalCenter
                anchors.margins:units.gu(2)
                text: "0 10 50 80 60 90"
            }
            Row {
                anchors.top:txt.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.margins: units.gu(2)
                spacing: units.gu(2)
                Button {
                    id:drawButton
                    anchors.margins:units.gu(2)
                    text:i18n.tr("Draw")
                    enabled:(txt.length)
                    color: UbuntuColors.orange
                    onClicked: {
                        myType.draw(page.width, page.height, txt.text)
                        img.source = ""
                        img.source = Qt.resolvedUrl("graph.svg")
                    }
                }
                Button {
                    id:clearButton
                    anchors.margins:units.gu(2)
                    text:i18n.tr("Clear")
                    onClicked: {
                        myType.draw(page.width, page.height, "")
                        img.source = ""
                        img.source = Qt.resolvedUrl("graph.svg")
                    }
                }
            }
            Image {
                id:img
                anchors.fill:parent
                anchors.margins:units.gu(2)
                cache:false
                source: Qt.resolvedUrl("graph.svg")
            }
        }
    }
}

在这里,我们通过按钮"Draw"把我们所需要的数据传入到plugin中,并生产graph.svg文件,并在UI的Image中得以显示:






整个项目的源码在:https://github.com/liu-xiao-guo/svggraph
作者:UbuntuTouch 发表于2016/4/25 10:36:03 原文链接
阅读:88 评论:0 查看评论

Read more
UbuntuTouch

我们知道在许多的项目中,当切换到一个页面时我们需要对一些资源进行监控.每当发送事件改变时,我们需要进行更新我们的页面.当然每当我们离开我们的页面时,我们就不想做这样的动作.我们可以取消或释放我们所需要的资源以节省资源.当我们需要这么做的时候,我们希望得到页面切换时的事件这样我们可以在合适的时机做出合适的动作.Qt中的Component.incubateObject()为我们提供了这样一个监控的机制.


下面我们用一个简单的例程来说明这个问题:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

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

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

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

    property int index: 0

    Component {
        id: page2Component

        Page {
            id: page


            header: PageHeader {
                id: header
                title: "Second Page"
            }

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

                Text {
                    text: "Page: " + index
                }

                Button {
                    text: "Close me"
                    onClicked: pageStack.removePages(pageStack.primaryPage);
                }
            }
        }
    }

    AdaptivePageLayout {
        id: pageLayout
        anchors.fill: parent
        primaryPage: Page {
            header: PageHeader {
                title: "Primary Page"
                flickable: listView
            }

            ListView {
                id: listView
                anchors.fill: parent
                model: 10
                delegate: ListItem {
                    Label { text: modelData }
                    onClicked: {
                        mainview.index = index
                        var incubator = pageLayout.addPageToNextColumn(pageLayout.primaryPage, page2Component);
                        if (incubator && incubator.status === Component.Loading) {
                            incubator.onStatusChanged = function(status) {
                                if (status === Component.Ready) {
                                    // connect page's destruction to decrement model
                                    incubator.object.Component.destruction.connect(function() {
                                        console.log("it is destructed! " + index)
                                    });
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

在上面的例程中我们运用AdaptivePageLayout来实现我们的布局.运行我们的应用:

  



每当我们打开ListView中的一项的时候,我们通过:
   var incubator = pageLayout.addPageToNextColumn(pageLayout.primaryPage, page2Component);
把我们的新的一页加入到pageStack中,同时,我们可以利用incubator对刚刚加入的页面进行lifecycle的管理:

                        if (incubator && incubator.status === Component.Loading) {
                            incubator.onStatusChanged = function(status) {
                                if (status === Component.Ready) {
                                    // connect page's destruction to decrement model
                                    incubator.object.Component.destruction.connect(function() {
                                        console.log("it is destructed! " + index)
                                    });
                                }
                            }
                        }

每当一个页面被销毁时,我们将收到一个输出:

qml: it is destructed! 0
qml: it is destructed! 5
qml: it is destructed! 5
qml: it is destructed! 3
qml: it is destructed! 0
qml: it is destructed! 4
qml: it is destructed! 7

就像我们上面显示的那样.我们可以在这个输出的地方加入任何一个我们想要做的东西,比如释放内存及取消监视事件等.

作者:UbuntuTouch 发表于2016/4/26 11:20:08 原文链接
阅读:65 评论:0 查看评论

Read more
UbuntuTouch

[原]Ubnutu手机中的Theme颜色

在我们设计应用时,我们可能很想知道所有在Ubuntu手机设计中的Theme颜色.在今天的例程中,我们将动态地检测我们系统里所有的Theme的颜色,并显示它们.开发者可以充分利用这些颜色在自己的设计中.在我们的设计中,开发者可以通过向左或向右滑动手势来导航列表中的内容.这个项目的设计有点像是一个Property browser.可以扩展到其它的项目中.关于Ubuntu手机中的Theme更多的知识,请阅读https://developer.ubuntu.com/en/phone/apps/qml/tutorials/ubuntu-ui-toolkit-palette/.该应用可以在Ubuntu商店中下载"ThemeViewer".


   


   


Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3
import Qt.labs.settings 1.0

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

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

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

    property var props: [theme]
    property var propnames : ["theme"]

    ListModel {
        id: mymodel
    }

    Settings {
        id: settings
        property bool selectedAmbiance: true
    }

    function goUp() {
        if ( propnames.length > 1) {
            // We only do it when there is more than one item
            propnames.pop();
            header.text = propnames.join(".");
            props.pop();
            getProperties(props[props.length -1])
            console.log("swipe left: " + propnames)
        }
    }

    function getProperties(obj) {
        mymodel.clear();

        var keys = Object.keys(obj);
        for(var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var type = typeof obj[key];
            console.log(key + ' : ' + obj[key] + " " + type);

            if ( type === 'object' && obj[key] ) {
                if ( propnames.length === 3 ) {
                    mymodel.append({"name": key, "value": ""+obj[key] })
                } else {
                    mymodel.append({"name": key, "value": "white" })
                }
            }
        }
    }

    Page {
        id: page
        header: PageHeader {
            id: pageHeader
            title: i18n.tr("Theme Viewer")
            trailingActionBar.actions: [
                Action {
                    iconSource: settings.selectedAmbiance ?
                                "images/ambiance.png" : "images/dark.png"
                    text: "Ambiance"
                    onTriggered: {
                        settings.selectedAmbiance = !settings.selectedAmbiance

                        theme.name = (settings.selectedAmbiance ?
                                          "Ubuntu.Components.Themes.Ambiance" :
                                          "Ubuntu.Components.Themes.SuruDark" )

                        // We need to do it from the very beginning
                        props = [theme]
                        propnames = ["theme"]
                        header.text = propnames.join(".")
                        getProperties(props[props.length -1])
                    }
                }
            ]

            StyleHints {
                foregroundColor: UbuntuColors.orange
                backgroundColor: UbuntuColors.porcelain
                dividerColor: UbuntuColors.slate
            }
        }

        SystemPalette { id: __palette }

        Component {
            id: delegate
            Rectangle {
                width: parent.width
                height: propname.height * 1.7
                color: "transparent"

                Rectangle {
                    height: parent.height
                    width: parent.width*.2
                    anchors {
                        right: parent.right
                        top: parent.top
                    }
                    color: value
                    visible: propnames.length === 3
                }

                Label {
                    id: propname
                    text: name
                    fontSize: "x-large"
                    anchors.verticalCenter: parent.verticalCenter
                }

                MouseArea {
                    id: mouseRegion
                    anchors.fill: parent

                    onDoubleClicked: {
                        props.push(props[props.length -1][propname.text])
                        propnames.push(propname.text)
                        header.text = propnames.join(".")
                        getProperties(props[props.length -1])
                    }

                    onClicked: {
                        list.currentIndex = index;
                    }
                }

                SwipeArea {
                    id: swiperight
                    anchors.fill: parent
                    direction: SwipeArea.Rightwards

                    onDraggingChanged: {
                        if ( dragging ) {
                            console.log("swipe right: " + propname.text)
                            props.push(props[props.length -1][propname.text])
                            propnames.push(propname.text)
                            header.text = propnames.join(".")
                            getProperties(props[props.length -1])
                        }
                    }
                }
            }
        }

        Column {
            anchors {
                left: parent.left
                right: parent.right
                bottom: parent.bottom
                top: page.header.bottom
            }

            Row {
                width: parent.width
                height: header.height

                Button {
                    id: upButton
                    height: parent.height
                    width: units.gu(5)
                    iconSource: "qrc:///images/up.png"
                    onClicked:  {
                        goUp()
                    }
                }

                Label {
                    id: header
                    text: { return propnames.join(".") }
                    fontSize: "large"
                }

            }

            UbuntuListView {
                id: list
                clip:true
                width: page.width
                height: page.height - header.height
                focus: true
                model: mymodel
                highlight: Rectangle {
                    color: __palette.midlight
                    border.color: Qt.darker(__palette.window, 1.3)
                }
                highlightMoveDuration: -1
                highlightMoveVelocity: -1
                highlightFollowsCurrentItem: true
                delegate: delegate
            }

            Component.onCompleted: {
                getProperties(props[props.length -1])
            }
        }
    }

    SwipeArea {
        id: swipeleft
        direction:  SwipeArea.Leftwards
        anchors.fill: parent

        onDraggingChanged: {
            if ( dragging ) {
                goUp()
            }
        }
    }
}

整个项目的源码在:https://github.com/liu-xiao-guo/themeviewer
作者:UbuntuTouch 发表于2016/4/28 15:03:32 原文链接
阅读:111 评论:0 查看评论

Read more