2013年1月6日日曜日

Ubuntu for Phoneのアプリケーション作成チュートリアル(その3)


本日から、こちらのブログのQtネタはplanet で日本Qtユーザー会に転載される事に成りました。

Google Bloggerの機能だけだと、コードとかわかりやすくするのが難しいので、手書きHTMLで枠をつけたりしていたのですが、Atomを使った転載だと消えてしまうようでして。<hr>とか使って、区切りを入れたりとか、少し工夫してみる事にします。直前までの記事をわざわざ大仰に手直ししたりはしないので、planetでお読みの方は、読みづらい記事が多くてごめんなさい。

あと、Twitter経由の知り合いの読者が多いので、割と内輪ネタが多い場合もあります。ご不快でしたらごめんなさい。

さて、それでは引き続きにゅあんすで翻訳しつつ、適当に補足しつつ、チュートリアルのにゅあんすで〜翻訳を続けようかと思います。3ページ目、4ページ目は少ないですから、続けて行っちゃいます。


UIのアレンジ

ここまで、通貨コンバータアプリのためにバックエンドと素材のセットアップをしてきました。それでは最終のステップに移り楽しみましょう。それらを統合して、結果をみてみます。

ここまでと同様に最後の断片を追加しましょう。


    Column {
        id: pageLayout

        anchors {
            fill: parent
            margins: root.margins
            topMargin: title.height
        }
        spacing: units.gu(1)

        Row {
            spacing: units.gu(1)

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

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

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

            TextField {
                id: inputTo
                errorHighlight: false
                validator: DoubleValidator {notation: DoubleValidator.StandardNotation}
                width: pageLayout.width - 2 * root.margins - root.buttonWidth
                height: units.gu(4)
                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 {
            text: i18n.tr("Clear")
            width: units.gu(12)
            onClicked: {
                inputTo.text = '0.0';
                inputFrom.text = '0.0';
            }
        }
    }

これは、前の断片よりも長いコードの一部ですが、かなり単純で新しい構文はでてきません。ここでは、root のエリアにユーザーインターフェースを提供するための視覚コンポーネントの配置とシグナルハンドラの設定を行っています。

どのようにonClickedシグナルハンドラが、ユーザーが通貨の選択しをクリックした時の動作(例えばpopupを開く)動作を定義しているか、onTextChengedハンドラが入力された通貨の変換ように事前に定義された関数をどのように呼び出すのか、そしてcurrentSelector コンポーネントで前回期待されていたlist view itemのupdate()関数を定義している事に着目して下さい。

レイアウトを設定するためにColumnと2つのRowを使用しています。各行には、通貨選択ボタンと、通貨変換値を表示入力するためのテキストフィールドがあります。下には両方のテキストを一回でクリアするためのボタンも設置しています。これは、レイアウトのモックアップ画像です。

驚く事なかれ!

これでおしまいです。さぁ、もうのんびりと作ったものを楽しめます。好きな方法でqmlsceneを呼び出して下さい(私は、Ctrl+Shift+Pのカスタムショートカットを使っています)、そして、あなたがほんの数行のコードで書いた、完全な機能の素敵な通貨変換をご覧下さい。


まとめ

これで、フォームファクター非依存の携帯電話向けUbuntuアプリケーションの作成方法を学びました。ここまでで、あなたはQML, JavaScriptと色々なUnuntuコンポーネントのようなパワフルなテクノロジーを組み合わせて、素敵に奇麗なUbuntuルックのアプリケーションを作り出しました。

あなたは、これらのテクノロジーがもたらすとても大きな可能性に気がついた事でしょう。ここからはあなた次第です。私たちのtoolkitのpreviewを試し、フィードバックをすることで、Ubuntuが新しい多数の携帯電話に搭載されるお手伝いをして下さい。

詳細は

この先は、Toolkitの話ですが、こちらは以前のエントリに書いたのでそちらをどうぞ。

ここまでの記事

その1その2Toolkitについて

ここまでのコード

import QtQuick 2.0
import QtQuick.XmlListModel 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1
import Ubuntu.Components.Popups 0.1

Rectangle {
    id: root
    width: units.gu(60)
    height: units.gu(80)
    color: "lightgray"

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

    Label {
       id: title
       ItemStyle.class: "title"
       text: i18n.tr("CurrencyConverter")
       height: contentHeight + root.margins
       anchors {
           left: parent.left
           right: parent.right
           top: parent.top
       }
    }

    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
        }
    }

    XmlListModel {
        id: ratesFetcher
        source: "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"
        namespaceDeclarations: "declare namespace gesmes='http://www.gesmes.org/xml/2002-08-01';"
                               +"declare default element namespace 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref';"
        query: "/gesmes:Envelope/Cube/Cube/Cube"

        onStatusChanged: {
            if (status === XmlListModel.Ready) {
                for (var i = 0; i < count; i++)
                    currencies.append({"currency": get(i).currency, "rate": parseFloat(get(i).rate)})
            }
        }

        XmlRole { name: "currency"; query: "@currency/string()" }
        XmlRole { name: "rate"; query: "@rate/string()" }
    }

    ActivityIndicator {
        anchors.right: parent.right
        running: ratesFetcher.status === XmlListModel.Loading
    }

    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);
    }

    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 {
                        text: currency
                        onClicked: {
                            caller.currencyIndex = index
                            caller.input.update()
                            hide()
                        }
                    }
                }
            }
        }
    }
    Column {
        id: pageLayout

        anchors {
            fill: parent
            margins: root.margins
            topMargin: title.height
        }
        spacing: units.gu(1)

        Row {
            spacing: units.gu(1)

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

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

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

            TextField {
                id: inputTo
                errorHighlight: false
                validator: DoubleValidator {notation: DoubleValidator.StandardNotation}
                width: pageLayout.width - 2 * root.margins - root.buttonWidth
                height: units.gu(4)
                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 {
            text: i18n.tr("Clear")
            width: units.gu(12)
            onClicked: {
                inputTo.text = '0.0';
                inputFrom.text = '0.0';
            }
        }
    }
}


ということで、Ubuntu for Phoneのチュートリアルをにゅあんすでー翻訳してみましたけど、いかがでしたでしょうか。

細かい説明をと考えていましたが、このチュートリアルをやるうちに、実はこのチュートリアル、QMLを知らないといまいち良くわからずに流れてしまう気がしてきました。

そんなわけで、QMLについて、もうちょっと何かわかりやすい方法で、チュートリアル作れたらなぁなんて思ってます。

Twitterでは、今の円・ドルレート表示とかも欲しい的な話を聞きましたので、XMLでデータをくれそうな所を探しています。見つかったらさっくり作ってみたいなと思います。
QMLでグラフ書くのはちょっと大変そうなんで、まぁ、最初は文字だけ表示でしょうけど。

ちなみに、Rectangleの代わりにImageを指定し、colorプロパティの代わりにsourceプロパティを指定して、画像ファイルをURL指定するとこんな事も出来ちゃいます。


え、なんでアニメを使わないかって?さすがにQtユーザー会に転送されますからねぇ。そういう無茶はできませんでした。誰か、一定のルールで背景に使わせてくれる、いけてる萌え絵の絵師さんを紹介してくれませんか・・・。

0 件のコメント:

コメントを投稿