2013年1月7日月曜日

Qt の過去のNews Titleを途中まで


Webarchiveでnewsの過去ログを漁ってみた隠者です。
YAPF部の部長-黒の王-が時期等も含めて、Qt/Embeddedの流れとか知りたいなとおっしゃられていたので、少しまとめてみたいなぁと思ったのですが、抜けているものも多くて。

過去のものは楽だったのですが、2006年以降の動的なページはアーカイブされていないものも多いようでして。

Nokiaさんがサイト作ってた頃は、Trolltech時代のnewsまでArchiveしていてくれたのですが、Digiaさんでは見当たりませんでした。まとめるどころか、まだ日付とタイトルを並べただけですが、一応残しておきます。この続きどうしよう・・・。


1998/03/27 Trolltech Announces Qt Snapshots
1998/04/05 Trolltech Presents Qt-Based Port of Netscape Navigator 5.0
1998/04/08 The KDE Free Qt Foundation Announced
1998/07/09 Qt version 1.40 released
1998/10/01 Qt version 1.41 release
1998/11/18 Next Generation of Qt Free Edition to be Open Source
1998/12/02 Qt version 1.42 released
1999/03/02 Linux version of Opera to be based on Qt
1999/03/04 QPL 1.0 released
1999/03/12 Qt version 1.44 released
1999/06/25 Qt version 2.0 released
1999/07/23 Qt version 2.0.1 released
1999/09/30 Qt version 2.0.2 released
2000/03/09 The Embedded Linux Consortium launched
2000/03/20 Qt/Embedded (Preview)
2000/04/12 Qt version 2.1 released
2000/05/31 Qt version 2.1.1 released
2000/06/19 Beta release of Qt/Embedded
2000/06/27 Qt Designer preview
2000/07/10 Trolltech Donates Qt/Windows Site Licenses to Educational Institutions
2000/08/07 Partnership with Ericsson
2000/08/09 Trolltech and NEC Cooperate
2000/09/04 Qt free edition goes GPL
2000/09/06 Trolltech and Loki Partnership
2000/09/06 Qt 2.2 released
2000/09/25 Opera Software, PalmPalm and Trolltech form Strategic Alliance
2000/09/26 Embedded partnerships in Asia
2000/10/05 Qt 2.2.1 released
2000/10/24 Qt Linguist pre-released
2000/10/26 Trolltech congratulates KDE
2000/10/30 Trolltech to add GPL licensing to Qt/Embedded
2000/11/13 Qt/Embedded available for download (with Qt Palmtop)
2000/11/13 Qt 2.2.2 released
2000/12/07 What's the Difference Between a Linux Smart Phone and an Expensive Paper-Weight?
(PalmPalm http://www.mobilemag.com/2001/04/05/tynux-the-first-linux-based-smart-phone/)
Tynix OS
2000/12/12 Qt 2.2.3 released
2000/12/18 Qt Palmtop Environment 1.1 released
2000/12/19 Qt Palmtop Environment 1.1.1
2001/01/31 Voice control on desktop Linux applications
2001/02/02 Qt 2.2.4 released
2001/03/07 I can see clearly now (Qt 2.3 released)
2001/03/12 Prime time for Embedded Linux
2001/03/12 Concept Engineering's Support for Qt Provides Boost to C++ EDA Developers
2001/04/03 Trolltech's Qt University Makes the Dean's list
2001/04/09 Trolltech Previews Enterprise Ready Cross-Platform Application Framework
2001/04/09 MontaVista and Trolltech Join Forces: Embedded Linux and Graphical Toolkit
2001/04/10 Lineo and Trolltech expand Embedded Linux partnership
2001/04/11 Trolltech and Lynuxworks provide a robust GUI solution for embedded Linux devices
2001/05/21 A Whole New Universe of Applications Available for the Mac
2001/03/22 Qt 3.0 Beta Release
2001/06/04 Embedded Industry Leaders Establish Open Standards Java and Linux Platform for Device Manufacturers
2001/06/15 Qt 2.3.1 Released
2001/06/20 Trolltech, NeoMagic Collaborate on Platform for Handheld Internet and Personal Entertainment Devices
2001/06/26 Trolltech Makes Qt/Windows Available Under New Non-Commercial License
2001/07/31 Qt Palmtop Environment 1.3.1
2001/08/06 Trolltech releases an Open Beta of Qt/Mac
2001/08/28 Linux-Based PDA Suite Preview
2001/08/29 Qt 3.0 beta released
2001/09/03 Trolltech to include Qt/Mac in the Qt Academic Program
2001/09/13 New release of Linux-based mobile computing platform (Qt palmtop 1.4)
2001/10/15 Trolltech Releases Qt/Mac, OS X
2001/10/15 Trolltech Releases Qt 3.0
2001/11/05 Maintenance release: Qt 2.3.2
2001/11/05 Sharp, Trolltech Put Embedded Linux Applications in the Palm of Your Hand
2001/12/03 Trolltech Launches Worldwide Developer Contest for Sharp's Zaurus SL-5000D
2001/12/12 Maintenance Release: Qt 3.0.1
2002/01/23 Windows Evaluation Version Available
2002/01/31 Qt 3.0 Nominated for Product of the Year 2001
2002/02/25 Maintenance Release: Qt 3.0.2
2002/03/25 Maintenance Release: Qt 3.0.3
2002/03/25 Trolltech Unveils Qtopia
2002/03/25 Qtopia SDK (1.5)
2002/04/23 Trolltech's New ActiveQt Speeds and Simplifies Creation of ActiveX Controls
2002/05/03 Maintenance Release: Qt 3.0.4
2002/07/15 Maintenance Release: Qt 3.0.5
2002/10/23 Maintenance Release: Qt 3.0.6
2002/11/13 Trolltech releases Qt 3.1
2002/12/17 Maintenance Release: Qt 3.1.1
2003/02/03 Maintenance Release: Qt 3.0.7
2003/02/12 Trolltech's Qtopia Included in IBM's Embedded Linux Reference Platform
2003/02/19 Qtopia Pre-integrated and Productized for the new MontaVista Linux Consumer Electronics Edition
2003/03/03 Maintenance Release: Qt 3.1.2
2003/03/05 Trolltech launches latest version of Qtopia (1.6)
2003/06/24 Qt/Mac GPL Released
2003/07/02 Trolltech Introduces Qt Script for Applications, (QSA) Version 1.0
2003/07/23 Trolltech Releases Qt 3.2
2003/07/31 Maintenance Release: QSA 1.0.1
2003/08/05 Trolltech releases Qtopia 1.7
2003/08/27 Trolltech releases Qt 3.2.1
2003/10/16 Trolltech releases Qt 3.2.2
2003/10/31 Trolltech Unveils Qtopia Phone Edition
2003/10/31 Motorola selects Trolltech for the A760 Linux smartphone
2003/11/14 Trolltech releases Qt 3.2.3
2004/02/04 Trolltech releases Qt 3.3 and QSA 1.1
2004/03/01 Qt 3.3.1 released
2004/04/27 Qt 3.3.2 released
2004/05/26 Trolltech Releases Qtopia Phone Edition To Customers
2004/08/11 Trolltech Releases Qt 3.3.3
2004/08/27 Trolltech Releases Qt Script for Applications (QSA) 1.1.1
2004/09/27 Qtopia Development Environment for Linux Devices Now Available on TI's OMAP Processor
2004/09/27 Trolltech's Qtopia Phone Software Powers Yuhua TelTech's Upcoming Normandy Mobile Phone
2004/11/09 Trolltech Releases Major Upgrade to Qtopia Software for Embedded Linux Devices(Qtopia 2.1)
2004/11/09 New Motorola Linux-based Mobile Phones Built on Trolltech Platform
2004/11/09 Datang Mobile Picks Qtopia for New Linux-Based Mobile Phone Platform
2004/11/15 Philips Multimedia Software to Support Qtopia for Mobile Devices
2004/11/16 Trolltech and Toshiba to Provide Embedded Linux Software
2004/12/22 Qtopia PDA Edition Released Under GPL License(Qtopia 2.1)
2005/01/17 Trolltech Releases Qt Script for Applications (QSA) 1.1.2
2005/01/26 Trolltech Releases Qt 3.3.4
2005/02/07 Trolltech to Extend Dual Licensing to Qt for Windows
2005/02/14 Leadtek Chooses Qtopia Software for IP VideoPhones
2005/02/14 Ningbo Bird Standardizes on Trolltech's Qtopia Software for Linux-based Mobile Phones
2005/02/14 Trolltech's Qtopia Software Available on Philips NexperiaTM Mobile Platforms.
2005/06/06 Qt 4 Release Candidate Now Available
2005/06/28 Trolltech Launches Major New Version of Qt
2005/08/03 Trolltech Releases Qtopia SDK for Archos PMA400
2005/08/19 Trolltech Releases QSA for Qt 4
2005/08/19 Trolltech Releases Qt 4.0.1
2005/09/05 New Cellon Phone Platform Built with Trolltech's Qtopia
2005/09/09 Trolltech Releases Qt 3.3.5
2005/11/09 Trolltech Partners with Nissin Systems to Accelerate Embedded Linux Adoption in Japan
2005/11/09 Trolltech Inks Agreement with NeoMore to Resell Qtopia and Qt Embedded in France
2006/02/08 Teleca and Trolltech enter strategic partnership to deliver Linux-based mobile phone solutions
2006/02/13 Trolltech's Qtopia Strengthens Design and Flexibility of Latest Linux-based Mobile Phones
2006/02/13 Amoi Phone Built on Cellon's Qtopia®-Based Handset Design
2006/02/13 Accton uses Trolltech's Qtopia for Embedded Peer-to-Peer Wi-Fi VoIp Phone
2006/02/13 Trolltech raises bar again with Qtopia 4 Series
2006/02/22 Trolltech Releases Qt 4.1.1


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ユーザー会に転送されますからねぇ。そういう無茶はできませんでした。誰か、一定のルールで背景に使わせてくれる、いけてる萌え絵の絵師さんを紹介してくれませんか・・・。

2013年1月5日土曜日

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

なんだか生暖かく見守られてるらしい隠者です。
見守られてるらしいので、がんばりますか。

通貨の取得と変換

昨日のアプリに、通貨情報の取得と変換のロジックを実装していきます。
これから実装を追加していきますが、基本的にRectangleのブロックの内側に入れて行く必要がある事に注意して下さい。

とまぁ、チュートリアルでは追加行だけ書いてあるわけですが、つまるところこういう事です。

import QtQuick 2.0
import Ubuntu.Components 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
        }
    }
}

ここでは、為替を通貨とレートのペアをアイテムとしてもつリストのListModelオブジェクトとして扱います。通貨のListModelは、データを表示するビューのソースとして使用されます。ここでは欧州中央銀行(ECB)からユーロ外国為替参照レートから実際のデータをフェッチします。このデータにはユーロ自体は定義されていないので、事前に、1.0のリファレンスレートとして登録しています。

この関数の記事は、QMLの非常に強力な機能の実例となります(ここ、テストにでます)。Javascriptの統合です。2つのJavaScript関数は、indexから通貨とレートを取り出すためのグルーコードとなります。これらは、通貨情報がロードされていない最初にコンポーネントのプロパティがバインディングされた時に必要となります。ただまぁ、これらの事については今はあまり気にしなくて良いです。今の所、あなたのQMLオブジェクトにJavaScriptのコードを透過的に統合できるという事を覚えておく事が重要なのです。

さて、XmlListModel - XMLデータをmodelへとロードするQtQuickオブジェクト - で実際のデータをフェッチしましょう。これを使うために、ファイルの先頭に以下のようなimportを追加します。

import QtQuick 2.0
import QtQuick.XmlListModel 2.0
import Ubuntu.Components 0.1
続いて、Rectangle内のListModelのブロックの後に、実際の為替レートをフェッチするコードを追加します。

    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()" }
    }


関連したプロパティはsourceで、これは、データをどこからフェッチするのかというURLを含んでいます。queryは、以下のXmlRoleからモデルのアイテムを生成するための基本クエリとして使用するため、絶対XPathクエリを定義しています。また、namespaceDeclarationsは、XPathクエリに使う名前空間の宣言です。

onStatusChangedシグナルハンドラは、JavaScriptでのシグナル・ハンドラの仕組みもまた、もうひとつの自由度の機能である事を実証します。

少しだけ補足しておくと、Qtには独自のシグナル・スロットという仕組みがあります。これはUnix系OSが提供するシグナルとは異なるものですが、まぁ、あるイベントに対して、あるSlotとよばれるハンドラの呼び出しを登録するという仕組みである事は同じです。まぁ、Qt系のイベントのコールバック的な仕組みだと覚えておけば良いでしょう。

すべてのQMLプロパティは、<property>Changed というシグナルと、それに対応するon<property>Changedというシグナルハンドラを持っています。この場合、StatusChangedシグナルは、プロパティのステータスの任意の変更を通知するため発信され、rateFetcherがデータの読み込みを終え次第、すべての通貨・レートデータを通貨ListModelに登録するためのハンドラを定義しています。

要するにrateFetcher[XmlListModel]が通貨レート情報を読み込み、currencies[ListModel]に登録しています。

多くの場合、XmlListModel単体のデータソースとして使う事ができます。ただ、今回のケースでは中間コンテナとして使っています。今回はデータを修正し、ユーロのデータを追加しておく必要があったため、currencies[ListModel]にデータを入れています。

開発者であるあなたが、どのようにネットワークアクセスをしているのか考えなくて良い程、透過的にネットワークアクセスをしていることにお気づきでしょうか。

XmlListModelのブロックの後(60行目近辺)に、レートをフェッチしているアクティビティを示すため、Ubuntu ActivityIndicatorコンポーネントを追加しましょう。

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

それを、親(root)の右側に固定して、レートデータがフェッチされるまで、Activityを表示します(回線速度にもよるでしょうが、わりと一瞬なのですぐに消えちゃいます)

最後に、ActivityIndicatorの後(65行目近辺)に、実際の通貨変換を実行するconvert - JavaScript関数を追加します。

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

とまぁ、色々追加したのですが、ここまでだと画面を表示させてもほとんど変化はありません。裏でデータを取ってきて蓄える部分ですので。

通貨の選択

ここまででバックエンドはすべて加えたので、ここからはユーザー・インタラクションの追加に移ります。これから、オブジェクトの他のコンポーネントを組み合わせて再利用可能なブロック-新しいコンポーネント-の作成をします。

まずは、import分の追加です。
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
続いて、先ほどのコードに以下のコードを追加します。

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

ここでは、Ubuntu PopvarとスタンダードなQtQuickのListViewを組み合わせた通貨セレクタを作成しました。ListViewは、currencies ListModelからデータを出力します。どのように列オブジェクトがUbuntu Headerにラップされ、リストビューに縦に配置され、そしてどのようにLits Viewのそれぞれのアイテムが、スタンダードリストコンポーネントとなるのかに着目して下さい。

popvarはcurrenciesの選択肢を表示します。選択されると同時にpopvarはhideになり(onClickedハンドラを確認して下さい)、そしてcallerのデータが更新されています。

callerはcurrencyIndexとinputプロパティーを有しており、そして、inputはupdate()関数をもつitemを所持していると見なされます。


とここまでが、このページの説明なのですが、いやぁ、これ知らない人には確かにちんぷんかんぷんかもしれません。隠者はすっかり読み飛ばしていましたが・・・。この説明でわかった人は挙手を・・・・と勉強会なら聞く所です。

実はこのページのコードまでだと、画面上には何もでません。このComponentをpopup表示させるコードが必要です。ですので、動かして理解する事も困難かもしれません。とりあえず、次のページはほとんどコードと画像だけで、たいした説明が無いので、そちらで詳しく解説します。

今はとりあえず、以下の構成に成っているという事を理解しておいて下さい。()の中身はID-つまり名前です。

Rectangle(root) +-- Label(title)
                          +-- ListModel(currencies)
                          +-- XmlListModel(ratesFetcher)
                          +-- ActivityIndicator
                          +-- Component(currencySelector)
                                +-- Popver
                                     +-- Column
                                         +-- Header(header)
                                         +-- ListView[model:currencies]
                                             +-- delegate : Standard

とりあえず、本日ここまでのコードを記載しておきましょう。

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

というわけで、微妙にすっきりしない2ページ目の終わりですが、続きは次のエントリーへ持ち越します。



関連記事へのリンク

Toolkitについてその1その3

2013年1月4日金曜日

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

昨夜に引き続きUbuntu for Phoneで行こうかと思う隠者です。

チュートリアルに関する説明は、特に必要ないとおもったのですが、偉大なる空の王が、次はこれをヤレとおっしゃっておりまして。

sola ‏@androidsola
 知識もなく英語が苦手だと試すだけでも面倒だったので



ということだそうです。まぁ、偉大なる空の王にやれと言われたら断れません。

Ubuntu for Phoneのチュートリアルのcodeは、SDK teamのDavid Planellaによるものです。

通貨変換ツールの作成を題材にして、QMLのツールキットから、i18n(国際化対応)、単位、テーマのためのitemStyle、ラベル、ActiveIndicator、ポップバー、ボタン、テキストフィールド、リストアイテムのヘッダとStandard等のUbuntu Toolkitを使う事になります。

なお、このチュートリアルは、アーキテクチャ非依存のQMLについての説明で、異なるアーキテクチャへのクロスコンパイル等の高度な項目については、後日、完全なUbuntu SDKのリリースされた後、説明する事になるとの事です。

ようするに、とりあえず、お試しでこんな感じのUIを使ったツールなら簡単にできますよ的な内容で、より高度なアプリケーション開発については、もう少しお待ちくださいって事でしょうか。

ちなみに、QMLはJavaScriptとCSSに似たインターフェース宣言の組み合わせで出来ているスクリプト言語です。

ま、色々書かれていますが、ニュアンスで翻訳かつ、適当に補足のうえ、適当にすっとばしで、ざっくり行きましょう。真面目にやりたい人は、英語読みながら頑張るのです。その方が確かです。隠者は、まぁ、なんちゃってで、いぢわるで、てきとーなおっさんですので。

通貨コンバーターのための電話アプリの作成

必要な環境

  • Ubuntu 12.10のインストールをする
  • Qt5およびUbuntu for Phone Toolkitのインストールをする
  • Qt Creatorのインストールをする

環境構築

さすがにUbuntu 12.10については割愛します。Ubuntu 12.10上で、Terminalを開いて(ショートカット: Ctrl+Alt+T)、以下のコマンドを実行して行く。
  1. Qt5のインストール
    $ sudo add-apt-repository ppa:canonical-qt5-edgers/qt5-beta1 && sudo apt-get update && sudo apt-get install qt5-meta-full && echo 'export PATH=/opt/qt5/bin:$PATH' >> ~/.bashrc
  2. Ubuntu QML Toolkitのインストール
    $ sudo add-apt-repository ppa:ui-toolkit/ppa && sudo apt-get update && sudo apt-get install qt-components-ubuntu qt-components-ubuntu-demos qt-components-ubuntu-examples qt-components-ubuntu-doc notepad-qml

  3. Qt Creatorのインストール(チュートリアルより余計なものも入るけど)
    sudo apt-get install qtcreator qtcreator-doc

QML開発はどんなテキストエディタで行っても良いのですが、このチュートリアルは、Qt CreatorというQt用のIDEを使う手順を紹介しているので、せっかくだからこれを使っておいてみましょう。

ということなので、とりあえず、Qt Creatorに、QMLをpreviewするための設定をしておきましょう。すでにQt Quick用のpreviewがあるのですが、これはQt4用なので、Qt5用のものを設定する必要があります。チュートリアルは置き換えてますが、個人的な理由でQt4用の設定は残したかったので、Addにしています。ご了承ください。
  1. Qt Creatorを起動する

    • Dash Homeで、Qt Creatorを入力して起動します。

  2. Option設定を開く

    • メニューからTools → Optionsを選択してダイアログを開く

  3. ダイアログからQMLカテゴリに新しいToolsを登録する

    • External Toolsタブを開く
    • Qt Quickのカテゴリを選択し、Add Toolを選択する

  4. qmlsceneを設定する
    • 新しく追加したツール名は適当にわかるように設定
    • Executableは、/opt/qt5/bin/qmlsceneに指定する
    • 他の設定は、先にあったpreviewの設定に合わせる

Rectangle

続いて、Ubuntuのlabel コンポーネントを使った最小のQMLを作成します。

  1. Qt Creatorを起動する
  2. Ctrl+Nで新しいプロジェクトの作成を行う
  3. Projects -> Application -> Qt Quick UIを選択してChooseを選択する
  4. Project Nameには、CurrencyConverterを設定してNextへ
  5. Version Controlはとりあえず何もせずにFinishにしましょう。

最初にCurrencyConverter.qmlが生成されます。以下のようなHello Worldが最初から入っているかと思いますが、Ubuntu for Phone用ではないので、とりあえず削除して下さい。

// import QtQuick 1.0 // to target Maemo 5
import QtQuick 1.1

Rectangle {
    width: 360
    height: 360
    Text {
        anchors.centerIn: parent
        text: "Hello World"
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}


で、以下の内容をコピペします。
import QtQuick 2.0 
import Ubuntu.Components 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("くぎゅぅぅぅぅぅ")
       height: contentHeight + root.margins
       anchors {
           left: parent.left
           right: parent.right
           top: parent.top
       }
    }
}


コピペ後にCtrl+Sを押して保存します。
あとは、Tools -> External -> QtQuick -> [qmlsceneに設定した名]を呼び出すだけです。

おめでとうございます。第一段階終了です。

え、何か間違ってるって?気のせいです。さて、それでは、とりあえず起動した、何の面白みもないアプリを終了させて、上記のQMLの解説に移りましょう。

Import Section

すべてのQMLコードは、importセクションとオブジェクト宣言セクションから出来ています。すべての先頭で、名前空間とバージョンを指定して必要なQMLタイプとコンポーネントをimportします。

import QtQuick 2.0
import Ubuntu.Components 0.1

QtQuick 2.0は、Qt5に入っているコンポーネントとなります。ちなみにapt-get でインストールしたQt CreatorはQt4のもので、この頃のバージョンは1.1でした。ですので、最初に生成した時の宣言は、1.1になっています。

Ubuntu.Components 0.1は、Ubuntuの公開したコンポーネントのversion 0.1にあたるということです。preview版0.1が1.0になるのがいつなのか・・・気になる所です。

さて、このようにQMLの場合、利用するコンポーネントとそのバージョンを指定する事ができるようになっています。必要に応じて新たなコンポーネントを実装するなんて事もできますが、それは上位編でということでしょう。

オブジェクト宣言セクション

QMLのオブジェクトセクションでは、プロパティを持つオブジェクト木によってユーザーインターフェースが定義されます。JavaScriptを含める事もできるのですが、その辺りは後述します。

まずは、アプリケーションを内包するRectangleが指定されています。この長方形はもっとも基本的な構成要素になります。JSONに良く似た構文でプロパティーを定義しています。まず、idという外部からrootという名で参照できるようにするためのid定義を行い、その後見た目のプロパティーである、width,height,colorを定義しています。

‘property: value’というシンタックスで値を定義しているのがわかりますよね。

また、real型のmarginsというカスタムプロパティも定義しています。buttonWidthというのは、後ほど使うので先に定義してあるそうです。

import QtQuick 2.0

import Ubuntu.Components 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 // idでRectangleのmargins値を参照
       anchors {
           left: parent.left
           right: parent.right
           top: parent.top
       }
    }
}

ちなみに、RectangleはQtQuick2.0の構成要素になっています。その中のLabelが、Ubuntuのtoolkitに含まれるものですね(プロパティの値に使われているunits)。

たとえば、余計な者を削って以下のようにすると、背景色が紫な長方形のできあがりです。指定のないプロパティはデフォルト値が設定されます。

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


なお、Qt Creatorの機能を使うと簡単に設定できます。
Rectangleの定義の中で、マウスを右クリックし、メニューからShwo Qt Quick Toolbarを選択すると、Rectangleのプロパティをグラフィカルに変更できます。


気が向いたら適当にいぢって見るといいでしょう。

さて、widthプロパティの値等の指定で units.gu(60) といった値が使われていることに着目してみましょう。このgrid unitは、フォームファクターに依存せずに寸法を指定する方法と考えて下さい。これらは、アプリケーションが実行されているデバイス依存のピクセル数を返します。

Rectangleの子としてUbuntuツールキットの部品のLabelがtitleという名前で、Ubuntu.titleのスタイルにしています。

    Label {
       id: title
       ItemStyle.class: "title"
       text: i18n.tr("中二病でも開発したい")
       height: contentHeight + root.margins
       anchors {
           left: parent.left
           right: parent.right
           top: parent.top
       }
    }

スタイルは、クラスプロパティーで、CSSで行うかのように指定する事ができます。
Ubuntu Titleは /usr/share/themes/Ambiance/qmltheme/default.qmltheme で以下のように設定されています。



.title {
    fontSize: "x-large";
    color: "#757373";
    styleColor: "white";
    style: Text.Raised;
}


textプロパティは、i18n.tr()というもので修飾することで翻訳可能にしています。

元々QtにはQObject::tr(), QMLには、qsTr()というのがあって、これらで修飾してあると、Linguistというツールで文字列の一覧を抜き出し、言語に応じたメッセージカタログみたいなものを作る事ができるのですが、Ubuntuでは別実装を使うということのようです。

高さは、contentHeightという内部のテキストの高さを返すプロパティを使って設定しています。

QMLアンカーシステムに基づくアンカーを使うことで、親であるRectangleのアンカーに基づき、ラベルのレイアウトと位置を指定することができます。


解像度非依存

Ubuntuのユーザーインターフェースツールキットの主要な特徴は、複数のデバイスをもつユーザーが定義しているすべてのフォームファクターをスケールする機能です。
その方法は、Grid Unitという新しい単位を定義することでした。
Grid Unitは、アプリケーションが実行されている画面やデバイスの種類に応じたピクセル数に変換されます。以下に一例があります。

デバイス変換
一般的なラップトップ1 gu = 8 px
Retina対応ラップトップ1 gu = 16 px
スマートフォン1 gu = 18 px

国際化対応

国際化とネイティブ言語のサポートはUbuntuツールキットの重要な特徴です。国際化技術のフリーソフトの中でもっとも普及しているgettextを選び、i18n.tr()機能ファミリーとしてQMLに実装しました。

ということで、1ページ目終了です。とりあえず、本当は昨夜に公開する予定だったのだけど、ハウルツイートのせいで、ちょっと遅れたので、ここまでを昨夜の日記分として公開しておきます。


その2へと続きます。

2013年1月3日木曜日

Ubuntu for Phone

3日坊主を自称する以上、3日間は日記を続けなければと思う隠者です。

Ubuntuのサイトで何かカウントダウンされていたのに気がついていた人は多いでしょう。ついに公開された情報に、twitterは湧いていました。

Ubuntu for Phoneの発表があったのです。

特徴についてよくまとまった記事が既にありましたので、Ubuntu for Phoneの概要はそちらを参照するとよいでしょう。

上記の記事では触れられていませんが、このUbuntu for Phoneに、非常に重要なニュースが含まれていました。himamuraさんのツイートの通りです。

話題のubuntu PnoneはQt5+QMLで開発されているらしいゾ!
   

きたーーっというわけで、Qt+QMLがアプリケーションの作成に利用されているのです。いやぁ、年明け早々、Qt大好き〜な人には楽しそうな話がきましたね。

いつか見たあの夢・・・。Mの悪夢・・・。まぁ、QMLだとコードみえちゃうんじゃとか、Qt5-beta1を使ってるけど、Qt5.0.0までに機能削られてなかったかとか、QMLを賛美し過ぎだとか、色々懸念事項はあるものの、何よりまず、こういうモノが出てきた事が重要です。

Ubuntu for Phoneでは、アプリケーションの開発は、QMLあるいはHTML5という記載が、Go Mobiletというページに記載されています。

QML向けに、Ubuntu for Phoneと親和性の高いToolkitを提供するということで、既にpreview版が公開されています。

Preview晩のToolkit環境は、Ubuntu 12.10であれば、以下の2ステップでインストールできます。
  1. Qt5のインストール
    $ sudo add-apt-repository ppa:canonical-qt5-edgers/qt5-beta1 && sudo apt-get update && sudo apt-get install qt5-meta-full && echo 'export PATH=/opt/qt5/bin:$PATH' >> ~/.bashrc
  2. Ubuntu QML Toolkitのインストール
    $ sudo add-apt-repository ppa:ui-toolkit/ppa && sudo apt-get update && sudo apt-get install qt-components-ubuntu qt-components-ubuntu-demos qt-components-ubuntu-examples qt-components-ubuntu-doc notepad-qml

サイト通りだと、ここでアプリを開発してみましょうとチュートリアルに誘っていますが、とりあえず、どんなパーツがあるのか見てみましょう。ちなみに、あくまでPC版Ubuntu 上でのデモなのでさくさく動きます。
$ /usr/lib/qt-components-ubuntu/demos/launch_componentshowcase

Theming

あるアイテムのカスタム表示のスイッチですかねぇ。


Resolution Independence

スライダーを操作すると拡大縮小されますね。


Ubuntu Shape


Buttons

ボタンは色や中身、大きさも自由ですといったデモですね。Disable状態がわかりにくい気はしますけど、まぁ、背景色が悪いよなぁ。

Tabs(Old)

旧来のタブ機能ですね。タブを押す意外にも、外部のボタンでのタブ移動ができ、タブ表示した内部にFlickableなコンテンツを入れたり、Listを入れたりもできます。スクロールするコンテンツは、iOSやAndroidでよく見る、引っ張るとゴムみたいに弾む感じで戻るようなアニメーションも行われています。


Tabs(new)

フリック操作で切り替わるタブ表示です。表示上、フリックで切り替わるという事がいまいちわかりにくい気はしますが、小さい画面でタブを押すよりは、ずっと楽だと思います。



List Item

List には、タイトル、画像のほか、ListのValue表示をしたり、選択で表示・非表示にして選択できるようにしたり、スイッチやボタン、スライダ等も設置できるようです。




Page Stack

Android端末だと設定画面等によく使われている、スタックページですね。


Switch, Check Box, Activity Indicator, Progress Bar

まぁ、この辺は外観だけ。Activity Indicatorはくるくる回ってます。




Slider

Sliderは通常の離して値が決定した段階で更新通知されるものと、変更中も更新通知されるものの2種類があるようです。

Text Input, Text Area

Text InputとText Areaです。リッチテキストも使う事ができ、ダブルクリックで範囲を選択、Text Areaは任意の行数までの拡大を許したりもでき、Ubuntu上では、日本語も入力できました。



Scrollbar

Scrollbarは、スクロール中にちょこっとオレンジ色で表示されるだけで、いまいち見えにくいです。

Popup

あとは、ポップアップする機能ですかね。位置が自動なもの、位置指定なもの、問い合わせ用にボタンが配置されたものや、modal的に他の部分を一時無効にして表示するものなど、色々と機能があるようです。





GIcon Image Provider

"image://gicon/preferences-color" 等の指定で使えるIcon


ちなみに、以下で起動すると電話画面サイズでのデモが2画面分あるようです。
$ /usr/lib/qt-components-ubuntu/demos/launch_componentshowcase_phone

画面サイズかわっただけやん!って気もしないではないですが。

なお、チュートリアルは、以下のURLになります。
http://developer.ubuntu.com/resources/app-developer-cookbook/mobile/currency-converter-phone-app/

Qt Creatorのインストール手順のためには、上記のURLをUbuntu 12.10上のfirefoxか何かで表示した方がいいでしょうね。apt://qtcreator って指定で、ソフトウェアインストール用のGUIが開くんですねぇ。

$ sudo apt-get install qtcreator qtcreator-doc

と書けば良い気がするのですけど・・・。

ま、実際の所、こういうパーツは使う事は使うのだけど、本格的なアプリを書こうと思うと全然たりないわけで。そんな時どうしたら良いのかが重要なんですが、QMLは残念ながらまだメジャーではないし、exampleも2つしか入ってないし、まだまだこれから感でいっぱいですね。

タイミング的には1年遅いって気がしないでもないんですが。QMLでこういうの作りたいと思って、色々夢の語られたQt5 を待ってたら今頃になったんですかねぇ。
おそらく、正式版のQt5に移行して公開しようと思ったら、Qt5のQMLから色々削られてて辛い状況もあるのかもしれません。

Qt押しで、MeeGoで悲しい思いをした隠者としては、なんとか頑張って欲しいと思っています。

本日は、ほんと画像撮って貼付けるだけでしたけど、結構手間な作業だったので、今夜はここまでということで。