本日から、こちらのブログの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';
}
}
}
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、その2、Toolkitについてここまでのコード
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';
}
}
}
}
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ユーザー会に転送されますからねぇ。そういう無茶はできませんでした。誰か、一定のルールで背景に使わせてくれる、いけてる萌え絵の絵師さんを紹介してくれませんか・・・。
Harrah's Reno - Hotel, Casino, Spa & Spa - JTG Hub
返信削除Find out more about 과천 출장마사지 Harrah's Reno, 부산광역 출장마사지 including amenities, 부산광역 출장마사지 timings, amenities, photos, 광명 출장마사지 geolocation and more. 밀양 출장마사지