Canonical Voices

UbuntuTouch

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


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


   

   

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

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


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

#ifndef CAMERA_SELECTOR_H
#define CAMERA_SELECTOR_H

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

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

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

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

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

#endif // CAMERA_SELECTOR_H


#include "cameraselector.h"

#include <QMediaService>

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

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


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

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

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

    m_deviceSelector->setSelectedDevice(cameraId);
}


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

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
)

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

add_library(FrontCamerabackend MODULE
    ${FrontCamerabackend_SRCS}
)

set_target_properties(FrontCamerabackend PROPERTIES
         LIBRARY_OUTPUT_DIRECTORY FrontCamera)

qt5_use_modules(FrontCamerabackend Gui Qml Quick Multimedia)

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

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


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

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


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

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

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

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

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

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

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

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

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

        Button {
            id: activateRearCamera
            text: "Rear Camera"

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

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

        Camera {
            id: camera

            imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

            flash.mode: Camera.FlashRedEyeReduction

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

        CameraSelector {
            id: selector
            cameraObject: camera
        }

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

        Image {
            id: photoPreview
        }
    }
}


运行我们的应用:

  


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

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

Read more
UbuntuTouch

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


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


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


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.Popups 0.1
import Qt.labs.folderlistmodel 2.1

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

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

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

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

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

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

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

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

        Component {
            id: dialog
            Dialog {
                id: dialogue

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

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

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

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

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

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

                }

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

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

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

  


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


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




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

Read more
UbuntuTouch

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


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


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtFeedback 5.0

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

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

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

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

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

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

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

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

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

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

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

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

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

import QtFeedback 5.0

然后,实例化我们的HapticsEffect:

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

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


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

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

Read more
UbuntuTouch

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


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




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


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


当一个QML应用在使用:


import Ubuntu.PushNotifications 0.1

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


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


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

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


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



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


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


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

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

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

    automaticOrientation: true
    useDeprecatedToolbar: false

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

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

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

    state: "no-push-token"

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

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

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

    }

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

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

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

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

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

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

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

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




ChatClient.qml文件的定义如下:


import QtQuick 2.0
import Ubuntu.Components 0.1

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

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

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

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

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

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


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


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

   


  


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


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


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


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

然后运行:

$nodejs server.js

这样服务器就搭建好了。





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

Read more
UbuntuTouch

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


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


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


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


在Shell中打入如下的命令:

 ls -altr /etc/alternatives/awk



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

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

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

$sudo apt-get install gawk

2)下载Ubuntu-html5-theme脚本


我们可以在地址

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

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

$chmod +x ubuntu-html5-theme

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


3)创建我们HTML5应用

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

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

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

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

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






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

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

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

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



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

Read more
UbuntuTouch

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


我们使用我们的Ubuntu SDK来创建一个最基本的应用(QML App with Simple UI)。Main.qml的内容如下:

import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

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

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

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

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

            Label {
                id: label
                objectName: "label"

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

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

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

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


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


  


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


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

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

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

    Page {
        title: i18n.tr("")

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

            Label {
                id: label
                objectName: "label"

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

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

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

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


重新运行我们的应用:


  


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


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


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

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

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

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

}

  


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



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

Read more
UbuntuTouch

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


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




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


$qmlplugindump Ubuntu.Connectivity 1.0


显示的结果如下:


import QtQuick.tooling 1.1

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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


  


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




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

Read more
UbuntuTouch

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


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


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

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

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

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

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

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

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

                property real initialWidth
                property real initialHeight

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

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

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

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

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

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


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

    


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



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

Read more
UbuntuTouch

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


bzr branch lp:ubuntu-sdk-tutorials


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


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

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

Read more
UbuntuTouch

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


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


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


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


screen.h

#ifndef SCREEN_H
#define SCREEN_H

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

class Screen : public QObject
{
    Q_OBJECT

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

private:
    int m_height;
    int m_width;
};

#endif // FILEIO_H

screen.cpp


#include "screen.h"

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

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


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

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

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

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

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

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

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

    MyScreen {
        id: screen
    }

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

        MyType {
            id: myType

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

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

            Label {
                id: label
                objectName: "label"

                text: myType.helloWorld
            }

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

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

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

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


我们应用的输出为:


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

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


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


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

Read more
UbuntuTouch

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

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


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


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

  

 

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


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

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

#ifndef FILEIO_H
#define FILEIO_H

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

class FileIO : public QObject
{
    Q_OBJECT

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

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

    QString source() { return mSource; };

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

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

private:
    QString getenv(const QString envVarName) const;

private:
    QString mSource;
    QString datapath;
};

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

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

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

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

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

#endif // FILEIO_H

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

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

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

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

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

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

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

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

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

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

    if (mSource.isEmpty())
        return false;

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

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

    file.close();

    return true;
}

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

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

在backend的CMakeLists.txt中加入:

    modules/ReadFileQML/fileio.cpp

同时在backend.cpp中加入:

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

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

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


3)在应用中调用

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

import QtQuick 2.0
import Ubuntu.Components 1.1
import ReadFileQML 1.0

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

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

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

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

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

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

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

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

            wrapMode: Text.Wrap
        }

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

            text: "Reload file"

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

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

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

这里,我们定义了:


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

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



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

Read more
UbuntuTouch

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


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


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


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

   

   

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


2)加入我们需要的文件


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

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

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

TEMPLATE = app
TARGET = trafficlight

load(ubuntu-click)

QT += core qml quick

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

RESOURCES += trafficlight.qrc

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

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

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

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

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

SOURCES += main.cpp trafficlight.cpp

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

HEADERS += trafficlight.h

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

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


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

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

#include <trafficlight.h>

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

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

    int count = 3;

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

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

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

这里,我们通过:

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

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

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

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

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

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

import QtQuick 2.0
import Ubuntu.Components 1.1
import Light 1.0

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

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

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

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

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

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

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

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

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

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

import Light 1.0

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

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

运行我们的应用:



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

Read more
UbuntuTouch

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


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


import QtQuick 2.0
import Ubuntu.Components 1.1

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

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

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

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

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

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

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

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

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

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




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

Read more
UbuntuTouch

很多QML应需要访问web services。我们可以通过Javascript的方法来解析得到我们所需要的JSON数据,并把它们展示出来。在今天的例子中,我们将展示如何实现它?


我们可以创建一个最基本的“QML App with Simple UI (qmlproject)”,并取名我们的应用为“baidutranslator”。我们将使用的API为:


http://openapi.baidu.com/public/2.0/bmt/translate?client_id=2hG67S2yRm5chkr62j2IEmYL&from=auto&to=auto&q=%E4%BD%A0%E5%A5%BD

显示的结果为:

{"from":"zh","to":"en","trans_result":[{"src":"\u4f60\u597d","dst":"Hello"}]}

我们可以通过这个API的接口来得到中文或英文的翻译,甚至我们可以得到一个完整句子的中文或英文。上面接口返回的结果是JSON格式的。

为了能够解析我们得到的JSON格式,我们创建了一个“jsonparser.js”文件:


var URL = "http://openapi.baidu.com/public/2.0/bmt/translate?client_id=2hG67S2yRm5chkr62j2IEmYL&from=auto&to=auto&q=";

function startParse(keyword, callback) {
    var doc = new XMLHttpRequest();
    doc.onreadystatechange = function() {
        if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
        } else if (doc.readyState === XMLHttpRequest.DONE) {
            if(doc.status != 200) {
                console.log("!!!Network connection failed!")
            }
            else {
                console.log("got some results!");
                if(doc.responseText == null) {
                }
                else {
                    console.log("result: ", doc.responseText)
                    var json = JSON.parse('' + doc.responseText+ '');
                    json["status"] = "OK";
                    callback.update(json);
                }
            }
        }
    }

    doc.open("GET", URL + keyword);
    doc.send();
}


我们通过“startParse”来发送请求,并通过JSON.parse()来解析我们得到的结果。我们通过“callback.update”来返回到我们的QML设计中。


Main.qml”的设计如下:


import QtQuick 2.0
import Ubuntu.Components 1.1
import "jsonparser.js" as API

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

MainView {
    id: root

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

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

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

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

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

    function update(json) {
        console.log("json: " + JSON.stringify(json));

        mymodel.clear();

        if ( json.trans_result !== undefined && json.trans_result.length !== undefined ) {
            for ( var idx = 0; idx < json.trans_result.length; idx++ ) {
                if ( json.trans_result[ idx ].dst ) {
                    console.log( 'meaning: ' + json.trans_result[ idx ].dst);
                    mymodel.append( {"meaning": json.trans_result[ idx ].dst });
                }
            }
        } else {
            mymodel.clear();
        }
    }

    Page {
        title: i18n.tr("Baidu translator")

        ListModel {
            id: mymodel
        }

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

            TextField {
                id: input
                placeholderText: "Please input a word"
                width: parent.width
                text: "你好"

                onTextChanged: {
                    mymodel.clear();
                    var json = API.startParse(input.text, root);
                }
            }

            Button {
                id: doit
                width: parent.width

                text: i18n.tr("Translate")

                onClicked: {
                    var json = API.startParse(input.text, root);
                }
            }

            ListView {
                id: listview
                width: parent.width
                height: parent.height - input.height - doit.height
                model: mymodel

                delegate: Text {
                    text: modelData
                }

            }
        }
    }
}

这里我们通过“update”来更新我们的ListView。


  


所有项目的源码是在:git clone https://gitcafe.com/ubuntu/baidutranslator.git


作者:UbuntuTouch 发表于2015/4/30 12:07:35 原文链接
阅读:219 评论:0 查看评论

Read more
UbuntuTouch

在前面的一篇文章中“如何在Ubuntu手机中使用前置照相机”,我们可以使用相应的C++代码来控制前后位置的照相机来拍照,但是我们又如何能够把所拍到的照片存储到相应的文件中呢?我们可以使用Qt 5.4版本中的Item所提供的一个新的功能“grabToImage”。这样我们可以很方便地把我们得到的照片存到我们相应的目录中。


在Ubuntu手机平台中,由于安全的原因,我们的应用只可以访问自己所在的可以访问的目录。在这个例程中,我们使用了一些环境的变量来得到我们所需要的存储的目录的位置:


我们整个的代码如下:


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

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

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

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

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

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

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

    Page {
        id: mainPage
        title: i18n.tr("camerashot")

        property string path: ""

        ReadEnv {
            id: env
        }

        Item {
            id: page1
            anchors.fill: parent

            Camera {
                id: camera

                imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash

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

                flash.mode: Camera.FlashRedEyeReduction

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

            VideoOutput {
                id: video
                source: camera
                anchors.fill: parent
                focus : visible // to receive focus and capture key events when visible
                orientation: -90

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        var APP_ID = env.getenv("APP_ID");
                        var app_pkgname = APP_ID.split('_')[0]
                        mainPage.path = env.getenv("XDG_DATA_HOME") +
                                "/" + app_pkgname + "/pic.png";
                        console.log("share path: " + mainPage.path);

                        var a = video.grabToImage(function(result) {
                            result.saveToFile(mainPage.path);
                        }, Qt.size(photoPreview.width, photoPreview.height) );

                        console.log("return result: " + a);

                    }
                }

                Image {
                    id: photoPreview
                }
            }

            Button {
                id: button
                anchors.bottom: parent.bottom
                anchors.right: parent.right

                text: "Show the taken picture"

                onClicked: {
                    pic.visible = true;
                    page1.visible = false;
                    console.log("image path: " + mainPage.path);
                    image.source = "";
                    image.source = "file:///" + mainPage.path;
                }
            }
        }

        Item {
            id: pic

            anchors.fill: parent
            visible: false

            Image {
                id: image
                cache: false
                anchors.fill: parent
            }

            Label {
                text: mainPage.path
            }

            Image {
                anchors.bottom: parent.bottom
                anchors.left: parent.left
                anchors.leftMargin: units.gu(1)
                anchors.bottomMargin: units.gu(1)
                width: units.gu(3)
                height: units.gu(3)

                source: "images/icon-left-arrow.png"
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        pic.visible = false
                        page1.visible = true;
                    }
                }
            }
        }

    }
}


整个的设计非常简单。在主界面中我们可以在相机的影像的任何地方进行触屏来拍下我们的照片。我们可以使用“Show the taken picture”按钮来进入下一个页面来显示存储的图片。为了方便设计,我们选择了一个固定的文件名。当然,开发者们可以自己来选择自己的所需要的文件名。


  


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

作者:UbuntuTouch 发表于2015/5/18 16:55:18 原文链接
阅读:191 评论:0 查看评论

Read more
UbuntuTouch

我们知道有很多的web services是使用XML格式的,我们可以通过使用XmlListModel来解析我们的XML。但是在有些情况下,我们可能需要使用Javascript来解析XML,这样使得我们可以更加灵活地解析我们所需要的XML数据。比如,通过一个请求,我们可以来解析XML结果中的多个数据。比较而言,XmlListModel只能对XPath路经下(由source属性定义)的数据进行解析。如果需要多个路径,可以通过多次对不同的路径进行查询。当然,我们可能需要一些方法来同步这些查询(如果最终的数据有互相联系的话)。


我们这里就使用我们已经有的一个教程“构建首个QML应用程序”。在这个应用中,它使用了XmlListModel来解析所得到的XML数据。API的接口为:http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml


This XML file does not appear to have any style information associated with it. The document tree is shown below.
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time="2015-04-29">
<Cube currency="USD" rate="1.1002"/>
<Cube currency="JPY" rate="131.20"/>
<Cube currency="BGN" rate="1.9558"/>
<Cube currency="CZK" rate="27.435"/>
<Cube currency="DKK" rate="7.4619"/>
<Cube currency="GBP" rate="0.71610"/>
<Cube currency="HUF" rate="302.55"/>
<Cube currency="PLN" rate="4.0120"/>
<Cube currency="RON" rate="4.4125"/>
<Cube currency="SEK" rate="9.2723"/>
<Cube currency="CHF" rate="1.0491"/>
<Cube currency="NOK" rate="8.3850"/>
<Cube currency="HRK" rate="7.5763"/>
<Cube currency="RUB" rate="56.7850"/>
<Cube currency="TRY" rate="2.9437"/>
<Cube currency="AUD" rate="1.3762"/>
<Cube currency="BRL" rate="3.2467"/>
<Cube currency="CAD" rate="1.3262"/>
<Cube currency="CNY" rate="6.8211"/>
<Cube currency="HKD" rate="8.5278"/>
<Cube currency="IDR" rate="14212.78"/>
<Cube currency="ILS" rate="4.2601"/>
<Cube currency="INR" rate="69.7841"/>
<Cube currency="KRW" rate="1179.14"/>
<Cube currency="MXN" rate="16.8221"/>
<Cube currency="MYR" rate="3.9178"/>
<Cube currency="NZD" rate="1.4310"/>
<Cube currency="PHP" rate="48.743"/>
<Cube currency="SGD" rate="1.4557"/>
<Cube currency="THB" rate="36.142"/>
<Cube currency="ZAR" rate="13.0682"/>
</Cube>
</Cube>
</gesmes:Envelope>

为了能够解析我们的XML数据,我们可以通过如下的方法来解析:


function startParse() {    
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
        } else if (xhr.readyState == XMLHttpRequest.DONE) {
            var doc = xhr.responseXML.documentElement;
            showRequestInfo("xhr length: " + doc.childNodes.length );

            for (var i = 0; i < doc.childNodes.length; ++i) {
                var child = doc.childNodes[i];

                for (var j = 0; j < child.childNodes.length; ++j) {
                    if ( child.nodeName ===  "Cube") {

                        var kid = child.childNodes[j];
                        var length = kid.childNodes.length;

                        for ( var k = 0; k < length; k ++) {
                            var cube = kid.childNodes[k];

                            if ( cube.nodeName === "Cube") {
                                var len = cube.attributes.length;
                                var currency = cube.attributes[0].nodeValue;
                                var rate = cube.attributes[1].nodeValue;
                                currencies.append({"currency": currency, "rate": parseFloat(rate)})
                            }
                        }
                    }

                }
            }
        }
    }

    xhr.open("GET", URL);
    xhr.send();
}

这里我们使用了“XMLHttpRequest”来发送我们的请求,并通过“nodeName”及“nodeValue”来遍历我们的XML数据。最终,我们完成解析我们的XML数据。在项目中,我们定义了“xmlparser.js”文件。


Main.qml

import QtQuick 2.0
import Ubuntu.Components 1.1
import QtQuick.XmlListModel 2.0
import Ubuntu.Components.ListItems 0.1
import Ubuntu.Components.Popups 0.1
import "xmlparser.js" as API

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

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

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

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

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

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

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

    function convert(from, fromRateIndex, toRateIndex) {
        var fromRate = currencies.getRate(fromRateIndex);
        if (from.length <= 0 || fromRate <= 0.0)
            return "";
        return currencies.getRate(toRateIndex) * (parseFloat(from) / fromRate);
    }

    function update() {
        indicator.running = false;
    }

    Page {
        title: i18n.tr("Currency Converter")

        ListModel {
            id: currencies
            ListElement {
                currency: "EUR"
                rate: 1.0
            }

            function getCurrency(idx) {
                return (idx >= 0 && idx < count) ? get(idx).currency: ""
            }

            function getRate(idx) {
                return (idx >= 0 && idx < count) ? get(idx).rate: 0.0
            }
        }

        ActivityIndicator {
            id: indicator
            objectName: "activityIndicator"
            anchors.right: parent.right
            running: true
        }

        Component {
            id: currencySelector
            Popover {
                Column {
                    anchors {
                        top: parent.top
                        left: parent.left
                        right: parent.right
                    }
                    height: pageLayout.height
                    Header {
                        id: header
                        text: i18n.tr("Select currency")
                    }
                    ListView {
                        clip: true
                        width: parent.width
                        height: parent.height - header.height
                        model: currencies
                        delegate: Standard {
                            objectName: "popoverCurrencySelector"
                            text: model.currency
                            onClicked: {
                                caller.currencyIndex = index
                                caller.input.update()
                                hide()
                            }
                        }
                    }
                }
            }
        }

        Column {
            id: pageLayout

            anchors {
                fill: parent
                margins: root.margins
            }

            spacing: units.gu(1)

            Row {
                spacing: units.gu(1)

                Button {
                    id: selectorFrom
                    objectName: "selectorFrom"
                    property int currencyIndex: 0
                    property TextField input: inputFrom
                    text: currencies.getCurrency(currencyIndex)
                    onClicked: PopupUtils.open(currencySelector, selectorFrom)
                }

                TextField {
                    id: inputFrom
                    objectName: "inputFrom"
                    errorHighlight: false
                    validator: DoubleValidator {notation: DoubleValidator.StandardNotation}
                    width: pageLayout.width - 2 * root.margins - root.buttonWidth
                    height: units.gu(5)
                    font.pixelSize: FontUtils.sizeToPixels("medium")
                    text: '0.0'
                    onTextChanged: {
                        if (activeFocus) {
                            inputTo.text = convert(inputFrom.text, selectorFrom.currencyIndex, selectorTo.currencyIndex)
                        }
                    }

                    // This is more like a callback function
                    function update() {
                        text = convert(inputTo.text, selectorTo.currencyIndex, selectorFrom.currencyIndex)
                    }
                }
            }

            Row {
                spacing: units.gu(1)
                Button {
                    id: selectorTo
                    objectName: "selectorTo"
                    property int currencyIndex: 1
                    property TextField input: inputTo
                    text: currencies.getCurrency(currencyIndex)
                    onClicked: PopupUtils.open(currencySelector, selectorTo)
                }

                TextField {
                    id: inputTo
                    objectName: "inputTo"
                    errorHighlight: false
                    validator: DoubleValidator {notation: DoubleValidator.StandardNotation}
                    width: pageLayout.width - 2 * root.margins - root.buttonWidth
                    height: units.gu(5)
                    font.pixelSize: FontUtils.sizeToPixels("medium")
                    text: '0.0'
                    onTextChanged: {
                        if (activeFocus) {
                            inputFrom.text = convert(inputTo.text, selectorTo.currencyIndex, selectorFrom.currencyIndex)
                        }
                    }

                    function update() {
                        text = convert(inputFrom.text, selectorFrom.currencyIndex, selectorTo.currencyIndex)
                    }
                }
            }

            Button {
                id: clearBtn
                objectName: "clearBtn"
                text: i18n.tr("Clear")
                width: units.gu(12)
                onClicked: {
                    inputTo.text = '0.0';
                    inputFrom.text = '0.0';
                }
            }
        }

        Component.onCompleted: {
            API.startParse(root);
        }
    }
}


  


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




            
作者:UbuntuTouch 发表于2015/4/30 13:13:42 原文链接
阅读:237 评论:0 查看评论

Read more
UbuntuTouch

我们知道ListView在QML应用中扮演非常重要的角色。看看我们的很多的应用都是在使用ListView。那么当我们点击ListView中的item并导航到另外一个页面呢?其实这样的方法有很多。在这篇文章中,我们来介绍其中的几种。开发者可以参照其中的设计,或自己想出更好的设计。


1)使用PageStack来完成


在我们的RssReader中的例子中,我们使用了PageStack来完成我们的导航。我们可以把我们的每个页面都做成我们的Page。当我们的页面被点击后,我们把新的Page压入栈中。在返回时,我们只需要点击返回按钮即可:


     

我们可以在我的例程中找到相应的代码。


2)使用一个不可见的显示在需要时显示出来


在我们的使用中,我们使用两个重叠在一起的窗口,但是详细的页面只有在ListView中的item被点击后才能显示。在默认的情况下,我们显示ListView。

        ListView {
            id: listview

            clip: true
            anchors.fill: parent
            model:mymodel

            header: Text {
                text: "This is the header"
                font.pixelSize: 30

                Rectangle {
                    anchors.top: parent.bottom
                    width: listview.width
                    height: units.gu(0.4)
                    color: "blue"
                }
            }

            delegate: MyDelegate {}

            footer: Text {
                text: "This is the footer"
                font.pixelSize: 30
            }
        }

        Item {
            id: popup
            visible: false
            clip: true
            property url loadUrl
            onLoadUrlChanged: {
                opacity = 0;
                visible = (loadUrl == '' ? false : true);
                console.log("opacity: " + opacity );
                console.log("visible: " + visible );
            }
            anchors.fill: parent
            Rectangle {
                id: bg
                anchors.fill: parent
                color: "white"
            }
            MouseArea{
                anchors.fill: parent
                enabled: popup.visible
                //Eats mouse events
            }
            Loader{
                focus: true
                source: popup.loadUrl
                width: parent.width
                height: parent.height -toolbar.height
            }

            Rectangle {
                id: toolbar
                width: parent.width
                height: units.gu(4)
                anchors.bottom: parent.bottom
                color: "blue"

                Icon {
                    name: "previous"
                    width: units.gu(3.5)
                    height: units.gu(3.5)

                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            popup.loadUrl = "";
                            ani.running = true;
                        }
                    }
                }
            }

            NumberAnimation on opacity {
                id: ani
                from: 0
                to: 1
                duration: 3000
            }
        }
    }


在上面的代码中,默认的情况下,我们试popup为不可见,它和listview是重叠在一起的。当item被点击后,我们才修改它为可见。这个代码在MyDelegate中实现的:

MyDelegate.qml


import QtQuick 2.0

Item {
    property Item item

    width: listview.width
    height: units.gu(8)
    Text {
        id: text
        text: title
        anchors.verticalCenter: parent.verticalCenter
        anchors.leftMargin: units.gu(10)
        font.pixelSize: 30

    }

    Rectangle {
        anchors.top: text.bottom
        width: parent.width
        height: units.gu(0.2)
        color: "gray"
    }

    Image {
        anchors.right: parent.right
        anchors.verticalCenter: parent.verticalCenter
        source: "images/arrow.png"
        rotation: -90
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log("it is clicked");
            popup.loadUrl = "ExampleQml.qml"
        }
    }
}

  

代码在地址 git clone https://gitcafe.com/ubuntu/listview_visible.git



3)使用一个左右结构的页面,每次只有一个页面显示


同样的方法,我们可以使用一个左右排列的两个页面。每个页面的大小和主屏是一样大小的。但是,只有一个页面在不同的时间显示。我们可以通javascript来控制什么时候显示哪个页面。我们可以通过改变x坐标来显示我们所需要的页面。可以结合动画来展示效果:

        Row {
            id: view

            ListView {
                id: listview

                clip: true
                width: root.width
                height: root.height
                model:mymodel

                header: Text {
                    text: "This is the header"
                    font.pixelSize: 30

                    Rectangle {
                        anchors.top: parent.bottom
                        width: listview.width
                        height: units.gu(0.4)
                        color: "blue"
                    }
                }

                delegate: MyDelegate {}

                footer: Text {
                    text: "This is the footer"
                    font.pixelSize: 30
                }
            }

            // This is the second page
            DetailedPage {
                id: detailPage
                width: root.width
                height: root.height
            }

            Behavior on x {
                 NumberAnimation { duration: 500 }
            }
        }

通过使用Row来把两个页面并列显示,但是,每次只显示其中的一个页面。当点击ListView中的item后,改变当前view的x坐标来显示另外一个页面。这个是通过MyDelegate来改变的:

MyDelegate.qml


import QtQuick 2.0

Item {
    property Item item

    width: listview.width
    height: units.gu(8)
    Text {
        id: text
        text: title
        anchors.verticalCenter: parent.verticalCenter
        anchors.leftMargin: units.gu(10)
        font.pixelSize: 30

    }

    Rectangle {
        anchors.top: text.bottom
        width: parent.width
        height: units.gu(0.2)
        color: "gray"
    }

    Image {
        anchors.right: parent.right
        anchors.verticalCenter: parent.verticalCenter
        source: "images/arrow.png"
        rotation: -90
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
//            popup.loadUrl = "ExampleQml.qml"
            view.x = -root.width;
        }
    }
}

  

当然我们也可以把画面弄成上下排列的,使用同样的方法,我们可以把详细的画面移动到我们的视口中即可。

所有的源码在: git clone https://gitcafe.com/ubuntu/listview.git
作者:UbuntuTouch 发表于2015/5/4 16:38:32 原文链接
阅读:201 评论:0 查看评论

Read more
UbuntuTouch

培训需要:

带上自己的电脑

带上自己的电脑充电器

-可以自己安装14.10或15.04的Ubuntu操作系统

-按照“Ubuntu 手机开发培训准备”安装Ubuntu开发SDK

-在上面的链接中下载自己感兴趣的学习资料

-详细活动信息,请参照我们的官方微博:http://weibo.com/officialubuntu

-如有任何问题,欢迎到我们的讨论区进行讨论

-本人微博:老刘就是老牛


 

作者:UbuntuTouch 发表于2015/5/5 10:19:18 原文链接
阅读:303 评论:0 查看评论

Read more
UbuntuTouch

对于一下应用来说,我们需要使用网路上的一下文件,并下载它们。那么我们怎么在QML应用中来下载文件呢?我们在SDK API的网页中,我们发现有一个叫做Download Manager的API。我们可以使用SingleDownloadDownloadManager来下载一个或多个文件。


首先,我们来创建一个简单的Download应用。这里我们使用“QML app with Simple UI (qmlproject)”。对于大家不熟悉Download Manager的开发者来说,我们可以使用如下的方法得到它更加详细的接口:


$qmlplugindump Ubuntu.DownloadManager 0.1

通过上面的命令,我们可以得到如下的输出:


import QtQuick.tooling 1.1

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

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

在今天的例子里,我们将使用SingleDownload来下载我们所需要的图片。我们可以看到,SingleDownload有“finished”信号,我们可以通过这个信号来得到文件下载完毕的通知,并在“path”中得到我们所下载文件的路径。当然我们也可以使用其它的信号来得到下载的更多的信息。


我们来修改我们的“Main.qml”如下:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.DownloadManager 0.1

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

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

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

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

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

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

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

        Rectangle {
            width: parent.width
            height: units.gu(20)
            TextField {
                id: text
                placeholderText: "File URL to download..."
                height: units.gu(5)
                anchors {
                    left: parent.left
                    right: button.left
                    rightMargin: units.gu(2)
                }

                text: "http://bbs.unpcn.com/attachment.aspx?attachmentid=3820584"
            }

            Button {
                id: button
                text: "Download"
                height: 50
                anchors.right: parent.right
                anchors.verticalCenter: text.verticalCenter
                onClicked: {
                    single.download(text.text);
                }
            }

            TextField {
                id: downloaded
                placeholderText: "Downloaded file address"
                height: 100
                anchors {
                    left: parent.left
                    right: parent.right
                    top: button.bottom
                    rightMargin: units.gu(2)
                    topMargin: units.gu(1)
                }
            }

            ProgressBar {
                id: progress
                minimumValue: 0
                maximumValue: 100
                value: single.progress
                anchors {
                    left: parent.left
                    right: parent.right
                    bottom: parent.bottom
                }

                SingleDownload {
                    id: single

                    onFinished: {
                        downloaded.text = path;
                        console.log("downloaded path: " + path);
                    }
                }
            }

            Image {
                anchors.top: progress.bottom
                source: downloaded.text
            }
        }
    }
}


界面非常简单,我们在上面可以输入我们想要的URL文件地址,在下面显示下载后的文件的路径。我们可以点击“Download”按钮来下载我们所需要的文件。为了说明问题,我们在下面使用了一个Image来显示下载的图片:




所有的源码在:git clone https://gitcafe.com/ubuntu/download.git


作者:UbuntuTouch 发表于2015/5/5 14:23:01 原文链接
阅读:169 评论:0 查看评论

Read more
UbuntuTouch

我们知道qmake是Qt最原生的跨平台编译工具。由于一些原因,在Ubuntu手机14.10上,广泛使用CMake以跨不同平台。事实上,在Qt Creator上,如果我们使用qmake,我们可以更加方便地管理我们的项目。比如:


-我们可以在项目中使用点击右鼠标键来添加或删除文件。在CMake项目中,我们只能进行添加的动作

-在新的模版中加入了使用“main()”的入口,这样可以很方便地让我们的项目编译成为binary。对那些希望把自己的代码进行保护的开发者来说,这无疑是个好的方案

-对文件的部署来说,qmake比CMake方便得多。开发者们可以研究一下项目管理文件.pro


注意:开发者也可以在14.10的Desktop上创建qmake项目,但是它的运行必须是在15.04的目标上(比如15.04的手机或15.04的模拟器上)。当然生产的click包可以除外(只要没有使用Qt 5.4中的新的features)。注:15.04系统使用的Qt 5.4,14.10使用的是Qt 5.3


1)创建一个最基本的QML qmake项目


打开SDK,并按照如下的步骤来创建项目:


   


  


我们可以和以前一样的方法来运行我们的项目 (使用热键 Ctrl + R):




删除文件:




在14.10的armhf Kit下运行qmake项目:




在15.04的手机中运行:



click文件包的内容如下:




在上面的包中,我们可以看到有“Main.qml”文件在其中。也就是说,任何人可以获取这个内容只有把click进行重新打开即可。


创建的源码在: git clone https://gitcafe.com/ubuntu/qmaketest.git


2)创建一个QtQuick的应用


利用同样的方法,我们使用SDK按照一下的方式:


   

  


我们可以看出来在项目中有一个叫做“main.cpp”文件。

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

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

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

这个文件用来启动我们的QML文件“Main.qml”。整个的QML文件被放置于一个叫做“qtquicktest.qrc”的资源文件中。这个文件将被变成最终binary的一部分,而不被其它文件看见。

最终的生产文件为qtquicktest。这个在“qtquicktest.pro”中被定义:

TEMPLATE = app
TARGET = qtquicktest

我们可以把最终的click包的内容展开如下:



在这里,我们可以看到一个叫做“./lib/arm-linux-gnueabihf/bin/qtquicktest”文件。这个就是我们的执行文件。我们看不见任何的QML文件。运行应用:




所有的源码在: git clone https://gitcafe.com/ubuntu/qtquicktest.git


作者:UbuntuTouch 发表于2015/5/6 11:49:38 原文链接
阅读:160 评论:0 查看评论

Read more